Project import
diff --git a/mdnsresponder/Android.mk b/mdnsresponder/Android.mk
new file mode 100644
index 0000000..3fd87ea
--- /dev/null
+++ b/mdnsresponder/Android.mk
@@ -0,0 +1,237 @@
+LOCAL_PATH := $(call my-dir)
+
+commonSources := \
+    mDNSShared/dnssd_clientlib.c  \
+    mDNSShared/dnssd_clientstub.c \
+    mDNSShared/dnssd_ipc.c
+
+commonLibs := libcutils liblog
+
+commonFlags := \
+    -O2 -g \
+    -fno-strict-aliasing \
+    -D_GNU_SOURCE \
+    -DHAVE_IPV6 \
+    -DNOT_HAVE_SA_LEN \
+    -DPLATFORM_NO_RLIMIT \
+    -DMDNS_DEBUGMSGS=0 \
+    -DMDNS_UDS_SERVERPATH=\"/dev/socket/mdnsd\" \
+    -DMDNS_USERNAME=\"mdnsr\" \
+    -W \
+    -Wall \
+    -Wextra \
+    -Wno-array-bounds \
+    -Wno-pointer-sign \
+    -Wno-unused \
+    -Wno-unused-but-set-variable \
+    -Wno-unused-parameter \
+    -Werror=implicit-function-declaration \
+
+daemonSources := mDNSCore/mDNS.c            \
+                 mDNSCore/DNSDigest.c       \
+                 mDNSCore/uDNS.c            \
+                 mDNSCore/DNSCommon.c       \
+                 mDNSShared/uds_daemon.c    \
+                 mDNSShared/mDNSDebug.c     \
+                 mDNSShared/dnssd_ipc.c     \
+                 mDNSShared/GenLinkedList.c \
+                 mDNSShared/PlatformCommon.c \
+                 mDNSPosix/PosixDaemon.c    \
+                 mDNSPosix/mDNSPosix.c      \
+                 mDNSPosix/mDNSUNP.c
+
+daemonIncludes := external/mdnsresponder/mDNSCore  \
+                  external/mdnsresponder/mDNSShared \
+                  external/mdnsresponder/mDNSPosix
+
+#########################
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES :=  $(daemonSources)
+LOCAL_MODULE := mdnsd
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_C_INCLUDES := $(daemonIncludes)
+
+LOCAL_CFLAGS := \
+  $(commonFlags) \
+  -DTARGET_OS_LINUX \
+  -DMDNS_VERSIONSTR_NODTS=1 \
+  -DHAVE_LINUX \
+  -DUSES_NETLINK \
+
+LOCAL_STATIC_LIBRARIES := $(commonLibs) libc
+LOCAL_FORCE_STATIC_EXECUTABLE := true
+LOCAL_INIT_RC := mdnsd.rc
+include $(BUILD_EXECUTABLE)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES :=  $(daemonSources)
+LOCAL_MODULE := mdnsd
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_C_INCLUDES := $(daemonIncludes)
+
+LOCAL_CFLAGS := \
+  $(commonFlags) \
+  -DMDNS_VERSIONSTR_NODTS=1 \
+
+LOCAL_CFLAGS_linux := -DTARGET_OS_LINUX -DHAVE_LINUX -DUSES_NETLINK
+LOCAL_CFLAGS_darwin := -DTARGET_OS_MAC
+
+LOCAL_STATIC_LIBRARIES := $(commonLibs)
+include $(BUILD_HOST_EXECUTABLE)
+
+# include $(CLEAR_VARS)
+# LOCAL_SRC_FILES := \
+#   mDNSWindows/SystemService/main.c    \
+#   mDNSWindows/SystemService/Service.c \
+#   mDNSWindows/Secret.c                \
+#   mDNSWindows/mDNSWin32.c             \
+#   mDNSShared/dnssd_ipc.c              \
+#   mDNSShared/uds_daemon.c             \
+#   mDNSShared/mDNSDebug.c              \
+#   mDNSShared/GenLinkedList.c          \
+#   mDNSShared/DebugServices.c          \
+#   mDNSCore/DNSDigest.c                \
+#   mDNSCore/DNSCommon.c                \
+#   mDNSCore/uDNS.c                     \
+#   mDNSCore/mDNS.c                     \
+#   android/windows_firewall_dummy.c
+# 
+# LOCAL_MODULE := mdnsd
+# LOCAL_MODULE_TAGS := optional
+# LOCAL_MODULE_CLASS := EXECUTABLES
+# 
+# mdnsd_generated_sources := $(call local-generated-sources-dir)
+# event_log_h := $(mdnsd_generated_sources)/EventLog.h
+# event_log_o := $(mdnsd_generated_sources)/EventLog.o
+# event_log_rc := $(mdnsd_generated_sources)/EventLog.rc
+# 
+# event_log_mc := external/mdnsresponder/mDNSWindows/SystemService/EventLog.mc
+# mingw_bin := prebuilts/gcc/$(HOST_PREBUILT_TAG)/host/x86_64-w64-mingw32-4.8/bin
+# windmc := $(mingw_bin)/x86_64-w64-mingw32-windmc
+# windres := $(mingw_bin)/x86_64-w64-mingw32-windres
+# 
+# $(event_log_h) $(event_log_o): $(event_log_mc)
+# 	@echo "Generating EventLog messages"
+# 	@mkdir -p $(mdnsd_generated_sources)
+# 	@$(windmc) -r$(mdnsd_generated_sources) -h$(mdnsd_generated_sources) $<
+# 	@$(windres) -F pe-i386 -I$(mdnsd_generated_sources) $(event_log_rc) $(event_log_o)
+# 
+# LOCAL_GENERATED_SOURCES := \
+#   $(mdnsd_generated_sources)/EventLog.h \
+#   $(mdnsd_generated_sources)/EventLog.o
+# 
+# LOCAL_C_INCLUDES := \
+#   external/mdnsresponder/mDNSShared \
+#   external/mdnsresponder/mDNSCore \
+#   external/mdnsresponder/mDNSWindows/SystemService \
+#   external/mdnsresponder/mDNSWindows \
+#   external/mdnsresponder/android/caseMapping \
+#   $(mdnsd_generated_sources)
+# 
+# LOCAL_CFLAGS := $(commonFlags) \
+#   -DMDNS_VERSIONSTR_NODTS=1 \
+#   -DTARGET_OS_WINDOWS \
+#   -DTARGET_OS_WIN32 \
+#   -DWIN32 \
+#   -DNDEBUG \
+#   -D_CONSOLE \
+#   -D_WIN32_LEAN_AND_MEAN \
+#   -DUSE_TCP_LOOPBACK \
+#   -DPLATFORM_NO_STRSEP \
+#   -DPLATFORM_NO_EPIPE \
+#   -DPLATFORM_NO_RLIMIT \
+#   -DPID_FILE='""' \
+#   -DUNICODE \
+#   -D_UNICODE \
+#   -D_CRT_SECURE_NO_DEPRECATE \
+#   -D_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1 \
+#   -D_LEGACY_NAT_TRAVERSAL \
+#   -include stdint.h \
+#   -include winsock2.h \
+#   -include ws2ipdef.h \
+#   -include wincrypt.h \
+#   -include netioapi.h \
+#   -Wno-sign-compare \
+#   -Wno-empty-body
+# 
+# LOCAL_LDFLAGS := -lws2_32 -liphlpapi -lpowrprof -lnetapi32 -municode
+# 
+# 
+# LOCAL_MODULE_HOST_OS := windows
+# 
+# LOCAL_STATIC_LIBRARIES := $(commonLibs)
+# include $(BUILD_HOST_EXECUTABLE)
+# ##########################
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(commonSources)
+LOCAL_MODULE := libmdnssd
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS := $(commonFlags) -DTARGET_OS_LINUX -DHAVE_LINUX -DUSES_NETLINK
+LOCAL_SYSTEM_SHARED_LIBRARIES := libc
+LOCAL_SHARED_LIBRARIES := $(commonLibs)
+LOCAL_EXPORT_C_INCLUDE_DIRS := external/mdnsresponder/mDNSShared
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := $(commonSources)
+LOCAL_MODULE := libmdnssd
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS := $(commonFlags) -DTARGET_OS_LINUX -DHAVE_LINUX -DUSES_NETLINK
+LOCAL_STATIC_LIBRARIES := $(commonLibs)
+LOCAL_EXPORT_C_INCLUDE_DIRS := external/mdnsresponder/mDNSShared
+include $(BUILD_STATIC_LIBRARY)
+
+# include $(CLEAR_VARS)
+# LOCAL_SRC_FILES := $(commonSources)
+# LOCAL_SRC_FILES_windows := mDNSWindows/DLL/dllmain.c
+# LOCAL_MODULE := libmdnssd
+# LOCAL_MODULE_TAGS := optional
+# LOCAL_CFLAGS := $(commonFlags)
+# LOCAL_CFLAGS_windows := \
+#   -DTARGET_OS_WINDOWS \
+#   -DWIN32 \
+#   -DNDEBUG \
+#   -D_WINDOWS \
+#   -D_USERDLL \
+#   -D_MDNS_DEBUGMSGS=0 \
+#   -D_WIN32_LEAN_AND_MEAN \
+#   -D_SSIZE_T \
+#   -DUSE_TCP_LOOPBACK \
+#   -D_CRT_SECURE_NO_DEPRECATE \
+#   -D_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1 \
+#   -DNOT_HAVE_SA_LENGTH \
+#   -Wno-unknown-pragmas \
+#   -Wno-sign-compare \
+#   -Wno-overflow \
+#   -include stdint.h \
+#   -include winsock2.h \
+#   -include ws2ipdef.h \
+#   -include wincrypt.h \
+#   -include iphlpapi.h \
+#   -include netioapi.h \
+#   -include stdlib.h \
+#   -include stdio.h
+# 
+# LOCAL_CFLAGS_linux := -DTARGET_OS_LINUX -DHAVE_LINUX -DUSES_NETLINK
+# LOCAL_CFLAGS_darwin := -DTARGET_OS_MAC
+# LOCAL_STATIC_LIBRARIES := $(commonLibs)
+# LOCAL_EXPORT_C_INCLUDE_DIRS := external/mdnsresponder/mDNSShared
+# LOCAL_C_INCLUDES_windows := external/mdnsresponder/mDNSShared external/mdnsresponder/mDNSWindows
+# LOCAL_C_INCLUDES_windows += external/mdnsresponder/android/caseMapping
+# LOCAL_MODULE_HOST_OS := darwin linux windows
+# include $(BUILD_HOST_STATIC_LIBRARY)
+# 
+############################
+
+include $(CLEAR_VARS)
+LOCAL_SRC_FILES := Clients/dns-sd.c Clients/ClientCommon.c
+LOCAL_MODULE := dnssd
+LOCAL_MODULE_TAGS := optional
+LOCAL_CFLAGS := $(commonFlags) -DTARGET_OS_LINUX -DHAVE_LINUX -DUSES_NETLINK
+LOCAL_SYSTEM_SHARED_LIBRARIES := libc
+LOCAL_SHARED_LIBRARIES := libmdnssd libcutils liblog
+include $(BUILD_EXECUTABLE)
diff --git a/mdnsresponder/Clients/ClientCommon.c b/mdnsresponder/Clients/ClientCommon.c
new file mode 100644
index 0000000..812efbe
--- /dev/null
+++ b/mdnsresponder/Clients/ClientCommon.c
@@ -0,0 +1,75 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2008 Apple Inc. All rights reserved.
+ *
+ * Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple Computer, Inc.
+ * ("Apple") in consideration of your agreement to the following terms, and your
+ * use, installation, modification or redistribution of this Apple software
+ * constitutes acceptance of these terms.  If you do not agree with these terms,
+ * please do not use, install, modify or redistribute this Apple software.
+ *
+ * In consideration of your agreement to abide by the following terms, and subject
+ * to these terms, Apple grants you a personal, non-exclusive license, under Apple's
+ * copyrights in this original Apple software (the "Apple Software"), to use,
+ * reproduce, modify and redistribute the Apple Software, with or without
+ * modifications, in source and/or binary forms; provided that if you redistribute
+ * the Apple Software in its entirety and without modifications, you must retain
+ * this notice and the following text and disclaimers in all such redistributions of
+ * the Apple Software.  Neither the name, trademarks, service marks or logos of
+ * Apple Computer, Inc. may be used to endorse or promote products derived from the
+ * Apple Software without specific prior written permission from Apple.  Except as
+ * expressly stated in this notice, no other rights or licenses, express or implied,
+ * are granted by Apple herein, including but not limited to any patent rights that
+ * may be infringed by your derivative works or by other works in which the Apple
+ * Software may be incorporated.
+ *
+ * The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
+ * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
+ * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
+ * COMBINATION WITH YOUR PRODUCTS.
+ *
+ * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
+ * OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
+ * (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <ctype.h>
+#include <stdio.h>			// For stdout, stderr
+
+#include "ClientCommon.h"
+
+const char *GetNextLabel(const char *cstr, char label[64])
+	{
+	char *ptr = label;
+	while (*cstr && *cstr != '.')				// While we have characters in the label...
+		{
+		char c = *cstr++;
+		if (c == '\\' && *cstr)					// If we have a backslash, and it's not the last character of the string
+			{
+			c = *cstr++;
+			if (isdigit(cstr[-1]) && isdigit(cstr[0]) && isdigit(cstr[1]))
+				{
+				int v0 = cstr[-1] - '0';						// then interpret as three-digit decimal
+				int v1 = cstr[ 0] - '0';
+				int v2 = cstr[ 1] - '0';
+				int val = v0 * 100 + v1 * 10 + v2;
+				// If valid three-digit decimal value, use it
+				// Note that although ascii nuls are possible in DNS labels
+				// we're building a C string here so we have no way to represent that
+				if (val == 0) val = '-';
+				if (val <= 255) { c = (char)val; cstr += 2; }
+				}
+			}
+		*ptr++ = c;
+		if (ptr >= label+64) { label[63] = 0; return(NULL); }	// Illegal label more than 63 bytes
+		}
+	*ptr = 0;													// Null-terminate label text
+	if (ptr == label) return(NULL);								// Illegal empty label
+	if (*cstr) cstr++;											// Skip over the trailing dot (if present)
+	return(cstr);
+	}
diff --git a/mdnsresponder/Clients/ClientCommon.h b/mdnsresponder/Clients/ClientCommon.h
new file mode 100644
index 0000000..afe5b7a
--- /dev/null
+++ b/mdnsresponder/Clients/ClientCommon.h
@@ -0,0 +1,41 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2008 Apple Inc. All rights reserved.
+ *
+ * Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple Computer, Inc.
+ * ("Apple") in consideration of your agreement to the following terms, and your
+ * use, installation, modification or redistribution of this Apple software
+ * constitutes acceptance of these terms.  If you do not agree with these terms,
+ * please do not use, install, modify or redistribute this Apple software.
+ *
+ * In consideration of your agreement to abide by the following terms, and subject
+ * to these terms, Apple grants you a personal, non-exclusive license, under Apple's
+ * copyrights in this original Apple software (the "Apple Software"), to use,
+ * reproduce, modify and redistribute the Apple Software, with or without
+ * modifications, in source and/or binary forms; provided that if you redistribute
+ * the Apple Software in its entirety and without modifications, you must retain
+ * this notice and the following text and disclaimers in all such redistributions of
+ * the Apple Software.  Neither the name, trademarks, service marks or logos of
+ * Apple Computer, Inc. may be used to endorse or promote products derived from the
+ * Apple Software without specific prior written permission from Apple.  Except as
+ * expressly stated in this notice, no other rights or licenses, express or implied,
+ * are granted by Apple herein, including but not limited to any patent rights that
+ * may be infringed by your derivative works or by other works in which the Apple
+ * Software may be incorporated.
+ *
+ * The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
+ * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
+ * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
+ * COMBINATION WITH YOUR PRODUCTS.
+ *
+ * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
+ * OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
+ * (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+extern const char *GetNextLabel(const char *cstr, char label[64]);
diff --git a/mdnsresponder/Clients/Makefile b/mdnsresponder/Clients/Makefile
new file mode 100755
index 0000000..ce0b5f0
--- /dev/null
+++ b/mdnsresponder/Clients/Makefile
@@ -0,0 +1,54 @@
+# -*- tab-width: 4 -*-
+#
+# Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+#
+# Licensed 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.
+#
+# Notes:
+# $@ means "The file name of the target of the rule"
+# $< means "The name of the first prerequisite"
+# $+ means "The names of all the prerequisites, with spaces between them, exactly as given"
+# For more magic automatic variables, see
+# <http://www.gnu.org/software/make/manual/html_chapter/make_10.html#SEC111>
+
+#############################################################################
+
+# On OS X the dns_sd library functions are included in libSystem, which is implicitly linked with every executable
+# If /usr/lib/libSystem.dylib exists, then we're on OS X, so we don't need also to link the "dns_sd" shared library
+ifneq "$(wildcard /usr/lib/libSystem.dylib)" ""
+TARGETS = build/dns-sd build/dns-sd64
+LIBS =
+else
+TARGETS = build/dns-sd
+LIBS = -L../mDNSPosix/build/prod/ -ldns_sd
+endif
+
+all: $(TARGETS)
+
+clean:
+	rm -rf build
+
+build:
+	mkdir build
+
+build/dns-sd: build dns-sd.c ClientCommon.c
+	cc $(filter %.c %.o, $+) $(LIBS) -I../mDNSShared -Wall -o $@
+
+build/dns-sd64: build dns-sd.c ClientCommon.c
+	cc $(filter %.c %.o, $+) $(LIBS) -I../mDNSShared -Wall -o $@ -m64
+
+# Note, we can make a 'fat' version of dns-sd using 'lipo', as shown below, but we
+# don't, because we don't want or need a 'fat' version of dns-sd, because it will
+# never need to access more than 4GB of data. We build the 64-bit version purely so
+# we have a test tool for making sure that the APIs work properly from 64-bit clients.
+# lipo -create dns-sd dns-sd64 -output dns-sd-fat
diff --git a/mdnsresponder/Clients/ReadMe.txt b/mdnsresponder/Clients/ReadMe.txt
new file mode 100644
index 0000000..83dd242
--- /dev/null
+++ b/mdnsresponder/Clients/ReadMe.txt
@@ -0,0 +1,25 @@
+This directory contains a variety of clients that use the
+"/usr/include/dns_sd.h" APIs.
+
+Some of the clients are command-line oriented; some are graphical.
+
+Some of the clients, like the "dns-sd" command-line tool, can be built
+for a variety of platforms. Some of the clients only build for one
+platform, like "DNS Service Browser" and "DNS Service Registration",
+which use Objective C and Cocoa and only run on OS X 10.3 or later.
+
+Some of the clients can be built more than one way. For example, the
+"dns-sd" command-line tool can be built for OS X using both the XCode
+IDE, or using a simple command-line "make" command.
+
+What all clients have in common is that they all demonstrate how you
+can use the "/usr/include/dns_sd.h" APIs.
+
+The table below gives a summary of which clients build for which platforms.
+
+Client                    Type          OS X   OS X   Posix
+                                        XCode  Make   Make
+
+dns-sd                    Command-line   X      X      X
+DNS Service Browser       Graphical      X
+DNS Service Registration  Graphical      X
diff --git a/mdnsresponder/Clients/build/dns-sd b/mdnsresponder/Clients/build/dns-sd
new file mode 100755
index 0000000..0a927ee
--- /dev/null
+++ b/mdnsresponder/Clients/build/dns-sd
Binary files differ
diff --git a/mdnsresponder/Clients/dns-sd.c b/mdnsresponder/Clients/dns-sd.c
new file mode 100644
index 0000000..c5a09ff
--- /dev/null
+++ b/mdnsresponder/Clients/dns-sd.c
@@ -0,0 +1,1351 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2008 Apple Inc. All rights reserved.
+ *
+ * Disclaimer: IMPORTANT:  This Apple software is supplied to you by Apple Computer, Inc.
+ * ("Apple") in consideration of your agreement to the following terms, and your
+ * use, installation, modification or redistribution of this Apple software
+ * constitutes acceptance of these terms.  If you do not agree with these terms,
+ * please do not use, install, modify or redistribute this Apple software.
+ *
+ * In consideration of your agreement to abide by the following terms, and subject
+ * to these terms, Apple grants you a personal, non-exclusive license, under Apple's
+ * copyrights in this original Apple software (the "Apple Software"), to use,
+ * reproduce, modify and redistribute the Apple Software, with or without
+ * modifications, in source and/or binary forms; provided that if you redistribute
+ * the Apple Software in its entirety and without modifications, you must retain
+ * this notice and the following text and disclaimers in all such redistributions of
+ * the Apple Software.  Neither the name, trademarks, service marks or logos of
+ * Apple Computer, Inc. may be used to endorse or promote products derived from the
+ * Apple Software without specific prior written permission from Apple.  Except as
+ * expressly stated in this notice, no other rights or licenses, express or implied,
+ * are granted by Apple herein, including but not limited to any patent rights that
+ * may be infringed by your derivative works or by other works in which the Apple
+ * Software may be incorporated.
+ *
+ * The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
+ * WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
+ * WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
+ * COMBINATION WITH YOUR PRODUCTS.
+ *
+ * IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
+ * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
+ * OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
+ * (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
+ * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ * Formatting notes:
+ * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion
+ * on C indentation can be found on the web, such as <http://www.kafejo.com/komp/1tbs.htm>,
+ * but for the sake of brevity here I will say just this: Curly braces are not syntactially
+ * part of an "if" statement; they are the beginning and ending markers of a compound statement;
+ * therefore common sense dictates that if they are part of a compound statement then they
+ * should be indented to the same level as everything else in that compound statement.
+ * Indenting curly braces at the same level as the "if" implies that curly braces are
+ * part of the "if", which is false. (This is as misleading as people who write "char* x,y;"
+ * thinking that variables x and y are both of type "char*" -- and anyone who doesn't
+ * understand why variable y is not of type "char*" just proves the point that poor code
+ * layout leads people to unfortunate misunderstandings about how the C language really works.)
+
+To build this tool, copy and paste the following into a command line:
+
+OS X:
+gcc dns-sd.c -o dns-sd
+
+POSIX systems:
+gcc dns-sd.c -o dns-sd -I../mDNSShared -ldns_sd
+
+Windows:
+cl dns-sd.c -I../mDNSShared -DNOT_HAVE_GETOPT ws2_32.lib ..\mDNSWindows\DLL\Release\dnssd.lib
+(may require that you run a Visual Studio script such as vsvars32.bat first)
+*/
+
+// For testing changes to dnssd_clientstub.c, uncomment this line and the code will be compiled
+// with an embedded copy of the client stub instead of linking the system library version at runtime.
+// This also useful to work around link errors when you're working on an older version of Mac OS X,
+// and trying to build a newer version of the "dns-sd" command which uses new API entry points that
+// aren't in the system's /usr/lib/libSystem.dylib.
+//#define TEST_NEW_CLIENTSTUB 1
+
+// When building mDNSResponder for Mac OS X 10.4 and earlier, /usr/lib/libSystem.dylib is built using its own private
+// copy of dnssd_clientstub.c, which is old and doesn't have all the entry points defined in the latest version, so
+// when we're building dns-sd.c on Mac OS X 10.4 or earlier, we automatically set TEST_NEW_CLIENTSTUB so that we'll
+// embed a copy of the latest dnssd_clientstub.c instead of trying to link to the incomplete version in libSystem.dylib
+#if defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && __ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ <= 1040
+#define TEST_NEW_CLIENTSTUB 1
+#endif
+
+#include <ctype.h>
+#include <stdio.h>			// For stdout, stderr
+#include <stdlib.h>			// For exit()
+#include <string.h>			// For strlen(), strcpy()
+#include <errno.h>			// For errno, EINTR
+#include <time.h>
+#include <sys/types.h>		// For u_char
+
+#ifdef _WIN32
+	#include <winsock2.h>
+	#include <ws2tcpip.h>
+	#include <Iphlpapi.h>
+	#include <process.h>
+	typedef int        pid_t;
+	#define getpid     _getpid
+	#define strcasecmp _stricmp
+	#define snprintf   _snprintf
+	static const char kFilePathSep = '\\';
+	#ifndef HeapEnableTerminationOnCorruption
+	#     define HeapEnableTerminationOnCorruption (HEAP_INFORMATION_CLASS)1
+	#endif
+	#if !defined(IFNAMSIZ)
+	 #define IFNAMSIZ 16
+    #endif
+	#define if_nametoindex if_nametoindex_win
+	#define if_indextoname if_indextoname_win
+
+	typedef PCHAR (WINAPI * if_indextoname_funcptr_t)(ULONG index, PCHAR name);
+	typedef ULONG (WINAPI * if_nametoindex_funcptr_t)(PCSTR name);
+
+	unsigned if_nametoindex_win(const char *ifname)
+		{
+		HMODULE library;
+		unsigned index = 0;
+
+		// Try and load the IP helper library dll
+		if ((library = LoadLibrary(TEXT("Iphlpapi")) ) != NULL )
+			{
+			if_nametoindex_funcptr_t if_nametoindex_funcptr;
+
+			// On Vista and above there is a Posix like implementation of if_nametoindex
+			if ((if_nametoindex_funcptr = (if_nametoindex_funcptr_t) GetProcAddress(library, "if_nametoindex")) != NULL )
+				{
+				index = if_nametoindex_funcptr(ifname);
+				}
+
+			FreeLibrary(library);
+			}
+
+		return index;
+		}
+
+	char * if_indextoname_win( unsigned ifindex, char *ifname)
+		{
+		HMODULE library;
+		char * name = NULL;
+
+		// Try and load the IP helper library dll
+		if ((library = LoadLibrary(TEXT("Iphlpapi")) ) != NULL )
+			{
+			if_indextoname_funcptr_t if_indextoname_funcptr;
+
+			// On Vista and above there is a Posix like implementation of if_indextoname
+			if ((if_indextoname_funcptr = (if_indextoname_funcptr_t) GetProcAddress(library, "if_indextoname")) != NULL )
+				{
+				name = if_indextoname_funcptr(ifindex, ifname);
+				}
+
+			FreeLibrary(library);
+			}
+
+		return name;
+		}
+
+	static size_t _sa_len(const struct sockaddr *addr)
+		{
+		if (addr->sa_family == AF_INET) return (sizeof(struct sockaddr_in));
+		else if (addr->sa_family == AF_INET6) return (sizeof(struct sockaddr_in6));
+		else return (sizeof(struct sockaddr));
+		}
+
+#   define SA_LEN(addr) (_sa_len(addr))
+
+#else
+	#include <unistd.h>			// For getopt() and optind
+	#include <netdb.h>			// For getaddrinfo()
+	#include <sys/time.h>		// For struct timeval
+	#include <sys/socket.h>		// For AF_INET
+	#include <netinet/in.h>		// For struct sockaddr_in()
+	#include <arpa/inet.h>		// For inet_addr()
+	#include <net/if.h>			// For if_nametoindex()
+	static const char kFilePathSep = '/';
+// #ifndef NOT_HAVE_SA_LEN
+// 	#define SA_LEN(addr) ((addr)->sa_len)
+// #else
+    #define SA_LEN(addr) (((addr)->sa_family == AF_INET6)? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr_in))
+// #endif
+#endif
+
+#if (TEST_NEW_CLIENTSTUB && !defined(__APPLE_API_PRIVATE))
+#define __APPLE_API_PRIVATE 1
+#endif
+
+// DNSServiceSetDispatchQueue is not supported on 10.6 & prior
+#if ! TEST_NEW_CLIENTSTUB && defined(__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__) && (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ - (__ENVIRONMENT_MAC_OS_X_VERSION_MIN_REQUIRED__ % 10) <= 1060)
+#undef _DNS_SD_LIBDISPATCH
+#endif
+#include "dns_sd.h"
+#include "ClientCommon.h"
+
+#if TEST_NEW_CLIENTSTUB
+#include "../mDNSShared/dnssd_ipc.c"
+#include "../mDNSShared/dnssd_clientlib.c"
+#include "../mDNSShared/dnssd_clientstub.c"
+#endif
+
+// The "+0" is to cope with the case where _DNS_SD_H is defined but empty (e.g. on Mac OS X 10.4 and earlier)
+#if _DNS_SD_H+0 >= 116
+#define HAS_NAT_PMP_API 1
+#define HAS_ADDRINFO_API 1
+#else
+#define kDNSServiceFlagsReturnIntermediates 0
+#endif
+
+//*************************************************************************************************************
+// Globals
+
+typedef union { unsigned char b[2]; unsigned short NotAnInteger; } Opaque16;
+
+static int operation;
+static uint32_t opinterface = kDNSServiceInterfaceIndexAny;
+static DNSServiceRef client    = NULL;
+static DNSServiceRef client_pa = NULL;	// DNSServiceRef for RegisterProxyAddressRecord
+static DNSServiceRef sc1, sc2, sc3;		// DNSServiceRefs for kDNSServiceFlagsShareConnection testing
+
+static int num_printed;
+static char addtest = 0;
+static DNSRecordRef record = NULL;
+static char myhinfoW[14] = "\002PC\012Windows XP";
+static char myhinfoX[ 9] = "\003Mac\004OS X";
+static char updatetest[3] = "\002AA";
+static char bigNULL[8192];	// 8K is maximum rdata we support
+
+#if _DNS_SD_LIBDISPATCH
+dispatch_queue_t main_queue;
+dispatch_source_t timer_source;
+#endif
+
+// Note: the select() implementation on Windows (Winsock2) fails with any timeout much larger than this
+#define LONG_TIME 100000000
+
+static volatile int stopNow = 0;
+static volatile int timeOut = LONG_TIME;
+
+#if _DNS_SD_LIBDISPATCH
+#define EXIT_IF_LIBDISPATCH_FATAL_ERROR(E) \
+	if (main_queue && (E) == kDNSServiceErr_ServiceNotRunning) { fprintf(stderr, "Error code %d\n", (E)); exit(0); }
+#else
+#define EXIT_IF_LIBDISPATCH_FATAL_ERROR(E)
+#endif
+
+//*************************************************************************************************************
+// Supporting Utility Functions
+
+static uint16_t GetRRType(const char *s)
+	{
+	if      (!strcasecmp(s, "A"       )) return(kDNSServiceType_A);
+	else if (!strcasecmp(s, "NS"      )) return(kDNSServiceType_NS);
+	else if (!strcasecmp(s, "MD"      )) return(kDNSServiceType_MD);
+	else if (!strcasecmp(s, "MF"      )) return(kDNSServiceType_MF);
+	else if (!strcasecmp(s, "CNAME"   )) return(kDNSServiceType_CNAME);
+	else if (!strcasecmp(s, "SOA"     )) return(kDNSServiceType_SOA);
+	else if (!strcasecmp(s, "MB"      )) return(kDNSServiceType_MB);
+	else if (!strcasecmp(s, "MG"      )) return(kDNSServiceType_MG);
+	else if (!strcasecmp(s, "MR"      )) return(kDNSServiceType_MR);
+	else if (!strcasecmp(s, "NULL"    )) return(kDNSServiceType_NULL);
+	else if (!strcasecmp(s, "WKS"     )) return(kDNSServiceType_WKS);
+	else if (!strcasecmp(s, "PTR"     )) return(kDNSServiceType_PTR);
+	else if (!strcasecmp(s, "HINFO"   )) return(kDNSServiceType_HINFO);
+	else if (!strcasecmp(s, "MINFO"   )) return(kDNSServiceType_MINFO);
+	else if (!strcasecmp(s, "MX"      )) return(kDNSServiceType_MX);
+	else if (!strcasecmp(s, "TXT"     )) return(kDNSServiceType_TXT);
+	else if (!strcasecmp(s, "RP"      )) return(kDNSServiceType_RP);
+	else if (!strcasecmp(s, "AFSDB"   )) return(kDNSServiceType_AFSDB);
+	else if (!strcasecmp(s, "X25"     )) return(kDNSServiceType_X25);
+	else if (!strcasecmp(s, "ISDN"    )) return(kDNSServiceType_ISDN);
+	else if (!strcasecmp(s, "RT"      )) return(kDNSServiceType_RT);
+	else if (!strcasecmp(s, "NSAP"    )) return(kDNSServiceType_NSAP);
+	else if (!strcasecmp(s, "NSAP_PTR")) return(kDNSServiceType_NSAP_PTR);
+	else if (!strcasecmp(s, "SIG"     )) return(kDNSServiceType_SIG);
+	else if (!strcasecmp(s, "KEY"     )) return(kDNSServiceType_KEY);
+	else if (!strcasecmp(s, "PX"      )) return(kDNSServiceType_PX);
+	else if (!strcasecmp(s, "GPOS"    )) return(kDNSServiceType_GPOS);
+	else if (!strcasecmp(s, "AAAA"    )) return(kDNSServiceType_AAAA);
+	else if (!strcasecmp(s, "LOC"     )) return(kDNSServiceType_LOC);
+	else if (!strcasecmp(s, "NXT"     )) return(kDNSServiceType_NXT);
+	else if (!strcasecmp(s, "EID"     )) return(kDNSServiceType_EID);
+	else if (!strcasecmp(s, "NIMLOC"  )) return(kDNSServiceType_NIMLOC);
+	else if (!strcasecmp(s, "SRV"     )) return(kDNSServiceType_SRV);
+	else if (!strcasecmp(s, "ATMA"    )) return(kDNSServiceType_ATMA);
+	else if (!strcasecmp(s, "NAPTR"   )) return(kDNSServiceType_NAPTR);
+	else if (!strcasecmp(s, "KX"      )) return(kDNSServiceType_KX);
+	else if (!strcasecmp(s, "CERT"    )) return(kDNSServiceType_CERT);
+	else if (!strcasecmp(s, "A6"      )) return(kDNSServiceType_A6);
+	else if (!strcasecmp(s, "DNAME"   )) return(kDNSServiceType_DNAME);
+	else if (!strcasecmp(s, "SINK"    )) return(kDNSServiceType_SINK);
+	else if (!strcasecmp(s, "OPT"     )) return(kDNSServiceType_OPT);
+	else if (!strcasecmp(s, "TKEY"    )) return(kDNSServiceType_TKEY);
+	else if (!strcasecmp(s, "TSIG"    )) return(kDNSServiceType_TSIG);
+	else if (!strcasecmp(s, "IXFR"    )) return(kDNSServiceType_IXFR);
+	else if (!strcasecmp(s, "AXFR"    )) return(kDNSServiceType_AXFR);
+	else if (!strcasecmp(s, "MAILB"   )) return(kDNSServiceType_MAILB);
+	else if (!strcasecmp(s, "MAILA"   )) return(kDNSServiceType_MAILA);
+	else if (!strcasecmp(s, "ANY"     )) return(kDNSServiceType_ANY);
+	else                                 return(atoi(s));
+	}
+
+#if HAS_NAT_PMP_API | HAS_ADDRINFO_API
+static DNSServiceProtocol GetProtocol(const char *s)
+	{
+	if      (!strcasecmp(s, "v4"      )) return(kDNSServiceProtocol_IPv4);
+	else if (!strcasecmp(s, "v6"      )) return(kDNSServiceProtocol_IPv6);
+	else if (!strcasecmp(s, "v4v6"    )) return(kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6);
+	else if (!strcasecmp(s, "v6v4"    )) return(kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6);
+	else if (!strcasecmp(s, "udp"     )) return(kDNSServiceProtocol_UDP);
+	else if (!strcasecmp(s, "tcp"     )) return(kDNSServiceProtocol_TCP);
+	else if (!strcasecmp(s, "udptcp"  )) return(kDNSServiceProtocol_UDP | kDNSServiceProtocol_TCP);
+	else if (!strcasecmp(s, "tcpudp"  )) return(kDNSServiceProtocol_UDP | kDNSServiceProtocol_TCP);
+	else                                 return(atoi(s));
+	}
+#endif
+
+//*************************************************************************************************************
+// Sample callback functions for each of the operation types
+
+static void printtimestamp(void)
+	{
+	struct tm tm;
+	int ms;
+#ifdef _WIN32
+	SYSTEMTIME sysTime;
+	time_t uct = time(NULL);
+	tm = *localtime(&uct);
+	GetLocalTime(&sysTime);
+	ms = sysTime.wMilliseconds;
+#else
+	struct timeval tv;
+	gettimeofday(&tv, NULL);
+	localtime_r((time_t*)&tv.tv_sec, &tm);
+	ms = tv.tv_usec/1000;
+#endif
+	printf("%2d:%02d:%02d.%03d  ", tm.tm_hour, tm.tm_min, tm.tm_sec, ms);
+	}
+
+#define DomainMsg(X) (((X) & kDNSServiceFlagsDefault) ? "(Default)" : \
+                      ((X) & kDNSServiceFlagsAdd)     ? "Added"     : "Removed")
+
+#define MAX_LABELS 128
+
+static void DNSSD_API enum_reply(DNSServiceRef sdref, const DNSServiceFlags flags, uint32_t ifIndex,
+	DNSServiceErrorType errorCode, const char *replyDomain, void *context)
+	{
+	DNSServiceFlags partialflags = flags & ~(kDNSServiceFlagsMoreComing | kDNSServiceFlagsAdd | kDNSServiceFlagsDefault);
+	int labels = 0, depth = 0, i, initial = 0;
+	char text[64];
+	const char *label[MAX_LABELS];
+	
+	(void)sdref;        // Unused
+	(void)ifIndex;      // Unused
+	(void)context;      // Unused
+	EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode);
+
+	// 1. Print the header
+	if (num_printed++ == 0) printf("Timestamp     Recommended %s domain\n", operation == 'E' ? "Registration" : "Browsing");
+	printtimestamp();
+	if (errorCode)
+		printf("Error code %d\n", errorCode);
+	else if (!*replyDomain)
+		printf("Error: No reply domain\n");
+	else
+		{
+		printf("%-10s", DomainMsg(flags));
+		printf("%-8s", (flags & kDNSServiceFlagsMoreComing) ? "(More)" : "");
+		if (partialflags) printf("Flags: %4X  ", partialflags);
+		else printf("             ");
+		
+		// 2. Count the labels
+		while (replyDomain && *replyDomain && labels < MAX_LABELS)
+			{
+			label[labels++] = replyDomain;
+			replyDomain = GetNextLabel(replyDomain, text);
+			}
+		
+		// 3. Decide if we're going to clump the last two or three labels (e.g. "apple.com", or "nicta.com.au")
+		if      (labels >= 3 && replyDomain - label[labels-1] <= 3 && label[labels-1] - label[labels-2] <= 4) initial = 3;
+		else if (labels >= 2 && replyDomain - label[labels-1] <= 4) initial = 2;
+		else initial = 1;
+		labels -= initial;
+	
+		// 4. Print the initial one-, two- or three-label clump
+		for (i=0; i<initial; i++)
+			{
+			GetNextLabel(label[labels+i], text);
+			if (i>0) printf(".");
+			printf("%s", text);
+			}
+		printf("\n");
+	
+		// 5. Print the remainder of the hierarchy
+		for (depth=0; depth<labels; depth++)
+			{
+			printf("                                             ");
+			for (i=0; i<=depth; i++) printf("- ");
+			GetNextLabel(label[labels-1-depth], text);
+			printf("> %s\n", text);
+			}
+		}
+
+	if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout);
+	}
+
+static int CopyLabels(char *dst, const char *lim, const char **srcp, int labels)
+	{
+	const char *src = *srcp;
+	while (*src != '.' || --labels > 0)
+		{
+		if (*src == '\\') *dst++ = *src++;	// Make sure "\." doesn't confuse us
+		if (!*src || dst >= lim) return -1;
+		*dst++ = *src++;
+		if (!*src || dst >= lim) return -1;
+		}
+	*dst++ = 0;
+	*srcp = src + 1;	// skip over final dot
+	return 0;
+	}
+
+static void DNSSD_API zonedata_resolve(DNSServiceRef sdref, const DNSServiceFlags flags, uint32_t ifIndex, DNSServiceErrorType errorCode,
+	const char *fullname, const char *hosttarget, uint16_t opaqueport, uint16_t txtLen, const unsigned char *txt, void *context)
+	{
+	union { uint16_t s; u_char b[2]; } port = { opaqueport };
+	uint16_t PortAsNumber = ((uint16_t)port.b[0]) << 8 | port.b[1];
+
+	const char *p = fullname;
+	char n[kDNSServiceMaxDomainName];
+	char t[kDNSServiceMaxDomainName];
+
+	const unsigned char *max = txt + txtLen;
+
+	(void)sdref;        // Unused
+	(void)ifIndex;      // Unused
+	(void)context;      // Unused
+
+	//if (!(flags & kDNSServiceFlagsAdd)) return;
+	if (errorCode) { printf("Error code %d\n", errorCode); return; }
+
+	if (CopyLabels(n, n + kDNSServiceMaxDomainName, &p, 3)) return;		// Fetch name+type
+	p = fullname;
+	if (CopyLabels(t, t + kDNSServiceMaxDomainName, &p, 1)) return;		// Skip first label
+	if (CopyLabels(t, t + kDNSServiceMaxDomainName, &p, 2)) return;		// Fetch next two labels (service type)
+
+	if (num_printed++ == 0)
+		{
+		printf("\n");
+		printf("; To direct clients to browse a different domain, substitute that domain in place of '@'\n");
+		printf("%-47s PTR     %s\n", "lb._dns-sd._udp", "@");
+		printf("\n");
+		printf("; In the list of services below, the SRV records will typically reference dot-local Multicast DNS names.\n");
+		printf("; When transferring this zone file data to your unicast DNS server, you'll need to replace those dot-local\n");
+		printf("; names with the correct fully-qualified (unicast) domain name of the target host offering the service.\n");
+		}
+
+	printf("\n");
+	printf("%-47s PTR     %s\n", t, n);
+	printf("%-47s SRV     0 0 %d %s ; Replace with unicast FQDN of target host\n", n, PortAsNumber, hosttarget);
+	printf("%-47s TXT    ", n);
+
+	while (txt < max)
+		{
+		const unsigned char *const end = txt + 1 + txt[0];
+		txt++;		// Skip over length byte
+		printf(" \"");
+		while (txt<end)
+			{
+			if (*txt == '\\' || *txt == '\"') printf("\\");
+			printf("%c", *txt++);
+			}
+		printf("\"");
+		}
+	printf("\n");
+
+	DNSServiceRefDeallocate(sdref);
+	free(context);
+
+	if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout);
+	}
+
+static void DNSSD_API zonedata_browse(DNSServiceRef sdref, const DNSServiceFlags flags, uint32_t ifIndex, DNSServiceErrorType errorCode,
+	const char *replyName, const char *replyType, const char *replyDomain, void *context)
+	{
+	DNSServiceRef *newref;
+
+	(void)sdref;        // Unused
+	(void)context;      // Unused
+	EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode);
+
+	if (!(flags & kDNSServiceFlagsAdd)) return;
+	if (errorCode) { printf("Error code %d\n", errorCode); return; }
+
+	newref = malloc(sizeof(*newref));
+	*newref = client;
+	DNSServiceResolve(newref, kDNSServiceFlagsShareConnection, ifIndex, replyName, replyType, replyDomain, zonedata_resolve, newref);
+	}
+
+static void DNSSD_API browse_reply(DNSServiceRef sdref, const DNSServiceFlags flags, uint32_t ifIndex, DNSServiceErrorType errorCode,
+	const char *replyName, const char *replyType, const char *replyDomain, void *context)
+	{
+	char *op = (flags & kDNSServiceFlagsAdd) ? "Add" : "Rmv";
+	(void)sdref;        // Unused
+	(void)context;      // Unused
+	EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode);
+
+	if (num_printed++ == 0) printf("Timestamp     A/R Flags if %-25s %-25s %s\n", "Domain", "Service Type", "Instance Name");
+	printtimestamp();
+	if (errorCode) printf("Error code %d\n", errorCode);
+	else printf("%s%6X%3d %-25s %-25s %s\n", op, flags, ifIndex, replyDomain, replyType, replyName);
+	if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout);
+
+	// To test selective cancellation of operations of shared sockets,
+	// cancel the current operation when we've got a multiple of five results
+	//if (operation == 'S' && num_printed % 5 == 0) DNSServiceRefDeallocate(sdref);
+	}
+
+static void ShowTXTRecord(uint16_t txtLen, const unsigned char *txtRecord)
+	{
+	const unsigned char *ptr = txtRecord;
+	const unsigned char *max = txtRecord + txtLen;
+	while (ptr < max)
+		{
+		const unsigned char *const end = ptr + 1 + ptr[0];
+		if (end > max) { printf("<< invalid data >>"); break; }
+		if (++ptr < end) printf(" ");   // As long as string is non-empty, begin with a space
+		while (ptr<end)
+			{
+			// We'd like the output to be shell-friendly, so that it can be copied and pasted unchanged into a "dns-sd -R" command.
+			// However, this is trickier than it seems. Enclosing a string in double quotes doesn't necessarily make it
+			// shell-safe, because shells still expand variables like $foo even when they appear inside quoted strings.
+			// Enclosing a string in single quotes is better, but when using single quotes even backslash escapes are ignored,
+			// meaning there's simply no way to represent a single quote (or apostrophe) inside a single-quoted string.
+			// The only remaining solution is not to surround the string with quotes at all, but instead to use backslash
+			// escapes to encode spaces and all other known shell metacharacters.
+			// (If we've missed any known shell metacharacters, please let us know.)
+			// In addition, non-printing ascii codes (0-31) are displayed as \xHH, using a two-digit hex value.
+			// Because '\' is itself a shell metacharacter (the shell escape character), it has to be escaped as "\\" to survive
+			// the round-trip to the shell and back. This means that a single '\' is represented here as EIGHT backslashes:
+			// The C compiler eats half of them, resulting in four appearing in the output.
+			// The shell parses those four as a pair of "\\" sequences, passing two backslashes to the "dns-sd -R" command.
+			// The "dns-sd -R" command interprets this single "\\" pair as an escaped literal backslash. Sigh.
+			if (strchr(" &;`'\"|*?~<>^()[]{}$", *ptr)) printf("\\");
+			if      (*ptr == '\\') printf("\\\\\\\\");
+			else if (*ptr >= ' ' ) printf("%c",        *ptr);
+			else                   printf("\\\\x%02X", *ptr);
+			ptr++;
+			}
+		}
+	}
+
+static void DNSSD_API resolve_reply(DNSServiceRef sdref, const DNSServiceFlags flags, uint32_t ifIndex, DNSServiceErrorType errorCode,
+	const char *fullname, const char *hosttarget, uint16_t opaqueport, uint16_t txtLen, const unsigned char *txtRecord, void *context)
+	{
+	union { uint16_t s; u_char b[2]; } port = { opaqueport };
+	uint16_t PortAsNumber = ((uint16_t)port.b[0]) << 8 | port.b[1];
+
+	(void)sdref;        // Unused
+	(void)ifIndex;      // Unused
+	(void)context;      // Unused
+	EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode);
+
+	if (errorCode)
+		printf("Error code %d\n", errorCode);
+	else
+		{
+		printtimestamp();
+		printf("%s can be reached at %s:%u (interface %d)", fullname, hosttarget, PortAsNumber, ifIndex);
+		if (flags) printf(" Flags: %X", flags);
+		// Don't show degenerate TXT records containing nothing but a single empty string
+		if (txtLen > 1) { printf("\n"); ShowTXTRecord(txtLen, txtRecord); }
+		printf("\n");
+		}
+
+	if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout);
+	}
+
+static void myTimerCallBack(void)
+	{
+	DNSServiceErrorType err = kDNSServiceErr_Unknown;
+
+	switch (operation)
+		{
+		case 'A':
+			{
+			switch (addtest)
+				{
+				case 0: printf("Adding Test HINFO record\n");
+						err = DNSServiceAddRecord(client, &record, 0, kDNSServiceType_HINFO, sizeof(myhinfoW), &myhinfoW[0], 0);
+						addtest = 1;
+						break;
+				case 1: printf("Updating Test HINFO record\n");
+						err = DNSServiceUpdateRecord(client, record, 0, sizeof(myhinfoX), &myhinfoX[0], 0);
+						addtest = 2;
+						break;
+				case 2: printf("Removing Test HINFO record\n");
+						err = DNSServiceRemoveRecord(client, record, 0);
+						addtest = 0;
+						break;
+				}
+			}
+			break;
+
+		case 'U':
+			{
+			if (updatetest[1] != 'Z') updatetest[1]++;
+			else                      updatetest[1] = 'A';
+			updatetest[0] = 3 - updatetest[0];
+			updatetest[2] = updatetest[1];
+			printtimestamp();
+			printf("Updating Test TXT record to %c\n", updatetest[1]);
+			err = DNSServiceUpdateRecord(client, NULL, 0, 1+updatetest[0], &updatetest[0], 0);
+			}
+			break;
+
+		case 'N':
+			{
+			printf("Adding big NULL record\n");
+			err = DNSServiceAddRecord(client, &record, 0, kDNSServiceType_NULL, sizeof(bigNULL), &bigNULL[0], 0);
+			if (err) printf("Failed: %d\n", err); else printf("Succeeded\n");
+			timeOut = LONG_TIME;
+#if _DNS_SD_LIBDISPATCH
+			if (timer_source)
+				dispatch_source_set_timer(timer_source, dispatch_time(DISPATCH_TIME_NOW, (uint64_t)timeOut * NSEC_PER_SEC),
+					(uint64_t)timeOut * NSEC_PER_SEC, 0);
+#endif
+			}
+			break;
+		}
+
+	if (err != kDNSServiceErr_NoError)
+		{
+		fprintf(stderr, "DNSService add/update/remove failed %ld\n", (long int)err);
+		stopNow = 1;
+		}
+	}
+
+static void DNSSD_API reg_reply(DNSServiceRef sdref, const DNSServiceFlags flags, DNSServiceErrorType errorCode,
+	const char *name, const char *regtype, const char *domain, void *context)
+	{
+	(void)sdref;    // Unused
+	(void)flags;    // Unused
+	(void)context;  // Unused
+	EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode);
+
+	printtimestamp();
+	printf("Got a reply for service %s.%s%s: ", name, regtype, domain);
+
+	if (errorCode == kDNSServiceErr_NoError)
+		{
+		if (flags & kDNSServiceFlagsAdd) printf("Name now registered and active\n"); 
+		else printf("Name registration removed\n"); 
+		if (operation == 'A' || operation == 'U' || operation == 'N')
+			{
+			timeOut = 5;
+#if _DNS_SD_LIBDISPATCH
+			if (timer_source)
+				dispatch_source_set_timer(timer_source, dispatch_time(DISPATCH_TIME_NOW, (uint64_t)timeOut * NSEC_PER_SEC),
+					(uint64_t)timeOut * NSEC_PER_SEC, 0);
+#endif
+			}
+		}
+	else if (errorCode == kDNSServiceErr_NameConflict)
+		{
+		printf("Name in use, please choose another\n");
+		exit(-1);
+		}
+	else
+		printf("Error %d\n", errorCode);
+
+	if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout);
+	}
+
+// Output the wire-format domainname pointed to by rd
+static int snprintd(char *p, int max, const unsigned char **rd)
+	{
+	const char *const buf = p;
+	const char *const end = p + max;
+	while (**rd) { p += snprintf(p, end-p, "%.*s.", **rd, *rd+1); *rd += 1 + **rd; }
+	*rd += 1;	// Advance over the final zero byte
+	return(p-buf);
+	}
+
+static void DNSSD_API qr_reply(DNSServiceRef sdref, const DNSServiceFlags flags, uint32_t ifIndex, DNSServiceErrorType errorCode,
+	const char *fullname, uint16_t rrtype, uint16_t rrclass, uint16_t rdlen, const void *rdata, uint32_t ttl, void *context)
+	{
+	char *op = (flags & kDNSServiceFlagsAdd) ? "Add" : "Rmv";
+	const unsigned char *rd  = rdata;
+	const unsigned char *end = (const unsigned char *) rdata + rdlen;
+	char rdb[1000] = "", *p = rdb;
+	int unknowntype = 0;
+
+	(void)sdref;    // Unused
+	(void)flags;    // Unused
+	(void)ifIndex;  // Unused
+	(void)ttl;      // Unused
+	(void)context;  // Unused
+	EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode);
+
+	if (num_printed++ == 0) printf("Timestamp     A/R Flags if %-30s%4s%4s Rdata\n", "Name", "T", "C");
+	printtimestamp();
+
+	if (!errorCode)
+		{
+		switch (rrtype)
+			{
+			case kDNSServiceType_A:
+				snprintf(rdb, sizeof(rdb), "%d.%d.%d.%d", rd[0], rd[1], rd[2], rd[3]);
+				break;
+	
+			case kDNSServiceType_NS:
+			case kDNSServiceType_CNAME:
+			case kDNSServiceType_PTR:
+			case kDNSServiceType_DNAME:
+				p += snprintd(p, sizeof(rdb), &rd);
+				break;
+	
+			case kDNSServiceType_SOA:
+				p += snprintd(p, rdb + sizeof(rdb) - p, &rd);		// mname
+				p += snprintf(p, rdb + sizeof(rdb) - p, " ");
+				p += snprintd(p, rdb + sizeof(rdb) - p, &rd);		// rname
+				p += snprintf(p, rdb + sizeof(rdb) - p, " Ser %d Ref %d Ret %d Exp %d Min %d",
+					ntohl(((uint32_t*)rd)[0]), ntohl(((uint32_t*)rd)[1]), ntohl(((uint32_t*)rd)[2]), ntohl(((uint32_t*)rd)[3]), ntohl(((uint32_t*)rd)[4]));
+				break;
+	
+			case kDNSServiceType_AAAA:
+				snprintf(rdb, sizeof(rdb), "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X",
+					rd[0x0], rd[0x1], rd[0x2], rd[0x3], rd[0x4], rd[0x5], rd[0x6], rd[0x7],
+					rd[0x8], rd[0x9], rd[0xA], rd[0xB], rd[0xC], rd[0xD], rd[0xE], rd[0xF]);
+				break;
+	
+			case kDNSServiceType_SRV:
+				p += snprintf(p, rdb + sizeof(rdb) - p, "%d %d %d ",	// priority, weight, port
+					ntohs(*(unsigned short*)rd), ntohs(*(unsigned short*)(rd+2)), ntohs(*(unsigned short*)(rd+4)));
+				rd += 6;
+				p += snprintd(p, rdb + sizeof(rdb) - p, &rd);			// target host
+				break;
+	
+			default : snprintf(rdb, sizeof(rdb), "%d bytes%s", rdlen, rdlen ? ":" : ""); unknowntype = 1; break;
+			}
+		}
+
+	printf("%s%6X%3d %-30s%4d%4d %s", op, flags, ifIndex, fullname, rrtype, rrclass, rdb);
+	if (unknowntype) while (rd < end) printf(" %02X", *rd++);
+	if (errorCode)
+		{
+		if (errorCode == kDNSServiceErr_NoSuchRecord) printf("No Such Record");
+		else if (errorCode == kDNSServiceErr_Timeout)
+			{
+			printf("No Such Record\n");
+			printf("Query Timed Out\n");
+			exit(1);
+			}
+		}
+	printf("\n");
+
+	if (operation == 'C')
+		if (flags & kDNSServiceFlagsAdd)
+			DNSServiceReconfirmRecord(flags, ifIndex, fullname, rrtype, rrclass, rdlen, rdata);
+
+	if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout);
+	}
+
+#if HAS_NAT_PMP_API
+static void DNSSD_API port_mapping_create_reply(DNSServiceRef sdref, DNSServiceFlags flags, uint32_t ifIndex, DNSServiceErrorType errorCode, uint32_t publicAddress, uint32_t protocol, uint16_t privatePort, uint16_t publicPort, uint32_t ttl, void *context)
+	{
+	(void)sdref;       // Unused
+	(void)flags;       // Unused
+	(void)context;     // Unused
+	EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode);
+
+	if (num_printed++ == 0) printf("Timestamp     if   %-20s %-15s %-15s %-15s %-6s\n", "External Address", "Protocol", "Internal Port", "External Port", "TTL");
+	printtimestamp();
+	if (errorCode && errorCode != kDNSServiceErr_DoubleNAT) printf("Error code %d\n", errorCode);
+	else
+		{
+		const unsigned char *digits = (const unsigned char *)&publicAddress;
+		char                 addr[256];
+
+		snprintf(addr, sizeof(addr), "%d.%d.%d.%d", digits[0], digits[1], digits[2], digits[3]);
+		printf("%-4d %-20s %-15d %-15d %-15d %-6d%s\n", ifIndex, addr, protocol, ntohs(privatePort), ntohs(publicPort), ttl, errorCode == kDNSServiceErr_DoubleNAT ? " Double NAT" : "");
+		}
+
+	if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout);
+	}
+#endif
+
+#if HAS_ADDRINFO_API
+static void DNSSD_API addrinfo_reply(DNSServiceRef sdref, DNSServiceFlags flags, uint32_t interfaceIndex, DNSServiceErrorType errorCode, const char *hostname, const struct sockaddr *address, uint32_t ttl, void *context)
+	{
+	char *op = (flags & kDNSServiceFlagsAdd) ? "Add" : "Rmv";
+	char addr[256] = "";
+	(void) sdref;
+	(void) context;
+	EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode);
+
+	if (num_printed++ == 0) printf("Timestamp     A/R Flags if %-25s %-44s %s\n", "Hostname", "Address", "TTL");
+	printtimestamp();
+
+	if (address && address->sa_family == AF_INET)
+		{
+		const unsigned char *b = (const unsigned char *) &((struct sockaddr_in *)address)->sin_addr;
+		snprintf(addr, sizeof(addr), "%d.%d.%d.%d", b[0], b[1], b[2], b[3]);
+		}
+	else if (address && address->sa_family == AF_INET6)
+		{
+		char if_name[IFNAMSIZ];		// Older Linux distributions don't define IF_NAMESIZE
+		const struct sockaddr_in6 *s6 = (const struct sockaddr_in6 *)address;
+		const unsigned char       *b  = (const unsigned char *      )&s6->sin6_addr;
+		if (!if_indextoname(s6->sin6_scope_id, if_name))
+			snprintf(if_name, sizeof(if_name), "<%d>", s6->sin6_scope_id);
+		snprintf(addr, sizeof(addr), "%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X%%%s",
+				b[0x0], b[0x1], b[0x2], b[0x3], b[0x4], b[0x5], b[0x6], b[0x7],
+				b[0x8], b[0x9], b[0xA], b[0xB], b[0xC], b[0xD], b[0xE], b[0xF], if_name);
+		}
+
+	printf("%s%6X%3d %-25s %-44s %d", op, flags, interfaceIndex, hostname, addr, ttl);
+	if (errorCode)
+		{
+		if (errorCode == kDNSServiceErr_NoSuchRecord) printf("   No Such Record");
+		else                                          printf("   Error code %d", errorCode);
+		}
+	printf("\n");
+
+	if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout);
+	}
+#endif
+
+//*************************************************************************************************************
+// The main test function
+
+static void HandleEvents(void)
+#if _DNS_SD_LIBDISPATCH
+	{
+	main_queue = dispatch_get_main_queue();
+	if (client)  DNSServiceSetDispatchQueue(client, main_queue);
+	if (client_pa)  DNSServiceSetDispatchQueue(client_pa, main_queue);
+	if (operation == 'A' || operation == 'U' || operation == 'N')
+		{
+		timer_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_TIMER, 0, 0, main_queue);
+		if (timer_source)
+			{
+			// Start the timer "timeout" seconds into the future and repeat it every "timeout" seconds
+			dispatch_source_set_timer(timer_source, dispatch_time(DISPATCH_TIME_NOW, (uint64_t)timeOut * NSEC_PER_SEC),
+				(uint64_t)timeOut * NSEC_PER_SEC, 0);
+			dispatch_source_set_event_handler(timer_source, ^{myTimerCallBack();});
+			dispatch_resume(timer_source);
+			}
+		}
+	dispatch_main();
+	}
+#else
+	{
+	int dns_sd_fd  = client    ? DNSServiceRefSockFD(client   ) : -1;
+	int dns_sd_fd2 = client_pa ? DNSServiceRefSockFD(client_pa) : -1;
+	int nfds = dns_sd_fd + 1;
+	fd_set readfds;
+	struct timeval tv;
+	int result;
+	
+	if (dns_sd_fd2 > dns_sd_fd) nfds = dns_sd_fd2 + 1;
+
+	while (!stopNow)
+		{
+		// 1. Set up the fd_set as usual here.
+		// This example client has no file descriptors of its own,
+		// but a real application would call FD_SET to add them to the set here
+		FD_ZERO(&readfds);
+
+		// 2. Add the fd for our client(s) to the fd_set
+		if (client   ) FD_SET(dns_sd_fd , &readfds);
+		if (client_pa) FD_SET(dns_sd_fd2, &readfds);
+
+		// 3. Set up the timeout.
+		tv.tv_sec  = timeOut;
+		tv.tv_usec = 0;
+
+		result = select(nfds, &readfds, (fd_set*)NULL, (fd_set*)NULL, &tv);
+		if (result > 0)
+			{
+			DNSServiceErrorType err = kDNSServiceErr_NoError;
+			if      (client    && FD_ISSET(dns_sd_fd , &readfds)) err = DNSServiceProcessResult(client   );
+			else if (client_pa && FD_ISSET(dns_sd_fd2, &readfds)) err = DNSServiceProcessResult(client_pa);
+			if (err) { fprintf(stderr, "DNSServiceProcessResult returned %d\n", err); stopNow = 1; }
+			}
+		else if (result == 0)
+			myTimerCallBack();
+		else
+			{
+			printf("select() returned %d errno %d %s\n", result, errno, strerror(errno));
+			if (errno != EINTR) stopNow = 1;
+			}
+		}
+	}
+#endif
+
+static int getfirstoption(int argc, char **argv, const char *optstr, int *pOptInd)
+// Return the recognized option in optstr and the option index of the next arg.
+#if NOT_HAVE_GETOPT
+	{
+	int i;
+	for (i=1; i < argc; i++)
+		{
+		if (argv[i][0] == '-' && &argv[i][1] && 
+			 NULL != strchr(optstr, argv[i][1]))
+			{
+			*pOptInd = i + 1;
+			return argv[i][1];
+			}
+		}
+	return -1;
+	}
+#else
+	{
+	int o = getopt(argc, (char *const *)argv, optstr);
+	*pOptInd = optind;
+	return o;
+	}
+#endif
+
+static void DNSSD_API MyRegisterRecordCallback(DNSServiceRef service, DNSRecordRef rec, const DNSServiceFlags flags,
+    DNSServiceErrorType errorCode, void *context)
+	{
+	char *name = (char *)context;
+	
+	(void)service;	// Unused
+	(void)rec;		// Unused
+	(void)flags;	// Unused
+	EXIT_IF_LIBDISPATCH_FATAL_ERROR(errorCode);
+
+	printtimestamp();
+	printf("Got a reply for record %s: ", name);
+
+	switch (errorCode)
+		{
+		case kDNSServiceErr_NoError:      printf("Name now registered and active\n"); break;
+		case kDNSServiceErr_NameConflict: printf("Name in use, please choose another\n"); exit(-1);
+		default:                          printf("Error %d\n", errorCode); break;
+		}
+	if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout);
+	// DNSServiceRemoveRecord(service, rec, 0); to test record removal
+
+#if 0	// To test updating of individual records registered via DNSServiceRegisterRecord
+	if (!errorCode)
+		{
+		int x = 0x11111111;
+		printf("Updating\n");
+		DNSServiceUpdateRecord(service, rec, 0, sizeof(x), &x, 0);
+		}
+#endif
+
+	if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout);
+	}
+
+static void getip(const char *const name, struct sockaddr_storage *result)
+	{
+	struct addrinfo *addrs = NULL;
+	int err = getaddrinfo(name, NULL, NULL, &addrs);
+	if (err) fprintf(stderr, "getaddrinfo error %d for %s", err, name);
+	else memcpy(result, addrs->ai_addr, SA_LEN(addrs->ai_addr));
+	if (addrs) freeaddrinfo(addrs);
+	}
+
+static DNSServiceErrorType RegisterProxyAddressRecord(DNSServiceRef sdref, const char *host, const char *ip, DNSServiceFlags flags)
+	{
+	// Call getip() after the call DNSServiceCreateConnection().
+	// On the Win32 platform, WinSock must be initialized for getip() to succeed.
+	// Any DNSService* call will initialize WinSock for us, so we make sure
+	// DNSServiceCreateConnection() is called before getip() is.
+	struct sockaddr_storage hostaddr;
+	getip(ip, &hostaddr);
+	flags |= kDNSServiceFlagsUnique;
+	if (hostaddr.ss_family == AF_INET)
+		return(DNSServiceRegisterRecord(sdref, &record, flags, opinterface, host,
+			kDNSServiceType_A,    kDNSServiceClass_IN,  4, &((struct sockaddr_in *)&hostaddr)->sin_addr,  240, MyRegisterRecordCallback, (void*)host));
+	else if (hostaddr.ss_family == AF_INET6)
+		return(DNSServiceRegisterRecord(sdref, &record, flags, opinterface, host,
+			kDNSServiceType_AAAA, kDNSServiceClass_IN, 16, &((struct sockaddr_in6*)&hostaddr)->sin6_addr, 240, MyRegisterRecordCallback, (void*)host));
+	else return(kDNSServiceErr_BadParam);
+	}
+
+#define HexVal(X) ( ((X) >= '0' && (X) <= '9') ? ((X) - '0'     ) :  \
+					((X) >= 'A' && (X) <= 'F') ? ((X) - 'A' + 10) :  \
+					((X) >= 'a' && (X) <= 'f') ? ((X) - 'a' + 10) : 0)
+
+#define HexPair(P) ((HexVal((P)[0]) << 4) | HexVal((P)[1]))
+
+static DNSServiceErrorType RegisterService(DNSServiceRef *sdref,
+	const char *nam, const char *typ, const char *dom, const char *host, const char *port, int argc, char **argv, DNSServiceFlags flags)
+	{
+	uint16_t PortAsNumber = atoi(port);
+	Opaque16 registerPort = { { PortAsNumber >> 8, PortAsNumber & 0xFF } };
+	unsigned char txt[2048] = "";
+	unsigned char *ptr = txt;
+	int i;
+	
+	if (nam[0] == '.' && nam[1] == 0) nam = "";   // We allow '.' on the command line as a synonym for empty string
+	if (dom[0] == '.' && dom[1] == 0) dom = "";   // We allow '.' on the command line as a synonym for empty string
+	
+	printf("Registering Service %s.%s%s%s", nam[0] ? nam : "<<Default>>", typ, dom[0] ? "." : "", dom);
+	if (host && *host) printf(" host %s", host);
+	printf(" port %s", port);
+
+	if (argc)
+		{
+		for (i = 0; i < argc; i++)
+			{
+			const char *p = argv[i];
+			*ptr = 0;
+			while (*p && *ptr < 255 && ptr + 1 + *ptr < txt+sizeof(txt))
+				{
+				if      (p[0] != '\\' || p[1] == 0)                       { ptr[++*ptr] = *p;           p+=1; }
+				else if (p[1] == 'x' && isxdigit(p[2]) && isxdigit(p[3])) { ptr[++*ptr] = HexPair(p+2); p+=4; }
+				else                                                      { ptr[++*ptr] = p[1];         p+=2; }
+				}
+			ptr += 1 + *ptr;
+			}
+		printf(" TXT");
+		ShowTXTRecord(ptr-txt, txt);
+		}
+	printf("\n");
+	
+	//flags |= kDNSServiceFlagsAllowRemoteQuery;
+	//flags |= kDNSServiceFlagsNoAutoRename;
+
+	return(DNSServiceRegister(sdref, flags, opinterface, nam, typ, dom, host, registerPort.NotAnInteger, (uint16_t) (ptr-txt), txt, reg_reply, NULL));
+	}
+
+#define TypeBufferSize 80
+static char *gettype(char *buffer, char *typ)
+	{
+	if (!typ || !*typ || (typ[0] == '.' && typ[1] == 0)) typ = "_http._tcp";
+	if (!strchr(typ, '.')) { snprintf(buffer, TypeBufferSize, "%s._tcp", typ); typ = buffer; }
+	return(typ);
+	}
+
+int main(int argc, char **argv)
+	{
+	DNSServiceErrorType err;
+	char buffer[TypeBufferSize], *typ, *dom;
+	int opi;
+	DNSServiceFlags flags = 0;
+
+	// Extract the program name from argv[0], which by convention contains the path to this executable.
+	// Note that this is just a voluntary convention, not enforced by the kernel --
+	// the process calling exec() can pass bogus data in argv[0] if it chooses to.
+	const char *a0 = strrchr(argv[0], kFilePathSep) + 1;
+	if (a0 == (const char *)1) a0 = argv[0];
+
+#if defined(_WIN32)
+	HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
+#endif
+
+#if TEST_NEW_CLIENTSTUB
+	printf("Using embedded copy of dnssd_clientstub instead of system library\n");
+	if (sizeof(argv) == 8) printf("Running in 64-bit mode\n");
+#endif
+
+	// Test code for TXTRecord functions
+	//TXTRecordRef txtRecord;
+	//TXTRecordCreate(&txtRecord, 0, NULL);
+	//TXTRecordSetValue(&txtRecord, "aaa", 1, "b");
+	//printf("%d\n", TXTRecordContainsKey(TXTRecordGetLength(&txtRecord), TXTRecordGetBytesPtr(&txtRecord), "Aaa"));
+
+	if (argc > 1 && !strcmp(argv[1], "-lo"))
+		{
+		argc--;
+		argv++;
+		opinterface = kDNSServiceInterfaceIndexLocalOnly;
+		printf("Using LocalOnly\n");
+		}
+
+	if (argc > 1 && (!strcmp(argv[1], "-p2p") || !strcmp(argv[1], "-P2P")))
+		{
+		argc--;
+		argv++;
+		opinterface = kDNSServiceInterfaceIndexP2P;
+		printf("Using P2P\n");
+		}
+
+	if (argc > 1 && !strcasecmp(argv[1], "-includep2p"))
+		{
+		argc--;
+		argv++;
+		flags |= kDNSServiceFlagsIncludeP2P;
+		printf("Including P2P\n");
+		}
+
+	if (argc > 2 && !strcmp(argv[1], "-i"))
+		{
+		opinterface = if_nametoindex(argv[2]);
+		if (!opinterface) opinterface = atoi(argv[2]);
+		if (!opinterface) { fprintf(stderr, "Unknown interface %s\n", argv[2]); goto Fail; }
+		argc -= 2;
+		argv += 2;
+		}
+
+	if (argc < 2) goto Fail;        // Minimum command line is the command name and one argument
+	operation = getfirstoption(argc, argv, "EFBZLlRPQqtCAUNTMISV"
+								#if HAS_NAT_PMP_API
+									"X"
+								#endif
+								#if HAS_ADDRINFO_API
+									"G"
+								#endif
+								, &opi);
+	if (operation == -1) goto Fail;
+
+	if (opinterface) printf("Using interface %d\n", opinterface);
+
+	switch (operation)
+		{
+		case 'E':	printf("Looking for recommended registration domains:\n");
+					err = DNSServiceEnumerateDomains(&client, kDNSServiceFlagsRegistrationDomains, opinterface, enum_reply, NULL);
+					break;
+
+		case 'F':	printf("Looking for recommended browsing domains:\n");
+					err = DNSServiceEnumerateDomains(&client, kDNSServiceFlagsBrowseDomains, opinterface, enum_reply, NULL);
+					//enum_reply(client, kDNSServiceFlagsAdd, 0, 0, "nicta.com.au.", NULL);
+					//enum_reply(client, kDNSServiceFlagsAdd, 0, 0, "bonjour.nicta.com.au.", NULL);
+					//enum_reply(client, kDNSServiceFlagsAdd, 0, 0, "ibm.com.", NULL);
+					//enum_reply(client, kDNSServiceFlagsAdd, 0, 0, "dns-sd.ibm.com.", NULL);
+					break;
+
+		case 'B':	typ = (argc < opi+1) ? "" : argv[opi+0];
+					dom = (argc < opi+2) ? "" : argv[opi+1];  // Missing domain argument is the same as empty string i.e. use system default(s)
+					typ = gettype(buffer, typ);
+					if (dom[0] == '.' && dom[1] == 0) dom[0] = 0;   // We allow '.' on the command line as a synonym for empty string
+					printf("Browsing for %s%s%s\n", typ, dom[0] ? "." : "", dom);
+					err = DNSServiceBrowse(&client, flags, opinterface, typ, dom, browse_reply, NULL);
+					break;
+
+		case 'Z':	typ = (argc < opi+1) ? "" : argv[opi+0];
+					dom = (argc < opi+2) ? "" : argv[opi+1];  // Missing domain argument is the same as empty string i.e. use system default(s)
+					typ = gettype(buffer, typ);
+					if (dom[0] == '.' && dom[1] == 0) dom[0] = 0;   // We allow '.' on the command line as a synonym for empty string
+					printf("Browsing for %s%s%s\n", typ, dom[0] ? "." : "", dom);
+					err = DNSServiceCreateConnection(&client);
+					sc1 = client;
+					err = DNSServiceBrowse(&sc1, kDNSServiceFlagsShareConnection, opinterface, typ, dom, zonedata_browse, NULL);
+					break;
+
+		case 'l':
+		case 'L':	{
+					DNSServiceFlags rflags = 0;
+					if (argc < opi+2) goto Fail;
+ 					typ = (argc < opi+2) ? ""      : argv[opi+1];
+ 					dom = (argc < opi+3) ? "local" : argv[opi+2];
+ 					typ = gettype(buffer, typ);
+ 					if (dom[0] == '.' && dom[1] == 0) dom = "local";   // We allow '.' on the command line as a synonym for "local"
+ 					printf("Lookup %s.%s.%s\n", argv[opi+0], typ, dom);
+					if (operation == 'l') rflags |= kDNSServiceFlagsWakeOnResolve;
+					err = DNSServiceResolve(&client, rflags, opinterface, argv[opi+0], typ, dom, resolve_reply, NULL);
+ 					break;
+					}
+
+		case 'R':	if (argc < opi+4) goto Fail;
+					typ = (argc < opi+2) ? "" : argv[opi+1];
+					dom = (argc < opi+3) ? "" : argv[opi+2];
+					typ = gettype(buffer, typ);
+					if (dom[0] == '.' && dom[1] == 0) dom[0] = 0;   // We allow '.' on the command line as a synonym for empty string
+					err = RegisterService(&client, argv[opi+0], typ, dom, NULL, argv[opi+3], argc-(opi+4), argv+(opi+4), flags);
+					break;
+
+		case 'P':	if (argc < opi+6) goto Fail;
+					err = DNSServiceCreateConnection(&client_pa);
+					if (err) { fprintf(stderr, "DNSServiceCreateConnection returned %d\n", err); return(err); }
+					err = RegisterProxyAddressRecord(client_pa, argv[opi+4], argv[opi+5], flags);
+					if (err) break;
+					err = RegisterService(&client, argv[opi+0], gettype(buffer, argv[opi+1]), argv[opi+2], argv[opi+4], argv[opi+3], argc-(opi+6), argv+(opi+6), flags);
+					break;
+
+		case 't':
+		case 'q':
+		case 'Q':
+		case 'C':	{
+					uint16_t rrtype, rrclass;
+					flags |= kDNSServiceFlagsReturnIntermediates;
+					if (operation == 'q') flags |= kDNSServiceFlagsSuppressUnusable;
+					if (operation == 't') flags |= (kDNSServiceFlagsSuppressUnusable | kDNSServiceFlagsTimeout);
+					if (argc < opi+1) goto Fail;
+					rrtype = (argc <= opi+1) ? kDNSServiceType_A  : GetRRType(argv[opi+1]);
+					rrclass = (argc <= opi+2) ? kDNSServiceClass_IN : atoi(argv[opi+2]);
+					if (rrtype == kDNSServiceType_TXT || rrtype == kDNSServiceType_PTR) flags |= kDNSServiceFlagsLongLivedQuery;
+					err = DNSServiceQueryRecord(&client, flags, opinterface, argv[opi+0], rrtype, rrclass, qr_reply, NULL);
+					break;
+					}
+
+		case 'A':
+		case 'U':
+		case 'N':	{
+					Opaque16 registerPort = { { 0x12, 0x34 } };
+					static const char TXT[] = "\xC" "First String" "\xD" "Second String" "\xC" "Third String";
+					printf("Registering Service Test._testupdate._tcp.local.\n");
+					err = DNSServiceRegister(&client, 0, opinterface, "Test", "_testupdate._tcp.", "", NULL, registerPort.NotAnInteger, sizeof(TXT)-1, TXT, reg_reply, NULL);
+					break;
+					}
+
+		case 'T':	{
+					Opaque16 registerPort = { { 0x23, 0x45 } };
+					char TXT[1024];
+					unsigned int i;
+					for (i=0; i<sizeof(TXT); i++)
+						if ((i & 0x1F) == 0) TXT[i] = 0x1F; else TXT[i] = 'A' + (i >> 5);
+					printf("Registering Service Test._testlargetxt._tcp.local.\n");
+					err = DNSServiceRegister(&client, 0, opinterface, "Test", "_testlargetxt._tcp.", "", NULL, registerPort.NotAnInteger, sizeof(TXT), TXT, reg_reply, NULL);
+					break;
+					}
+
+		case 'M':	{
+					pid_t pid = getpid();
+					Opaque16 registerPort = { { pid >> 8, pid & 0xFF } };
+					static const char TXT1[] = "\xC" "First String"  "\xD" "Second String" "\xC" "Third String";
+					static const char TXT2[] = "\xD" "Fourth String" "\xC" "Fifth String"  "\xC" "Sixth String";
+					printf("Registering Service Test._testdualtxt._tcp.local.\n");
+					err = DNSServiceRegister(&client, flags, opinterface, "Test", "_testdualtxt._tcp.", "", NULL, registerPort.NotAnInteger, sizeof(TXT1)-1, TXT1, reg_reply, NULL);
+					if (!err) err = DNSServiceAddRecord(client, &record, flags, kDNSServiceType_TXT, sizeof(TXT2)-1, TXT2, 0);
+					break;
+					}
+
+		case 'I':	{
+					pid_t pid = getpid();
+					Opaque16 registerPort = { { pid >> 8, pid & 0xFF } };
+					static const char TXT[] = "\x09" "Test Data";
+					printf("Registering Service Test._testtxt._tcp.local.\n");
+					err = DNSServiceRegister(&client, 0, opinterface, "Test", "_testtxt._tcp.", "", NULL, registerPort.NotAnInteger, 0, NULL, reg_reply, NULL);
+					if (!err) err = DNSServiceUpdateRecord(client, NULL, 0, sizeof(TXT)-1, TXT, 0);
+					break;
+					}
+
+#if HAS_NAT_PMP_API
+		case 'X':   {
+					if (argc == opi)	// If no arguments, just fetch IP address
+						err = DNSServiceNATPortMappingCreate(&client, 0, 0, 0, 0, 0, 0, port_mapping_create_reply, NULL);
+					else if (argc >= opi+2 && atoi(argv[opi+0]) == 0)
+						{
+						DNSServiceProtocol prot  = GetProtocol(argv[opi+0]);						// Must specify TCP or UDP
+						uint16_t IntPortAsNumber = atoi(argv[opi+1]);							// Must specify internal port
+						uint16_t ExtPortAsNumber = (argc < opi+3) ? 0 : atoi(argv[opi+2]);	// Optional desired external port
+						uint32_t ttl             = (argc < opi+4) ? 0 : atoi(argv[opi+3]);	// Optional desired lease lifetime
+						Opaque16 intp = { { IntPortAsNumber >> 8, IntPortAsNumber & 0xFF } };
+						Opaque16 extp = { { ExtPortAsNumber >> 8, ExtPortAsNumber & 0xFF } };
+						err = DNSServiceNATPortMappingCreate(&client, 0, 0, prot, intp.NotAnInteger, extp.NotAnInteger, ttl, port_mapping_create_reply, NULL);
+						}
+					else goto Fail;
+					break;
+		            }
+#endif
+
+#if HAS_ADDRINFO_API
+		case 'G':   {
+					if (argc != opi+2) goto Fail;
+					else err = DNSServiceGetAddrInfo(&client, kDNSServiceFlagsReturnIntermediates, opinterface, GetProtocol(argv[opi+0]), argv[opi+1], addrinfo_reply, NULL);
+					break;
+		            }
+#endif
+
+		case 'S':	{
+					Opaque16 registerPort = { { 0x23, 0x45 } };		// 9029 decimal
+					unsigned char txtrec[16] = "\xF" "/path=test.html";
+					DNSRecordRef rec;
+					unsigned char nulrec[4] = "1234";
+
+					err = DNSServiceCreateConnection(&client);
+					if (err) { fprintf(stderr, "DNSServiceCreateConnection failed %ld\n", (long int)err); return (-1); }
+
+					sc1 = client;
+					err = DNSServiceBrowse(&sc1, kDNSServiceFlagsShareConnection, opinterface, "_http._tcp", "", browse_reply, NULL);
+					if (err) { fprintf(stderr, "DNSServiceBrowse _http._tcp failed %ld\n", (long int)err); return (-1); }
+
+					sc2 = client;
+					err = DNSServiceBrowse(&sc2, kDNSServiceFlagsShareConnection, opinterface, "_ftp._tcp", "", browse_reply, NULL);
+					if (err) { fprintf(stderr, "DNSServiceBrowse _ftp._tcp failed %ld\n", (long int)err); return (-1); }
+
+					sc3 = client;
+					err = DNSServiceRegister(&sc3, kDNSServiceFlagsShareConnection, opinterface, "kDNSServiceFlagsShareConnection",
+						"_http._tcp", "local", NULL, registerPort.NotAnInteger, 0, NULL, reg_reply, NULL);
+					if (err) { fprintf(stderr, "SharedConnection DNSServiceRegister failed %ld\n", (long int)err); return (-1); }
+
+					err = DNSServiceUpdateRecord(sc3, NULL, 0, sizeof(txtrec), txtrec, 0);
+					if (err) { fprintf(stderr, "SharedConnection DNSServiceUpdateRecord failed %ld\n", (long int)err); return (-1); }
+
+					err = DNSServiceAddRecord(sc3, &rec, 0, kDNSServiceType_NULL, sizeof(nulrec), nulrec, 0);
+					if (err) { fprintf(stderr, "SharedConnection DNSServiceAddRecord failed %ld\n", (long int)err); return (-1); }
+
+					err = DNSServiceRemoveRecord(sc3, rec, 0);
+					if (err) { fprintf(stderr, "SharedConnection DNSServiceRemoveRecord failed %ld\n", (long int)err); return (-1); }
+
+					break;
+					}
+
+		case 'V':   {
+					uint32_t v;
+					uint32_t size = sizeof(v);
+					err = DNSServiceGetProperty(kDNSServiceProperty_DaemonVersion, &v, &size);
+					if (err) fprintf(stderr, "DNSServiceGetProperty failed %ld\n", (long int)err);
+					else printf("Currently running daemon (system service) is version %d.%d\n", v / 10000, v / 100 % 100);
+					exit(0);
+    				}
+
+		default: goto Fail;
+		}
+
+	if (!client || err != kDNSServiceErr_NoError) { fprintf(stderr, "DNSService call failed %ld\n", (long int)err); return (-1); }
+	HandleEvents();
+
+	// Be sure to deallocate the DNSServiceRef when you're finished
+	if (client   ) DNSServiceRefDeallocate(client   );
+	if (client_pa) DNSServiceRefDeallocate(client_pa);
+	return 0;
+
+Fail:
+	fprintf(stderr, "%s -E                  (Enumerate recommended registration domains)\n", a0);
+	fprintf(stderr, "%s -F                      (Enumerate recommended browsing domains)\n", a0);
+	fprintf(stderr, "%s -B        <Type> <Domain>        (Browse for services instances)\n", a0);
+	fprintf(stderr, "%s -L <Name> <Type> <Domain>           (Look up a service instance)\n", a0);
+	fprintf(stderr, "%s -R <Name> <Type> <Domain> <Port> [<TXT>...] (Register a service)\n", a0);
+	fprintf(stderr, "%s -P <Name> <Type> <Domain> <Port> <Host> <IP> [<TXT>...]  (Proxy)\n", a0);
+	fprintf(stderr, "%s -Z        <Type> <Domain>   (Output results in Zone File format)\n", a0);
+	fprintf(stderr, "%s -Q <FQDN> <rrtype> <rrclass> (Generic query for any record type)\n", a0);
+	fprintf(stderr, "%s -C <FQDN> <rrtype> <rrclass>   (Query; reconfirming each result)\n", a0);
+#if HAS_NAT_PMP_API
+	fprintf(stderr, "%s -X udp/tcp/udptcp <IntPort> <ExtPort> <TTL>   (NAT Port Mapping)\n", a0);
+#endif
+#if HAS_ADDRINFO_API
+	fprintf(stderr, "%s -G v4/v6/v4v6 <Hostname>  (Get address information for hostname)\n", a0);
+#endif
+	fprintf(stderr, "%s -V    (Get version of currently running daemon / system service)\n", a0);
+
+	fprintf(stderr, "%s -A                      (Test Adding/Updating/Deleting a record)\n", a0);
+	fprintf(stderr, "%s -U                                  (Test updating a TXT record)\n", a0);
+	fprintf(stderr, "%s -N                             (Test adding a large NULL record)\n", a0);
+	fprintf(stderr, "%s -T                            (Test creating a large TXT record)\n", a0);
+	fprintf(stderr, "%s -M      (Test creating a registration with multiple TXT records)\n", a0);
+	fprintf(stderr, "%s -I   (Test registering and then immediately updating TXT record)\n", a0);
+	fprintf(stderr, "%s -S                 (Test multiple operations on a shared socket)\n", a0);
+	return 0;
+	}
+
+// Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion
+// e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4"
+// To expand "version" to its value before making the string, use STRINGIFY(version) instead
+#define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) #s
+#define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s)
+
+#ifndef __ANDROID__
+// NOT static -- otherwise the compiler may optimize it out
+// The "@(#) " pattern is a special prefix the "what" command looks for
+const char VersionString_SCCS[] = "@(#) dns-sd " STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")";
+#endif
+
+#if _BUILDING_XCODE_PROJECT_
+// If the process crashes, then this string will be magically included in the automatically-generated crash log
+const char *__crashreporter_info__ = VersionString_SCCS + 5;
+asm(".desc ___crashreporter_info__, 0x10");
+#endif
diff --git a/mdnsresponder/LICENSE b/mdnsresponder/LICENSE
new file mode 100644
index 0000000..f458904
--- /dev/null
+++ b/mdnsresponder/LICENSE
@@ -0,0 +1,13 @@
+The majority of the source code in the mDNSResponder project is licensed
+under the terms of the Apache License, Version 2.0, available from:
+   <http://www.apache.org/licenses/LICENSE-2.0>
+
+To accommodate license compatibility with the widest possible range
+of client code licenses, the shared library code, which is linked
+at runtime into the same address space as the client using it, is
+licensed under the terms of the "Three-Clause BSD License".
+
+The Linux Name Service Switch code, contributed by National ICT
+Australia Ltd (NICTA) is licensed under the terms of the NICTA Public
+Software Licence (which is substantially similar to the "Three-Clause
+BSD License", with some additional language pertaining to Australian law).
diff --git a/mdnsresponder/MODULE_LICENSE_APACHE2 b/mdnsresponder/MODULE_LICENSE_APACHE2
new file mode 100644
index 0000000..e69de29
--- /dev/null
+++ b/mdnsresponder/MODULE_LICENSE_APACHE2
diff --git a/mdnsresponder/Makefile b/mdnsresponder/Makefile
new file mode 100644
index 0000000..7233229
--- /dev/null
+++ b/mdnsresponder/Makefile
@@ -0,0 +1,41 @@
+#
+# Top level makefile for Build & Integration.
+# 
+# This file is used to facilitate checking the mDNSResponder project
+# directly out of CVS and submitting to B&I at Apple.
+#
+# The various platform directories contain makefiles or projects
+# specific to that platform.
+#
+#    B&I builds must respect the following target:
+#         install:
+#         installsrc:
+#         installhdrs:
+#         clean:
+#
+
+include /Developer/Makefiles/pb_makefiles/platform.make
+
+MVERS = "mDNSResponder-320.10.80"
+
+DDNSWRITECONFIG = "$(DSTROOT)/Library/Application Support/Bonjour/ddnswriteconfig"
+
+installSome:
+	cd "$(SRCROOT)/mDNSMacOSX"; xcodebuild install     OBJROOT=$(OBJROOT) SYMROOT=$(SYMROOT) DSTROOT=$(DSTROOT) MVERS=$(MVERS) SDKROOT=$(SDKROOT) -target Build\ Some
+
+SystemLibraries:
+	cd "$(SRCROOT)/mDNSMacOSX"; xcodebuild install     OBJROOT=$(OBJROOT) SYMROOT=$(SYMROOT) DSTROOT=$(DSTROOT) MVERS=$(MVERS) SDKROOT=$(SDKROOT) -target SystemLibraries
+
+install:
+	cd "$(SRCROOT)/mDNSMacOSX"; xcodebuild install     OBJROOT=$(OBJROOT) SYMROOT=$(SYMROOT) DSTROOT=$(DSTROOT) MVERS=$(MVERS) SDKROOT=$(SDKROOT)
+	# Make sure ddnswriteconfig is owned by root:wheel, then make it setuid root executable
+	if test -e $(DDNSWRITECONFIG) ; then chown 0:80 $(DDNSWRITECONFIG) ; chmod 4555 $(DDNSWRITECONFIG) ; fi
+
+installsrc:
+	ditto . "$(SRCROOT)"
+
+installhdrs::
+	cd "$(SRCROOT)/mDNSMacOSX"; xcodebuild installhdrs OBJROOT=$(OBJROOT) SYMROOT=$(SYMROOT) DSTROOT=$(DSTROOT) MVERS=$(MVERS) SDKROOT=$(SDKROOT)  -target SystemLibraries
+
+clean::
+	echo clean
diff --git a/mdnsresponder/NOTICE b/mdnsresponder/NOTICE
new file mode 100644
index 0000000..f458904
--- /dev/null
+++ b/mdnsresponder/NOTICE
@@ -0,0 +1,13 @@
+The majority of the source code in the mDNSResponder project is licensed
+under the terms of the Apache License, Version 2.0, available from:
+   <http://www.apache.org/licenses/LICENSE-2.0>
+
+To accommodate license compatibility with the widest possible range
+of client code licenses, the shared library code, which is linked
+at runtime into the same address space as the client using it, is
+licensed under the terms of the "Three-Clause BSD License".
+
+The Linux Name Service Switch code, contributed by National ICT
+Australia Ltd (NICTA) is licensed under the terms of the NICTA Public
+Software Licence (which is substantially similar to the "Three-Clause
+BSD License", with some additional language pertaining to Australian law).
diff --git a/mdnsresponder/PrivateDNS.txt b/mdnsresponder/PrivateDNS.txt
new file mode 100644
index 0000000..e66bec8
--- /dev/null
+++ b/mdnsresponder/PrivateDNS.txt
@@ -0,0 +1,275 @@
+Private DNS
+
+Summary
+
+Private DNS is an extension to standard Wide Area Bonjour that allows
+for secure, encrypted, and authorized communications. Private data sent
+from a client to a DNS server is encrypted using Transport Layer
+Security (TLS), ensuring that the data is hidden from prying eyes, and
+contains Transaction Signatures (TSIG), so the server can authorize the
+request. TSIGs are typically associated with Dynamic Updates; we are
+using them for standard and long-lived queries as well. Private DNS also
+protects Dynamic Updates from eavesdropping, by wrapping the update in a
+TLS communication channel if the server has been configured appropriately.
+
+Architectural Overview
+
+mDNSResponder has been modified to automatically issue a private query
+when necessary. After receiving an NXDOMAIN error, mDNSResponder checks
+in the system keychain to see if the user has a DNS query key (TSIG key)
+for the name in question, or for a parent of that name. If a suitable
+key is found, mDNSResponder looks up the zone data associated with the
+name of the question. After determining the correct name server,
+mDNSResponder looks up an additional SRV record "_dns-private._tcp". If
+it finds this record, mDNSResponder will re-issue the query privately.
+If either there is no _dns-private._tcp record, or there is no secret
+key, the call fails as it initially did, with an NXDOMAIN error.
+
+Once the secret key is found and the SRV record is looked up, mDNSResponder
+opens a TLS connection to the server on the port specified in the SRV
+record just looked up. After the connection succeeds, mDNSResponder
+can proceed to use that communication channel to make requests of
+the server. Every private packet must also have a TSIG record;
+the DNS server uses this TSIG record to allow access to its data.
+
+When setting up a long-lived query over TCP (with or without TLS)
+TCP's standard three-way handshake makes the full four-packet LLQ setup
+exchange described in <http://files.dns-sd.org/draft-sekar-dns-llq.txt>
+unnecessary. Instead, when connecting over TCP, the client simply sends
+a setup message and expects to receive ACK + Answers. The setup message
+sent is formatted as described in the LLQ document, however there is
+an additional TSIG' resource record added to the end of it. The TSIG
+resource records looks and acts exactly as it does in a secure update.
+So when the server receives an LLQ (or a standard query), it looks to
+see if the zone that is being referenced is public or private. If it's
+private, then it makes sure that the client is authorized to query that
+zone (by using the TSIG signature) and returns the appropriate data.
+When a zone is configured as private, the server will do this type of
+authorization checking for every query except those queries that are
+looking for SOA and NS records.
+
+Implementation Issues
+
+dnsextd
+
+dnsextd has been modified to behave much like a DNS firewall. The "real"
+DNS server is configured to listen on non-standard ports on the loopback
+interface. dnsextd then listens on the standard DNS ports (TCP/UDP port
+53) and intercepts all DNS traffic. It is responsible for determining
+what zone a DNS request is associated with, determining whether the
+client is allowed access to that zone, and returning the appropriate
+information back to the caller. If the packet is allowed access, dnsextd
+forwards the request to the "real" nameserver, and returns the result to
+the caller.
+
+It was tempting to use BIND9's facility for configuring TSIG enabled
+queries while doing this work. However after proceeding down that path,
+enough subtle interaction problems were found that it was not practical
+to pursue this direction, so instead dnsextd does all TSIG processing
+for queries itself. It does continue to use BIND9 for processing TSIG
+enabled dynamic updates, though one minor downside with this is that
+there are two configuration files (named.conf or dnsextd.conf) that have
+the same secret key information. That seems redundant and error-prone,
+and moving all TSIG processing for both queries and updates into dnsextd
+would fix this.
+
+All private LLQ operations are TSIG-enabled and sent over a secure
+encrypted TLS channel. To accommodate service providers who don't want
+to have to keep open a large number of TLS connections to a large number
+of client machines, the server has the option of dropping the TLS
+connection after initial LLQ setup and sending subsequent events and
+refreshes using unencrypted UDP packets. This results in less load on
+the server, at the cost of slightly lower security (LLQs can only be set
+up by an authorized client, but once set up, subsequent change event
+packets sent over unencrypted UDP could be observed by an eavesdropper).
+A potential solution to this deficiency might be in using DTLS, which is
+a protocol based on TLS that is capable of securing datagram traffic.
+More investigation needs to be done to see if DTLS is suitable for
+private DNS.
+
+It was necessary to relax one of the checks that dnsextd performs during
+processing of an LLQ refresh. Prior to these changes, dnsextd would
+verify that the refresh request came from the same entity that setup the
+LLQ by comparing both the IP Address and port number of the request
+packet with the IP Address and port number of the setup packet. Because
+of the preceding issue, a refresh request might be sent over two
+different sockets. While their IP addresses would be the same, their
+port numbers could potentially differ. This check has been modified to
+only check that the IP addresses match.
+
+When setting up a semi-private LLQ (where the request and initial answer
+set is sent over TLS/TCP, but subsequent change events are sent over
+unencrypted UDP), dnsextd uses the port number of the client's TCP
+socket to determine the UDP event port number. While this eliminates the
+need to pass the UDP event port number in the LLQ setup request
+(obviating a potential data mismatch error), I think it does more harm
+than good, for three reasons:
+
+1) We are relying that all the routers out there implement the Port
+   Mapping Protocol spec correctly.
+
+2) Upon setup every LLQ must NAT map two ports. Upon tear down every LLQ
+   must tear down two NAT mappings.
+
+3) Every LLQ opens up two sockets (TCP and UDP), rather than just the
+   one TCP socket.
+
+All of this just to avoid sending two bytes in the LLQ setup packet
+doesn't seem logical. The approach also necessitates creating an
+additional UDP socket for every private LLQ, port mapping both the TCP
+socket as well as the UDP socket, and moderately increasing the
+complexity and efficiency of the code. Because of this we plan to allow
+the LLQ setup packet to specify a different UDP port for change event
+packets. This will allow mDNSResponder to receive all UDP change event
+packets on a single UDP port, instead of a different one for each LLQ.
+
+Currently, dnsextd is buggy on multi-homed hosts. If it receives a
+packet on interface 2, it will reply on interface 1 causing an error in
+the client program.
+
+dnsextd doesn't fully process all of its option parameters.
+Specifically, it doesn't process the keywords: "listen-on",
+"nameserver", "private", and "llq". It defaults to expecting the "real"
+nameserver to be listening on 127.0.0.1:5030.
+
+
+mDNSResponder
+
+Currently, mDNSResponder attempts to issue private queries for all
+queries that initially result in an NXDOMAIN error. This behavior might
+be modified in future versions, however it seems patently incorrect to
+do this for reverse name lookups. The code that attempts to get the zone
+data associated with the name will never find the zone for a reverse
+name lookup, and so will issue a number of wasteful DNS queries.
+
+mDNSResponder doesn't handle SERV_FULL or STATIC return codes after
+setting up an LLQ over TCP. This isn't a terrible problem right now,
+because dnsextd doesn't ever return them, but this should be fixed so
+that mDNSResponder will work when talking to other servers that do
+return these error codes.
+
+
+Configuration:
+
+Sample named.conf:
+
+//
+// Include keys file
+//
+include "/etc/rndc.key";
+// Declares control channels to be used by the rndc utility.
+//
+// It is recommended that 127.0.0.1 be the only address used.
+// This also allows non-privileged users on the local host to manage
+// your name server.
+
+//
+// Default controls
+//
+controls
+	{
+	inet 127.0.0.1 port 54 allow { any; } keys { "rndc-key"; };
+	};
+
+options
+	{
+	directory "/var/named";
+	/*
+	 * If there is a firewall between you and nameservers you want
+	 * to talk to, you might need to uncomment the query-source
+	 * directive below. Previous versions of BIND always asked
+	 * questions using port 53, but BIND 8.1 uses an unprivileged
+	 * port by default.
+	 */
+	
+	forwarders
+			{
+			65.23.128.2;
+			65.23.128.3;
+			};
+	
+	listen-on port 5030 { 127.0.0.1; };
+	recursion true;
+	};
+
+// 
+// a caching only nameserver config
+// 
+zone "." IN
+	{
+	type hint;
+	file "named.ca";
+	};
+
+zone "localhost" IN
+	{
+	type master;
+	file "localhost.zone";
+	allow-update { none; };
+	};
+
+zone "0.0.127.in-addr.arpa" IN
+	{
+	type master;
+	file "named.local";
+	allow-update { none; };
+	};
+
+zone "hungrywolf.org." in
+	{
+	type master;
+	file "db.hungrywolf.org";
+	allow-update { key hungrywolf.org.; };
+	};
+
+zone "157.23.65.in-addr.arpa" IN
+	{
+	file "db.65.23.157";
+	type master;
+	};
+
+zone "100.255.17.in-addr.arpa" IN
+	{
+	file "db.17.255.100";
+	type master;
+	};
+
+zone "66.6.24.in-addr.arpa" IN
+	{
+	file "db.24.6.66";
+	type master;
+	};
+
+key hungrywolf.org.
+	{
+	algorithm hmac-md5;
+	secret "c8LWr16K6ju6KMO5zT6Tyg==";
+	};
+
+logging
+	{
+	category default { _default_log; };
+
+	channel _default_log
+		{
+		file "/Library/Logs/named.log";
+		severity info;
+		print-time yes;
+		};
+	};
+
+
+Sample dnsextd.conf:
+
+options { };
+
+key "hungrywolf.org."
+	{
+	secret "c8LWr16K6ju6KMO5zT6Tyg==";
+	};
+
+zone "hungrywolf.org."
+	{
+	type private;
+	allow-query { key hungrywolf.org.; };
+	};
diff --git a/mdnsresponder/README.txt b/mdnsresponder/README.txt
new file mode 100644
index 0000000..86b041e
--- /dev/null
+++ b/mdnsresponder/README.txt
@@ -0,0 +1,166 @@
+What is mDNSResponder?
+----------------------
+
+The mDNSResponder project is a component of Bonjour,
+Apple's ease-of-use IP networking initiative:
+<http://developer.apple.com/bonjour/>
+
+Apple's Bonjour software derives from the ongoing standardization
+work of the IETF Zero Configuration Networking Working Group:
+<http://zeroconf.org/>
+
+The Zeroconf Working Group has identified three requirements for Zero
+Configuration Networking:
+1. An IP address (even when there is no DHCP server to assign one)
+2. Name-to-address translation (even when there is no DNS server)
+3. Discovery of Services on the network (again, without infrastucture)
+
+Requirement 1 is met by self-assigned link-local addresses, as
+described in "Dynamic Configuration of IPv4 Link-Local Addresses"
+<http://files.zeroconf.org/draft-ietf-zeroconf-ipv4-linklocal.txt>
+
+Requirement 2 is met by sending DNS-like queries via Multicast (mDNS).
+
+Requirement 3 is met by DNS Service Dicsovery (DNS-SD).
+
+Self-assigned link-local address capability has been available since
+1998, when it first appeared in Windows '98 and in Mac OS 8.5.
+Implementations for other platforms also exist.
+
+The mDNSResponder project allows us to meet requirements 2 and 3.
+It provides the ability for the user to identify hosts using names
+instead of dotted-decimal IP addresses, even if the user doesn't have a
+conventional DNS server set up. It also provides the ability for the
+user to discover what services are being advertised on the network,
+without having to know about them in advance, or configure the machines.
+
+The name "mDNS" was chosen because this protocol is designed to be,
+as much as possible, similar to conventional DNS. The main difference is
+that queries are sent via multicast to all local hosts, instead of via
+unicast to a specific known server. Every host on the local link runs an
+mDNSResponder which is constantly listening for those multicast queries,
+and if the mDNSResponder receives a query for which it knows the answer,
+then it responds. The mDNS protocol uses the same packet format as
+unicast DNS, and the same name structure, and the same DNS record types.
+The main difference is that queries are sent to a different UDP port
+(5353 instead of 53) and they are sent via multicast to address
+224.0.0.251. Another important difference is that all "mDNS" names
+end in ".local." When a user types "yourcomputer.local." into their Web
+browser, the presence of ".local." on the end of the name tells the host
+OS that the name should be looked up using local multicast instead of by
+sending that name to the worldwide DNS service for resolution. This
+helps reduce potential user confusion about whether a particular name
+is globally unique (e.g. "www.apple.com.") or whether that name has only
+local significance (e.g. "yourcomputer.local.").
+
+
+About the mDNSResponder Code
+----------------------------
+
+Because Apple benefits more from widespread adoption of Bonjour than
+it would benefit from keeping Bonjour proprietary, Apple is making
+this code open so that other developers can use it too.
+
+Because Apple recognises that networks are hetrogenous environments
+where devices run many different kinds of OS, this code has been made
+as portable as possible.
+
+A typical mDNS program contains three components:
+
+    +------------------+
+    |   Application    |
+    +------------------+
+    |    mDNS Core     |
+    +------------------+
+    | Platform Support |
+    +------------------+
+
+The "mDNS Core" layer is absolutely identical for all applications and
+all Operating Systems.
+
+The "Platform Support" layer provides the necessary supporting routines
+that are specific to each platform -- what routine do you call to send
+a UDP packet, what routine do you call to join multicast group, etc.
+
+The "Application" layer does whatever that particular application wants
+to do. It calls routines provided by the "mDNS Core" layer to perform
+the functions it needs --
+ * advertise services,
+ * browse for named instances of a particular type of service
+ * resolve a named instance to a specific IP address and port number,
+ * etc.
+The "mDNS Core" layer in turn calls through to the "Platform Support"
+layer to send and receive the multicast UDP packets to do the actual work.
+
+Apple currently provides "Platform Support" layers for Mac OS 9, Mac OS X,
+Microsoft Windows, VxWorks, and for POSIX platforms like Linux, Solaris,
+FreeBSD, etc.
+
+Note: Developers writing applications for OS X do not need to incorporate
+this code into their applications, since OS X provides a system service to
+handle this for them. If every application developer were to link-in the
+mDNSResponder code into their application, then we would end up with a
+situation like the picture below:
+
+  +------------------+    +------------------+    +------------------+
+  |   Application 1  |    |   Application 2  |    |   Application 3  |
+  +------------------+    +------------------+    +------------------+
+  |    mDNS Core     |    |    mDNS Core     |    |    mDNS Core     |
+  +------------------+    +------------------+    +------------------+
+  | Platform Support |    | Platform Support |    | Platform Support |
+  +------------------+    +------------------+    +------------------+
+
+This would not be very efficient. Each separate application would be sending
+their own separate multicast UDP packets and maintaining their own list of
+answers. Because of this, OS X provides a common system service which client
+software should access through the "/usr/include/dns_sd.h" APIs.
+
+The situation on OS X looks more like the picture below:
+
+                                   -------------------
+                                  /                   \
+  +---------+    +------------------+    +---------+   \  +---------+
+  |  App 1  |<-->|    daemon.c      |<-->|  App 2  |    ->|  App 3  |
+  +---------+    +------------------+    +---------+      +---------+
+                 |    mDNS Core     |
+                 +------------------+
+                 | Platform Support |
+                 +------------------+
+
+Applications on OS X make calls to the single mDNSResponder daemon
+which implements the mDNS and DNS-SD protocols. 
+
+Vendors of products such as printers, which are closed environments not
+expecting to be running third-party application software, can reasonably
+implement a single monolithic mDNSResponder to advertise all the
+services of that device. Vendors of open systems which run third-party
+application software should implement a system service such as the one
+provided by the OS X mDNSResponder daemon, and application software on
+that platform should, where possible, make use of that system service
+instead of embedding their own mDNSResponder.
+
+See ReadMe.txt in the mDNSPosix directory for specific details of
+building an mDNSResponder on a POSIX Operating System.
+
+
+Compiling on Older C Compilers
+------------------------------
+
+We go to some lengths to make the code portable, but //-style comments
+are one of the modern conveniences we can't live without.
+
+If your C compiler doesn't understand these comments, you can transform
+them into classical K&R /* style */ comments with a quick GREP
+search-and-replace pattern.
+
+In BBEdit on the Mac:
+1. Open the "Find" dialog window and make sure "Use Grep" is selected
+2. Search For  : ([^:])//(.*)
+3. Replace With: \1/*\2 */
+4. Drag your mDNSResponder source code folder to the Multi-File search pane
+5. Click "Replace All"
+
+For the more command-line oriented, cd into your mDNSResponder source code
+directory and execute the following command (all one line):
+
+find mDNSResponder \( -name \*.c\* -or -name \*.h \) -exec sed -i .orig -e 's,^//\(.*\),/*\1 */,' -e '/\/\*/\!s,\([^:]\)//\(.*\),\1/*\2 */,' {} \;
diff --git a/mdnsresponder/README.version b/mdnsresponder/README.version
new file mode 100644
index 0000000..f1fc452
--- /dev/null
+++ b/mdnsresponder/README.version
@@ -0,0 +1,3 @@
+URL: http://opensource.apple.com/tarballs/mDNSResponder/mDNSResponder-320.10.80.tar.gz
+Version: 320.10.80
+BugComponent: 31808
diff --git a/mdnsresponder/android/caseMapping/Iphlpapi.h b/mdnsresponder/android/caseMapping/Iphlpapi.h
new file mode 100644
index 0000000..4d08efd
--- /dev/null
+++ b/mdnsresponder/android/caseMapping/Iphlpapi.h
@@ -0,0 +1 @@
+#include <iphlpapi.h>
diff --git a/mdnsresponder/android/caseMapping/README b/mdnsresponder/android/caseMapping/README
new file mode 100644
index 0000000..951082f
--- /dev/null
+++ b/mdnsresponder/android/caseMapping/README
@@ -0,0 +1,3 @@
+Windows filesystems, and consequently Windows C include files, are
+case-insensitive. This folder provides alternately-cased versions of header
+files for cases where that becomes a problem.
diff --git a/mdnsresponder/android/caseMapping/Resource.h b/mdnsresponder/android/caseMapping/Resource.h
new file mode 100644
index 0000000..f4649ed
--- /dev/null
+++ b/mdnsresponder/android/caseMapping/Resource.h
@@ -0,0 +1 @@
+#include <resource.h>
diff --git a/mdnsresponder/android/caseMapping/Ws2tcpip.h b/mdnsresponder/android/caseMapping/Ws2tcpip.h
new file mode 100644
index 0000000..8fc8783
--- /dev/null
+++ b/mdnsresponder/android/caseMapping/Ws2tcpip.h
@@ -0,0 +1 @@
+#include <ws2tcpip.h>
diff --git a/mdnsresponder/android/caseMapping/ipExport.h b/mdnsresponder/android/caseMapping/ipExport.h
new file mode 100644
index 0000000..76ed79f
--- /dev/null
+++ b/mdnsresponder/android/caseMapping/ipExport.h
@@ -0,0 +1 @@
+#include <ipexport.h>
diff --git a/mdnsresponder/android/windows_firewall_dummy.c b/mdnsresponder/android/windows_firewall_dummy.c
new file mode 100644
index 0000000..f4d25ce
--- /dev/null
+++ b/mdnsresponder/android/windows_firewall_dummy.c
@@ -0,0 +1,10 @@
+int mDNSIsFileAndPrintSharingEnabled(int *ignored) {
+  (void)ignored;
+  return 0;
+}
+
+int mDNSAddToFirewall(const char* ignored, const char* also_ignored) {
+  (void)ignored;
+  (void)also_ignored;
+  return 0;
+}
diff --git a/mdnsresponder/mDNSCore/DNSCommon.c b/mdnsresponder/mDNSCore/DNSCommon.c
new file mode 100644
index 0000000..b75c128
--- /dev/null
+++ b/mdnsresponder/mDNSCore/DNSCommon.c
@@ -0,0 +1,3153 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+// Set mDNS_InstantiateInlines to tell mDNSEmbeddedAPI.h to instantiate inline functions, if necessary
+#define mDNS_InstantiateInlines 1
+#include "DNSCommon.h"
+
+// Disable certain benign warnings with Microsoft compilers
+#if (defined(_MSC_VER))
+	// Disable "conditional expression is constant" warning for debug macros.
+	// Otherwise, this generates warnings for the perfectly natural construct "while(1)"
+	// If someone knows a variant way of writing "while(1)" that doesn't generate warning messages, please let us know
+	#pragma warning(disable:4127)
+	// Disable "array is too small to include a terminating null character" warning
+	// -- domain labels have an initial length byte, not a terminating null character
+	#pragma warning(disable:4295)
+#endif
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark - Program Constants
+#endif
+
+mDNSexport const mDNSInterfaceID mDNSInterface_Any       = 0;
+mDNSexport const mDNSInterfaceID mDNSInterfaceMark       = (mDNSInterfaceID)-1;
+mDNSexport const mDNSInterfaceID mDNSInterface_LocalOnly = (mDNSInterfaceID)-2;
+mDNSexport const mDNSInterfaceID mDNSInterface_Unicast   = (mDNSInterfaceID)-3;
+mDNSexport const mDNSInterfaceID mDNSInterface_P2P       = (mDNSInterfaceID)-4;
+
+// Note: Microsoft's proposed "Link Local Multicast Name Resolution Protocol" (LLMNR) is essentially a limited version of
+// Multicast DNS, using the same packet formats, naming syntax, and record types as Multicast DNS, but on a different UDP
+// port and multicast address, which means it won't interoperate with the existing installed base of Multicast DNS responders.
+// LLMNR uses IPv4 multicast address 224.0.0.252, IPv6 multicast address FF02::0001:0003, and UDP port 5355.
+// Uncomment the appropriate lines below to build a special Multicast DNS responder for testing interoperability
+// with Microsoft's LLMNR client code.
+
+#define   DiscardPortAsNumber               9
+#define   SSHPortAsNumber                  22
+#define   UnicastDNSPortAsNumber           53
+#define   SSDPPortAsNumber               1900
+#define   IPSECPortAsNumber              4500
+#define   NSIPCPortAsNumber              5030		// Port used for dnsextd to talk to local nameserver bound to loopback
+#define   NATPMPAnnouncementPortAsNumber 5350
+#define   NATPMPPortAsNumber             5351
+#define   DNSEXTPortAsNumber             5352		// Port used for end-to-end DNS operations like LLQ, Updates with Leases, etc.
+#define   MulticastDNSPortAsNumber       5353
+#define   LoopbackIPCPortAsNumber        5354
+//#define MulticastDNSPortAsNumber       5355		// LLMNR
+#define   PrivateDNSPortAsNumber         5533
+
+mDNSexport const mDNSIPPort DiscardPort            = { { DiscardPortAsNumber            >> 8, DiscardPortAsNumber            & 0xFF } };
+mDNSexport const mDNSIPPort SSHPort                = { { SSHPortAsNumber                >> 8, SSHPortAsNumber                & 0xFF } };
+mDNSexport const mDNSIPPort UnicastDNSPort         = { { UnicastDNSPortAsNumber         >> 8, UnicastDNSPortAsNumber         & 0xFF } };
+mDNSexport const mDNSIPPort SSDPPort               = { { SSDPPortAsNumber               >> 8, SSDPPortAsNumber               & 0xFF } };
+mDNSexport const mDNSIPPort IPSECPort              = { { IPSECPortAsNumber              >> 8, IPSECPortAsNumber              & 0xFF } };
+mDNSexport const mDNSIPPort NSIPCPort              = { { NSIPCPortAsNumber              >> 8, NSIPCPortAsNumber              & 0xFF } };
+mDNSexport const mDNSIPPort NATPMPAnnouncementPort = { { NATPMPAnnouncementPortAsNumber >> 8, NATPMPAnnouncementPortAsNumber & 0xFF } };
+mDNSexport const mDNSIPPort NATPMPPort             = { { NATPMPPortAsNumber             >> 8, NATPMPPortAsNumber             & 0xFF } };
+mDNSexport const mDNSIPPort DNSEXTPort             = { { DNSEXTPortAsNumber             >> 8, DNSEXTPortAsNumber             & 0xFF } };
+mDNSexport const mDNSIPPort MulticastDNSPort       = { { MulticastDNSPortAsNumber       >> 8, MulticastDNSPortAsNumber       & 0xFF } };
+mDNSexport const mDNSIPPort LoopbackIPCPort        = { { LoopbackIPCPortAsNumber        >> 8, LoopbackIPCPortAsNumber        & 0xFF } };
+mDNSexport const mDNSIPPort PrivateDNSPort         = { { PrivateDNSPortAsNumber         >> 8, PrivateDNSPortAsNumber         & 0xFF } };
+
+mDNSexport const OwnerOptData    zeroOwner         = { 0, 0, { { 0 } }, { { 0 } }, { { 0 } } };
+
+mDNSexport const mDNSIPPort      zeroIPPort        = { { 0 } };
+mDNSexport const mDNSv4Addr      zerov4Addr        = { { 0 } };
+mDNSexport const mDNSv6Addr      zerov6Addr        = { { 0 } };
+mDNSexport const mDNSEthAddr     zeroEthAddr       = { { 0 } };
+mDNSexport const mDNSv4Addr      onesIPv4Addr      = { { 255, 255, 255, 255 } };
+mDNSexport const mDNSv6Addr      onesIPv6Addr      = { { 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255 } };
+mDNSexport const mDNSEthAddr     onesEthAddr       = { { 255, 255, 255, 255, 255, 255 } };
+mDNSexport const mDNSAddr        zeroAddr          = { mDNSAddrType_None, {{{ 0 }}} };
+
+mDNSexport const mDNSv4Addr  AllDNSAdminGroup   = { { 239, 255, 255, 251 } };
+mDNSexport const mDNSv4Addr  AllHosts_v4        = { { 224,   0,   0,   1 } }; // For NAT-PMP Annoucements
+mDNSexport const mDNSv6Addr  AllHosts_v6        = { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x01 } };
+mDNSexport const mDNSv6Addr  NDP_prefix         = { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x01, 0xFF,0x00,0x00,0xFB } }; // FF02:0:0:0:0:1:FF00::/104
+mDNSexport const mDNSEthAddr AllHosts_v6_Eth    = { { 0x33, 0x33, 0x00, 0x00, 0x00, 0x01 } };
+mDNSexport const mDNSAddr    AllDNSLinkGroup_v4 = { mDNSAddrType_IPv4, { { { 224,   0,   0, 251 } } } };
+//mDNSexport const mDNSAddr  AllDNSLinkGroup_v4 = { mDNSAddrType_IPv4, { { { 224,   0,   0, 252 } } } }; // LLMNR
+mDNSexport const mDNSAddr    AllDNSLinkGroup_v6 = { mDNSAddrType_IPv6, { { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0xFB } } } };
+//mDNSexport const mDNSAddr  AllDNSLinkGroup_v6 = { mDNSAddrType_IPv6, { { { 0xFF,0x02,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x00,0x00,0x00, 0x00,0x01,0x00,0x03 } } } }; // LLMNR
+
+mDNSexport const mDNSOpaque16 zeroID          = { { 0, 0 } };
+mDNSexport const mDNSOpaque16 onesID          = { { 255, 255 } };
+mDNSexport const mDNSOpaque16 QueryFlags      = { { kDNSFlag0_QR_Query    | kDNSFlag0_OP_StdQuery,                0 } };
+mDNSexport const mDNSOpaque16 uQueryFlags     = { { kDNSFlag0_QR_Query    | kDNSFlag0_OP_StdQuery | kDNSFlag0_RD, 0 } };
+mDNSexport const mDNSOpaque16 ResponseFlags   = { { kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery | kDNSFlag0_AA, 0 } };
+mDNSexport const mDNSOpaque16 UpdateReqFlags  = { { kDNSFlag0_QR_Query    | kDNSFlag0_OP_Update,                  0 } };
+mDNSexport const mDNSOpaque16 UpdateRespFlags = { { kDNSFlag0_QR_Response | kDNSFlag0_OP_Update,                  0 } };
+
+mDNSexport const mDNSOpaque64 zeroOpaque64    = { { 0 } };
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - General Utility Functions
+#endif
+
+// return true for RFC1918 private addresses
+mDNSexport mDNSBool mDNSv4AddrIsRFC1918(mDNSv4Addr *addr)
+	{
+	return ((addr->b[0] == 10) ||                                 // 10/8 prefix
+			(addr->b[0] == 172 && (addr->b[1] & 0xF0) == 16) ||   // 172.16/12
+			(addr->b[0] == 192 && addr->b[1] == 168));            // 192.168/16
+	}
+
+mDNSexport NetworkInterfaceInfo *GetFirstActiveInterface(NetworkInterfaceInfo *intf)
+	{
+	while (intf && !intf->InterfaceActive) intf = intf->next;
+	return(intf);
+	}
+
+mDNSexport mDNSInterfaceID GetNextActiveInterfaceID(const NetworkInterfaceInfo *intf)
+	{
+	const NetworkInterfaceInfo *next = GetFirstActiveInterface(intf->next);
+	if (next) return(next->InterfaceID); else return(mDNSNULL);
+	}
+
+mDNSexport mDNSu32 NumCacheRecordsForInterfaceID(const mDNS *const m, mDNSInterfaceID id)
+	{
+	mDNSu32 slot, used = 0;
+	CacheGroup *cg;
+	const CacheRecord *rr;
+	FORALL_CACHERECORDS(slot, cg, rr)
+		if (rr->resrec.InterfaceID == id) used++;
+	return(used);
+	}
+
+mDNSexport char *DNSTypeName(mDNSu16 rrtype)
+	{
+	switch (rrtype)
+		{
+		case kDNSType_A:    return("Addr");
+		case kDNSType_NS:   return("NS");
+		case kDNSType_CNAME:return("CNAME");
+		case kDNSType_SOA:  return("SOA");
+		case kDNSType_NULL: return("NULL");
+		case kDNSType_PTR:  return("PTR");
+		case kDNSType_HINFO:return("HINFO");
+		case kDNSType_TXT:  return("TXT");
+		case kDNSType_AAAA: return("AAAA");
+		case kDNSType_SRV:  return("SRV");
+		case kDNSType_OPT:  return("OPT");
+		case kDNSType_NSEC: return("NSEC");
+		case kDNSType_TSIG: return("TSIG");
+		case kDNSQType_ANY: return("ANY");
+		default:			{
+							static char buffer[16];
+							mDNS_snprintf(buffer, sizeof(buffer), "(%d)", rrtype);
+							return(buffer);
+							}
+		}
+	}
+
+// Note slight bug: this code uses the rdlength from the ResourceRecord object, to display
+// the rdata from the RDataBody object. Sometimes this could be the wrong length -- but as
+// long as this routine is only used for debugging messages, it probably isn't a big problem.
+mDNSexport char *GetRRDisplayString_rdb(const ResourceRecord *const rr, const RDataBody *const rd1, char *const buffer)
+	{
+	const RDataBody2 *const rd = (RDataBody2 *)rd1;
+	#define RemSpc (MaxMsg-1-length)
+	char *ptr = buffer;
+	mDNSu32 length = mDNS_snprintf(buffer, MaxMsg-1, "%4d %##s %s ", rr->rdlength, rr->name->c, DNSTypeName(rr->rrtype));
+	if (rr->RecordType == kDNSRecordTypePacketNegative) return(buffer);
+	if (!rr->rdlength) { mDNS_snprintf(buffer+length, RemSpc, "<< ZERO RDATA LENGTH >>"); return(buffer); }
+	
+	switch (rr->rrtype)
+		{
+		case kDNSType_A:	mDNS_snprintf(buffer+length, RemSpc, "%.4a", &rd->ipv4);          break;
+
+		case kDNSType_NS:	// Same as PTR
+		case kDNSType_CNAME:// Same as PTR
+		case kDNSType_PTR:	mDNS_snprintf(buffer+length, RemSpc, "%##s", rd->name.c);       break;
+
+		case kDNSType_SOA:  mDNS_snprintf(buffer+length, RemSpc, "%##s %##s %d %d %d %d %d",
+								rd->soa.mname.c, rd->soa.rname.c,
+								rd->soa.serial, rd->soa.refresh, rd->soa.retry, rd->soa.expire, rd->soa.min);
+							break;
+
+		case kDNSType_HINFO:// Display this the same as TXT (show all constituent strings)
+		case kDNSType_TXT:  {
+							const mDNSu8 *t = rd->txt.c;
+							while (t < rd->txt.c + rr->rdlength)
+								{
+								length += mDNS_snprintf(buffer+length, RemSpc, "%s%#s", t > rd->txt.c ? "¦" : "", t);
+								t += 1 + t[0];
+								}
+							} break;
+
+		case kDNSType_AAAA:	mDNS_snprintf(buffer+length, RemSpc, "%.16a", &rd->ipv6);       break;
+		case kDNSType_SRV:	mDNS_snprintf(buffer+length, RemSpc, "%u %u %u %##s",
+								rd->srv.priority, rd->srv.weight, mDNSVal16(rd->srv.port), rd->srv.target.c); break;
+
+		case kDNSType_OPT:  {
+							const rdataOPT *opt;
+							const rdataOPT *const end = (const rdataOPT *)&rd->data[rr->rdlength];
+							length += mDNS_snprintf(buffer+length, RemSpc, "Max %d", rr->rrclass);
+							for (opt = &rd->opt[0]; opt < end; opt++)
+								{
+								switch(opt->opt)
+									{
+									case kDNSOpt_LLQ:
+										length += mDNS_snprintf(buffer+length, RemSpc, " Vers %d",     opt->u.llq.vers);
+										length += mDNS_snprintf(buffer+length, RemSpc, " Op %d",       opt->u.llq.llqOp);
+										length += mDNS_snprintf(buffer+length, RemSpc, " Err/Port %d", opt->u.llq.err);
+										length += mDNS_snprintf(buffer+length, RemSpc, " ID %08X%08X", opt->u.llq.id.l[0], opt->u.llq.id.l[1]);
+										length += mDNS_snprintf(buffer+length, RemSpc, " Lease %d",    opt->u.llq.llqlease);
+										break;
+									case kDNSOpt_Lease:
+										length += mDNS_snprintf(buffer+length, RemSpc, " Lease %d",    opt->u.updatelease);
+										break;
+									case kDNSOpt_Owner:
+										length += mDNS_snprintf(buffer+length, RemSpc, " Vers %d",     opt->u.owner.vers);
+										length += mDNS_snprintf(buffer+length, RemSpc, " Seq %3d", (mDNSu8)opt->u.owner.seq);	// Display as unsigned
+										length += mDNS_snprintf(buffer+length, RemSpc, " MAC %.6a",    opt->u.owner.HMAC.b);
+										if (opt->optlen >= DNSOpt_OwnerData_ID_Wake_Space-4)
+											{
+											length += mDNS_snprintf(buffer+length, RemSpc, " I-MAC %.6a", opt->u.owner.IMAC.b);
+											if (opt->optlen > DNSOpt_OwnerData_ID_Wake_Space-4)
+												length += mDNS_snprintf(buffer+length, RemSpc, " Password %.6a", opt->u.owner.password.b);
+											}
+										break;
+									default:
+										length += mDNS_snprintf(buffer+length, RemSpc, " Unknown %d",  opt->opt);
+										break;
+									}
+								}
+							}
+							break;
+
+		case kDNSType_NSEC: {
+							mDNSu16 i;
+							for (i=0; i<255; i++)
+								if (rd->nsec.bitmap[i>>3] & (128 >> (i&7)))
+									length += mDNS_snprintf(buffer+length, RemSpc, "%s ", DNSTypeName(i));
+							}
+							break;
+
+		default:			mDNS_snprintf(buffer+length, RemSpc, "RDLen %d: %s", rr->rdlength, rd->data);
+							// Really should scan buffer to check if text is valid UTF-8 and only replace with dots if not
+							for (ptr = buffer; *ptr; ptr++) if (*ptr < ' ') *ptr = '.';
+							break;
+		}
+	return(buffer);
+	}
+
+// See comments in mDNSEmbeddedAPI.h
+#if _PLATFORM_HAS_STRONG_PRNG_
+#define mDNSRandomNumber mDNSPlatformRandomNumber
+#else
+mDNSlocal mDNSu32 mDNSRandomFromSeed(mDNSu32 seed)
+	{
+	return seed * 21 + 1;
+	}
+
+mDNSlocal mDNSu32 mDNSMixRandomSeed(mDNSu32 seed, mDNSu8 iteration)
+	{
+	return iteration ? mDNSMixRandomSeed(mDNSRandomFromSeed(seed), --iteration) : seed;
+	}
+
+mDNSlocal mDNSu32 mDNSRandomNumber()
+	{
+	static mDNSBool seeded = mDNSfalse;
+	static mDNSu32 seed = 0;
+	if (!seeded)
+		{
+		seed = mDNSMixRandomSeed(mDNSPlatformRandomSeed(), 100);
+		seeded = mDNStrue;
+		}
+	return (seed = mDNSRandomFromSeed(seed));
+	}
+#endif // ! _PLATFORM_HAS_STRONG_PRNG_
+	
+mDNSexport mDNSu32 mDNSRandom(mDNSu32 max)		// Returns pseudo-random result from zero to max inclusive
+	{
+	mDNSu32 ret = 0;
+	mDNSu32 mask = 1;
+
+	while (mask < max) mask = (mask << 1) | 1;
+
+	do ret = mDNSRandomNumber() & mask;
+	while (ret > max);
+
+	return ret;
+	}
+
+mDNSexport mDNSBool mDNSSameAddress(const mDNSAddr *ip1, const mDNSAddr *ip2)
+	{
+	if (ip1->type == ip2->type)
+		{
+		switch (ip1->type)
+			{
+			case mDNSAddrType_None : return(mDNStrue); // Empty addresses have no data and are therefore always equal
+			case mDNSAddrType_IPv4 : return(mDNSBool)(mDNSSameIPv4Address(ip1->ip.v4, ip2->ip.v4));
+			case mDNSAddrType_IPv6 : return(mDNSBool)(mDNSSameIPv6Address(ip1->ip.v6, ip2->ip.v6));
+			}
+		}
+	return(mDNSfalse);
+	}
+
+mDNSexport mDNSBool mDNSAddrIsDNSMulticast(const mDNSAddr *ip)
+	{
+	switch(ip->type)
+		{
+		case mDNSAddrType_IPv4: return(mDNSBool)(mDNSSameIPv4Address(ip->ip.v4, AllDNSLinkGroup_v4.ip.v4));
+		case mDNSAddrType_IPv6: return(mDNSBool)(mDNSSameIPv6Address(ip->ip.v6, AllDNSLinkGroup_v6.ip.v6));
+		default: return(mDNSfalse);
+		}
+	}
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - Domain Name Utility Functions
+#endif
+
+mDNSexport mDNSBool SameDomainLabel(const mDNSu8 *a, const mDNSu8 *b)
+	{
+	int i;
+	const int len = *a++;
+
+	if (len > MAX_DOMAIN_LABEL)
+		{ debugf("Malformed label (too long)"); return(mDNSfalse); }
+
+	if (len != *b++) return(mDNSfalse);
+	for (i=0; i<len; i++)
+		{
+		mDNSu8 ac = *a++;
+		mDNSu8 bc = *b++;
+		if (mDNSIsUpperCase(ac)) ac += 'a' - 'A';
+		if (mDNSIsUpperCase(bc)) bc += 'a' - 'A';
+		if (ac != bc) return(mDNSfalse);
+		}
+	return(mDNStrue);
+	}
+
+mDNSexport mDNSBool SameDomainName(const domainname *const d1, const domainname *const d2)
+	{
+	const mDNSu8 *      a   = d1->c;
+	const mDNSu8 *      b   = d2->c;
+	const mDNSu8 *const max = d1->c + MAX_DOMAIN_NAME;			// Maximum that's valid
+
+	while (*a || *b)
+		{
+		if (a + 1 + *a >= max)
+			{ debugf("Malformed domain name (more than 256 characters)"); return(mDNSfalse); }
+		if (!SameDomainLabel(a, b)) return(mDNSfalse);
+		a += 1 + *a;
+		b += 1 + *b;
+		}
+
+	return(mDNStrue);
+	}
+
+mDNSexport mDNSBool SameDomainNameCS(const domainname *const d1, const domainname *const d2)
+	{
+	mDNSu16 l1 = DomainNameLength(d1);
+	mDNSu16 l2 = DomainNameLength(d2);
+	return(l1 <= MAX_DOMAIN_NAME && l1 == l2 && mDNSPlatformMemSame(d1, d2, l1));
+	}
+
+mDNSexport mDNSBool IsLocalDomain(const domainname *d)
+	{
+	// Domains that are defined to be resolved via link-local multicast are:
+	// local., 254.169.in-addr.arpa., and {8,9,A,B}.E.F.ip6.arpa.
+	static const domainname *nL = (const domainname*)"\x5" "local";
+	static const domainname *nR = (const domainname*)"\x3" "254" "\x3" "169"         "\x7" "in-addr" "\x4" "arpa";
+	static const domainname *n8 = (const domainname*)"\x1" "8"   "\x1" "e" "\x1" "f" "\x3" "ip6"     "\x4" "arpa";
+	static const domainname *n9 = (const domainname*)"\x1" "9"   "\x1" "e" "\x1" "f" "\x3" "ip6"     "\x4" "arpa";
+	static const domainname *nA = (const domainname*)"\x1" "a"   "\x1" "e" "\x1" "f" "\x3" "ip6"     "\x4" "arpa";
+	static const domainname *nB = (const domainname*)"\x1" "b"   "\x1" "e" "\x1" "f" "\x3" "ip6"     "\x4" "arpa";
+
+	const domainname *d1, *d2, *d3, *d4, *d5;	// Top-level domain, second-level domain, etc.
+	d1 = d2 = d3 = d4 = d5 = mDNSNULL;
+	while (d->c[0])
+		{
+		d5 = d4; d4 = d3; d3 = d2; d2 = d1; d1 = d;
+		d = (const domainname*)(d->c + 1 + d->c[0]);
+		}
+
+	if (d1 && SameDomainName(d1, nL)) return(mDNStrue);
+	if (d4 && SameDomainName(d4, nR)) return(mDNStrue);
+	if (d5 && SameDomainName(d5, n8)) return(mDNStrue);
+	if (d5 && SameDomainName(d5, n9)) return(mDNStrue);
+	if (d5 && SameDomainName(d5, nA)) return(mDNStrue);
+	if (d5 && SameDomainName(d5, nB)) return(mDNStrue);
+	return(mDNSfalse);
+	}
+
+mDNSexport const mDNSu8 *LastLabel(const domainname *d)
+	{
+	const mDNSu8 *p = d->c;
+	while (d->c[0])
+		{
+		p = d->c;
+		d = (const domainname*)(d->c + 1 + d->c[0]);
+		}
+	return(p);
+	}
+
+// Returns length of a domain name INCLUDING the byte for the final null label
+// e.g. for the root label "." it returns one
+// For the FQDN "com." it returns 5 (length byte, three data bytes, final zero)
+// Legal results are 1 (just root label) to 256 (MAX_DOMAIN_NAME)
+// If the given domainname is invalid, result is 257 (MAX_DOMAIN_NAME+1)
+mDNSexport mDNSu16 DomainNameLengthLimit(const domainname *const name, const mDNSu8 *limit)
+	{
+	const mDNSu8 *src = name->c;
+	while (src < limit && *src <= MAX_DOMAIN_LABEL)
+		{
+		if (*src == 0) return((mDNSu16)(src - name->c + 1));
+		src += 1 + *src;
+		}
+	return(MAX_DOMAIN_NAME+1);
+	}
+
+// CompressedDomainNameLength returns the length of a domain name INCLUDING the byte
+// for the final null label, e.g. for the root label "." it returns one.
+// E.g. for the FQDN "foo.com." it returns 9
+// (length, three data bytes, length, three more data bytes, final zero).
+// In the case where a parent domain name is provided, and the given name is a child
+// of that parent, CompressedDomainNameLength returns the length of the prefix portion
+// of the child name, plus TWO bytes for the compression pointer.
+// E.g. for the name "foo.com." with parent "com.", it returns 6
+// (length, three data bytes, two-byte compression pointer).
+mDNSexport mDNSu16 CompressedDomainNameLength(const domainname *const name, const domainname *parent)
+	{
+	const mDNSu8 *src = name->c;
+	if (parent && parent->c[0] == 0) parent = mDNSNULL;
+	while (*src)
+		{
+		if (*src > MAX_DOMAIN_LABEL) return(MAX_DOMAIN_NAME+1);
+		if (parent && SameDomainName((const domainname *)src, parent)) return((mDNSu16)(src - name->c + 2));
+		src += 1 + *src;
+		if (src - name->c >= MAX_DOMAIN_NAME) return(MAX_DOMAIN_NAME+1);
+		}
+	return((mDNSu16)(src - name->c + 1));
+	}
+
+// CountLabels() returns number of labels in name, excluding final root label
+// (e.g. for "apple.com." CountLabels returns 2.)
+mDNSexport int CountLabels(const domainname *d)
+	{
+	int count = 0;
+	const mDNSu8 *ptr;
+	for (ptr = d->c; *ptr; ptr = ptr + ptr[0] + 1) count++;
+	return count;
+	}
+
+// SkipLeadingLabels skips over the first 'skip' labels in the domainname,
+// returning a pointer to the suffix with 'skip' labels removed.
+mDNSexport const domainname *SkipLeadingLabels(const domainname *d, int skip)
+	{
+	while (skip > 0 && d->c[0]) { d = (const domainname *)(d->c + 1 + d->c[0]); skip--; }
+	return(d);
+	}
+
+// AppendLiteralLabelString appends a single label to an existing (possibly empty) domainname.
+// The C string contains the label as-is, with no escaping, etc.
+// Any dots in the name are literal dots, not label separators
+// If successful, AppendLiteralLabelString returns a pointer to the next unused byte
+// in the domainname bufer (i.e. the next byte after the terminating zero).
+// If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 256 bytes)
+// AppendLiteralLabelString returns mDNSNULL.
+mDNSexport mDNSu8 *AppendLiteralLabelString(domainname *const name, const char *cstr)
+	{
+	mDNSu8       *      ptr  = name->c + DomainNameLength(name) - 1;	// Find end of current name
+	const mDNSu8 *const lim1 = name->c + MAX_DOMAIN_NAME - 1;			// Limit of how much we can add (not counting final zero)
+	const mDNSu8 *const lim2 = ptr + 1 + MAX_DOMAIN_LABEL;
+	const mDNSu8 *const lim  = (lim1 < lim2) ? lim1 : lim2;
+	mDNSu8       *lengthbyte = ptr++;									// Record where the length is going to go
+
+	while (*cstr && ptr < lim) *ptr++ = (mDNSu8)*cstr++;	// Copy the data
+	*lengthbyte = (mDNSu8)(ptr - lengthbyte - 1);			// Fill in the length byte
+	*ptr++ = 0;												// Put the null root label on the end
+	if (*cstr) return(mDNSNULL);							// Failure: We didn't successfully consume all input
+	else return(ptr);										// Success: return new value of ptr
+	}
+
+// AppendDNSNameString appends zero or more labels to an existing (possibly empty) domainname.
+// The C string is in conventional DNS syntax:
+// Textual labels, escaped as necessary using the usual DNS '\' notation, separated by dots.
+// If successful, AppendDNSNameString returns a pointer to the next unused byte
+// in the domainname bufer (i.e. the next byte after the terminating zero).
+// If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 256 bytes)
+// AppendDNSNameString returns mDNSNULL.
+mDNSexport mDNSu8 *AppendDNSNameString(domainname *const name, const char *cstring)
+	{
+	const char   *cstr      = cstring;
+	mDNSu8       *      ptr = name->c + DomainNameLength(name) - 1;	// Find end of current name
+	const mDNSu8 *const lim = name->c + MAX_DOMAIN_NAME - 1;		// Limit of how much we can add (not counting final zero)
+	while (*cstr && ptr < lim)										// While more characters, and space to put them...
+		{
+		mDNSu8 *lengthbyte = ptr++;									// Record where the length is going to go
+		if (*cstr == '.') { LogMsg("AppendDNSNameString: Illegal empty label in name \"%s\"", cstring); return(mDNSNULL); }
+		while (*cstr && *cstr != '.' && ptr < lim)					// While we have characters in the label...
+			{
+			mDNSu8 c = (mDNSu8)*cstr++;								// Read the character
+			if (c == '\\')											// If escape character, check next character
+				{
+				c = (mDNSu8)*cstr++;								// Assume we'll just take the next character
+				if (mDNSIsDigit(cstr[-1]) && mDNSIsDigit(cstr[0]) && mDNSIsDigit(cstr[1]))
+					{												// If three decimal digits,
+					int v0 = cstr[-1] - '0';						// then interpret as three-digit decimal
+					int v1 = cstr[ 0] - '0';
+					int v2 = cstr[ 1] - '0';
+					int val = v0 * 100 + v1 * 10 + v2;
+					if (val <= 255) { c = (mDNSu8)val; cstr += 2; }	// If valid three-digit decimal value, use it
+					}
+				}
+			*ptr++ = c;												// Write the character
+			}
+		if (*cstr) cstr++;											// Skip over the trailing dot (if present)
+		if (ptr - lengthbyte - 1 > MAX_DOMAIN_LABEL)				// If illegal label, abort
+			return(mDNSNULL);
+		*lengthbyte = (mDNSu8)(ptr - lengthbyte - 1);				// Fill in the length byte
+		}
+
+	*ptr++ = 0;														// Put the null root label on the end
+	if (*cstr) return(mDNSNULL);									// Failure: We didn't successfully consume all input
+	else return(ptr);												// Success: return new value of ptr
+	}
+
+// AppendDomainLabel appends a single label to a name.
+// If successful, AppendDomainLabel returns a pointer to the next unused byte
+// in the domainname bufer (i.e. the next byte after the terminating zero).
+// If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 256 bytes)
+// AppendDomainLabel returns mDNSNULL.
+mDNSexport mDNSu8 *AppendDomainLabel(domainname *const name, const domainlabel *const label)
+	{
+	int i;
+	mDNSu8 *ptr = name->c + DomainNameLength(name) - 1;
+
+	// Check label is legal
+	if (label->c[0] > MAX_DOMAIN_LABEL) return(mDNSNULL);
+
+	// Check that ptr + length byte + data bytes + final zero does not exceed our limit
+	if (ptr + 1 + label->c[0] + 1 > name->c + MAX_DOMAIN_NAME) return(mDNSNULL);
+
+	for (i=0; i<=label->c[0]; i++) *ptr++ = label->c[i];	// Copy the label data
+	*ptr++ = 0;								// Put the null root label on the end
+	return(ptr);
+	}
+
+mDNSexport mDNSu8 *AppendDomainName(domainname *const name, const domainname *const append)
+	{
+	mDNSu8       *      ptr = name->c + DomainNameLength(name) - 1;	// Find end of current name
+	const mDNSu8 *const lim = name->c + MAX_DOMAIN_NAME - 1;		// Limit of how much we can add (not counting final zero)
+	const mDNSu8 *      src = append->c;
+	while (src[0])
+		{
+		int i;
+		if (ptr + 1 + src[0] > lim) return(mDNSNULL);
+		for (i=0; i<=src[0]; i++) *ptr++ = src[i];
+		*ptr = 0;	// Put the null root label on the end
+		src += i;
+		}
+	return(ptr);
+	}
+
+// MakeDomainLabelFromLiteralString makes a single domain label from a single literal C string (with no escaping).
+// If successful, MakeDomainLabelFromLiteralString returns mDNStrue.
+// If unable to convert the whole string to a legal domain label (i.e. because length is more than 63 bytes) then
+// MakeDomainLabelFromLiteralString makes a legal domain label from the first 63 bytes of the string and returns mDNSfalse.
+// In some cases silently truncated oversized names to 63 bytes is acceptable, so the return result may be ignored.
+// In other cases silent truncation may not be acceptable, so in those cases the calling function needs to check the return result.
+mDNSexport mDNSBool MakeDomainLabelFromLiteralString(domainlabel *const label, const char *cstr)
+	{
+	mDNSu8       *      ptr   = label->c + 1;						// Where we're putting it
+	const mDNSu8 *const limit = label->c + 1 + MAX_DOMAIN_LABEL;	// The maximum we can put
+	while (*cstr && ptr < limit) *ptr++ = (mDNSu8)*cstr++;			// Copy the label
+	label->c[0] = (mDNSu8)(ptr - label->c - 1);						// Set the length byte
+	return(*cstr == 0);												// Return mDNStrue if we successfully consumed all input
+	}
+
+// MakeDomainNameFromDNSNameString makes a native DNS-format domainname from a C string.
+// The C string is in conventional DNS syntax:
+// Textual labels, escaped as necessary using the usual DNS '\' notation, separated by dots.
+// If successful, MakeDomainNameFromDNSNameString returns a pointer to the next unused byte
+// in the domainname bufer (i.e. the next byte after the terminating zero).
+// If unable to construct a legal domain name (i.e. label more than 63 bytes, or total more than 256 bytes)
+// MakeDomainNameFromDNSNameString returns mDNSNULL.
+mDNSexport mDNSu8 *MakeDomainNameFromDNSNameString(domainname *const name, const char *cstr)
+	{
+	name->c[0] = 0;									// Make an empty domain name
+	return(AppendDNSNameString(name, cstr));		// And then add this string to it
+	}
+
+mDNSexport char *ConvertDomainLabelToCString_withescape(const domainlabel *const label, char *ptr, char esc)
+	{
+	const mDNSu8 *      src = label->c;							// Domain label we're reading
+	const mDNSu8        len = *src++;							// Read length of this (non-null) label
+	const mDNSu8 *const end = src + len;						// Work out where the label ends
+	if (len > MAX_DOMAIN_LABEL) return(mDNSNULL);				// If illegal label, abort
+	while (src < end)											// While we have characters in the label
+		{
+		mDNSu8 c = *src++;
+		if (esc)
+			{
+			if (c == '.' || c == esc)							// If character is a dot or the escape character
+				*ptr++ = esc;									// Output escape character
+			else if (c <= ' ')									// If non-printing ascii,
+				{												// Output decimal escape sequence
+				*ptr++ = esc;
+				*ptr++ = (char)  ('0' + (c / 100)     );
+				*ptr++ = (char)  ('0' + (c /  10) % 10);
+				c      = (mDNSu8)('0' + (c      ) % 10);
+				}
+			}
+		*ptr++ = (char)c;										// Copy the character
+		}
+	*ptr = 0;													// Null-terminate the string
+	return(ptr);												// and return
+	}
+
+// Note: To guarantee that there will be no possible overrun, cstr must be at least MAX_ESCAPED_DOMAIN_NAME (1009 bytes)
+mDNSexport char *ConvertDomainNameToCString_withescape(const domainname *const name, char *ptr, char esc)
+	{
+	const mDNSu8 *src         = name->c;							// Domain name we're reading
+	const mDNSu8 *const max   = name->c + MAX_DOMAIN_NAME;			// Maximum that's valid
+
+	if (*src == 0) *ptr++ = '.';									// Special case: For root, just write a dot
+
+	while (*src)													// While more characters in the domain name
+		{
+		if (src + 1 + *src >= max) return(mDNSNULL);
+		ptr = ConvertDomainLabelToCString_withescape((const domainlabel *)src, ptr, esc);
+		if (!ptr) return(mDNSNULL);
+		src += 1 + *src;
+		*ptr++ = '.';												// Write the dot after the label
+		}
+
+	*ptr++ = 0;														// Null-terminate the string
+	return(ptr);													// and return
+	}
+
+// RFC 1034 rules:
+// Host names must start with a letter, end with a letter or digit,
+// and have as interior characters only letters, digits, and hyphen.
+// This was subsequently modified in RFC 1123 to allow the first character to be either a letter or a digit
+
+mDNSexport void ConvertUTF8PstringToRFC1034HostLabel(const mDNSu8 UTF8Name[], domainlabel *const hostlabel)
+	{
+	const mDNSu8 *      src  = &UTF8Name[1];
+	const mDNSu8 *const end  = &UTF8Name[1] + UTF8Name[0];
+	      mDNSu8 *      ptr  = &hostlabel->c[1];
+	const mDNSu8 *const lim  = &hostlabel->c[1] + MAX_DOMAIN_LABEL;
+	while (src < end)
+		{
+		// Delete apostrophes from source name
+		if (src[0] == '\'') { src++; continue; }		// Standard straight single quote
+		if (src + 2 < end && src[0] == 0xE2 && src[1] == 0x80 && src[2] == 0x99)
+			{ src += 3; continue; }	// Unicode curly apostrophe
+		if (ptr < lim)
+			{
+			if (mDNSValidHostChar(*src, (ptr > &hostlabel->c[1]), (src < end-1))) *ptr++ = *src;
+			else if (ptr > &hostlabel->c[1] && ptr[-1] != '-') *ptr++ = '-';
+			}
+		src++;
+		}
+	while (ptr > &hostlabel->c[1] && ptr[-1] == '-') ptr--;	// Truncate trailing '-' marks
+	hostlabel->c[0] = (mDNSu8)(ptr - &hostlabel->c[1]);
+	}
+
+#define ValidTransportProtocol(X) ( (X)[0] == 4 && (X)[1] == '_' && \
+	((((X)[2] | 0x20) == 'u' && ((X)[3] | 0x20) == 'd') || (((X)[2] | 0x20) == 't' && ((X)[3] | 0x20) == 'c')) && \
+	((X)[4] | 0x20) == 'p')
+
+mDNSexport mDNSu8 *ConstructServiceName(domainname *const fqdn,
+	const domainlabel *name, const domainname *type, const domainname *const domain)
+	{
+	int i, len;
+	mDNSu8 *dst = fqdn->c;
+	const mDNSu8 *src;
+	const char *errormsg;
+#if APPLE_OSX_mDNSResponder
+	mDNSBool	loggedUnderscore = mDNSfalse;
+	static char typeBuf[MAX_ESCAPED_DOMAIN_NAME];
+#endif
+
+	// In the case where there is no name (and ONLY in that case),
+	// a single-label subtype is allowed as the first label of a three-part "type"
+	if (!name && type)
+		{
+		const mDNSu8 *s0 = type->c;
+		if (s0[0] && s0[0] < 0x40)		// If legal first label (at least one character, and no more than 63)
+			{
+			const mDNSu8 * s1 = s0 + 1 + s0[0];
+			if (s1[0] && s1[0] < 0x40)	// and legal second label (at least one character, and no more than 63)
+				{
+				const mDNSu8 *s2 = s1 + 1 + s1[0];
+				if (s2[0] && s2[0] < 0x40 && s2[1+s2[0]] == 0)	// and we have three and only three labels
+					{
+					static const mDNSu8 SubTypeLabel[5] = "\x04_sub";
+					src = s0;									// Copy the first label
+					len = *src;
+					for (i=0; i <= len;                      i++) *dst++ = *src++;
+					for (i=0; i < (int)sizeof(SubTypeLabel); i++) *dst++ = SubTypeLabel[i];
+					type = (const domainname *)s1;
+					
+					// Special support to enable the DNSServiceBrowse call made by Bonjour Browser
+					// For these queries, we retract the "._sub" we just added between the subtype and the main type
+					// Remove after Bonjour Browser is updated to use DNSServiceQueryRecord instead of DNSServiceBrowse
+					if (SameDomainName((domainname*)s0, (const domainname*)"\x09_services\x07_dns-sd\x04_udp"))
+						dst -= sizeof(SubTypeLabel);
+					}
+				}
+			}
+		}
+
+	if (name && name->c[0])
+		{
+		src = name->c;									// Put the service name into the domain name
+		len = *src;
+		if (len >= 0x40) { errormsg = "Service instance name too long"; goto fail; }
+		for (i=0; i<=len; i++) *dst++ = *src++;
+		}
+	else
+		name = (domainlabel*)"";	// Set this up to be non-null, to avoid errors if we have to call LogMsg() below
+
+	src = type->c;										// Put the service type into the domain name
+	len = *src;
+	if (len < 2 || len > 16)
+		{
+		LogMsg("Bad service type in %#s.%##s%##s Application protocol name must be underscore plus 1-15 characters. "
+			"See <http://www.dns-sd.org/ServiceTypes.html>", name->c, type->c, domain->c);
+#if APPLE_OSX_mDNSResponder
+		ConvertDomainNameToCString(type, typeBuf);
+		mDNSASLLog(mDNSNULL, "serviceType.nameTooLong", "noop", typeBuf, "");
+#endif
+		}
+	if (len < 2 || len >= 0x40 || (len > 16 && !SameDomainName(domain, &localdomain))) return(mDNSNULL);
+	if (src[1] != '_') { errormsg = "Application protocol name must begin with underscore"; goto fail; }
+	for (i=2; i<=len; i++)
+		{
+		// Letters and digits are allowed anywhere
+		if (mDNSIsLetter(src[i]) || mDNSIsDigit(src[i])) continue;
+		// Hyphens are only allowed as interior characters
+		// Underscores are not supposed to be allowed at all, but for backwards compatibility with some old products we do allow them,
+		// with the same rule as hyphens
+		if ((src[i] == '-' || src[i] == '_') && i > 2 && i < len) 
+			{
+#if APPLE_OSX_mDNSResponder
+			if (src[i] == '_' && loggedUnderscore == mDNSfalse)
+				{
+				ConvertDomainNameToCString(type, typeBuf);
+				mDNSASLLog(mDNSNULL, "serviceType.nameWithUnderscore", "noop", typeBuf, "");
+				loggedUnderscore = mDNStrue;
+				}
+#endif
+			continue;
+			}
+		errormsg = "Application protocol name must contain only letters, digits, and hyphens";
+#if APPLE_OSX_mDNSResponder
+		{
+		ConvertDomainNameToCString(type, typeBuf);
+		mDNSASLLog(mDNSNULL, "serviceType.nameWithIllegalCharacters", "noop", typeBuf, "");
+		}
+#endif
+		 goto fail;
+		}
+	for (i=0; i<=len; i++) *dst++ = *src++;
+
+	len = *src;
+	if (!ValidTransportProtocol(src)) { errormsg = "Transport protocol name must be _udp or _tcp"; goto fail; }
+	for (i=0; i<=len; i++) *dst++ = *src++;
+
+	if (*src) { errormsg = "Service type must have only two labels"; goto fail; }
+
+	*dst = 0;
+	if (!domain->c[0]) { errormsg = "Service domain must be non-empty"; goto fail; }
+	if (SameDomainName(domain, (const domainname*)"\x05" "local" "\x04" "arpa"))
+		{ errormsg = "Illegal domain \"local.arpa.\" Use \"local.\" (or empty string)"; goto fail; }
+	dst = AppendDomainName(fqdn, domain);
+	if (!dst) { errormsg = "Service domain too long"; goto fail; }
+	return(dst);
+
+fail:
+	LogMsg("ConstructServiceName: %s: %#s.%##s%##s", errormsg, name->c, type->c, domain->c);
+	return(mDNSNULL);
+	}
+
+// A service name has the form: instance.application-protocol.transport-protocol.domain
+// DeconstructServiceName is currently fairly forgiving: It doesn't try to enforce character
+// set or length limits for the protocol names, and the final domain is allowed to be empty.
+// However, if the given FQDN doesn't contain at least three labels,
+// DeconstructServiceName will reject it and return mDNSfalse.
+mDNSexport mDNSBool DeconstructServiceName(const domainname *const fqdn,
+	domainlabel *const name, domainname *const type, domainname *const domain)
+	{
+	int i, len;
+	const mDNSu8 *src = fqdn->c;
+	const mDNSu8 *max = fqdn->c + MAX_DOMAIN_NAME;
+	mDNSu8 *dst;
+
+	dst = name->c;										// Extract the service name
+	len = *src;
+	if (!len)         { debugf("DeconstructServiceName: FQDN empty!");                             return(mDNSfalse); }
+	if (len >= 0x40)  { debugf("DeconstructServiceName: Instance name too long");                  return(mDNSfalse); }
+	for (i=0; i<=len; i++) *dst++ = *src++;
+
+	dst = type->c;										// Extract the service type
+	len = *src;
+	if (!len)         { debugf("DeconstructServiceName: FQDN contains only one label!");           return(mDNSfalse); }
+	if (len >= 0x40)  { debugf("DeconstructServiceName: Application protocol name too long");      return(mDNSfalse); }
+	if (src[1] != '_'){ debugf("DeconstructServiceName: No _ at start of application protocol");   return(mDNSfalse); }
+	for (i=0; i<=len; i++) *dst++ = *src++;
+
+	len = *src;
+	if (!len)         { debugf("DeconstructServiceName: FQDN contains only two labels!");          return(mDNSfalse); }
+	if (!ValidTransportProtocol(src))
+	                  { debugf("DeconstructServiceName: Transport protocol must be _udp or _tcp"); return(mDNSfalse); }
+	for (i=0; i<=len; i++) *dst++ = *src++;
+	*dst++ = 0;											// Put terminator on the end of service type
+
+	dst = domain->c;									// Extract the service domain
+	while (*src)
+		{
+		len = *src;
+		if (len >= 0x40)
+			{ debugf("DeconstructServiceName: Label in service domain too long"); return(mDNSfalse); }
+		if (src + 1 + len + 1 >= max)
+			{ debugf("DeconstructServiceName: Total service domain too long"); return(mDNSfalse); }
+		for (i=0; i<=len; i++) *dst++ = *src++;
+		}
+	*dst++ = 0;		// Put the null root label on the end
+
+	return(mDNStrue);
+	}
+
+// Notes on UTF-8:
+// 0xxxxxxx represents a 7-bit ASCII value from 0x00 to 0x7F
+// 10xxxxxx is a continuation byte of a multi-byte character
+// 110xxxxx is the first byte of a 2-byte character (11 effective bits; values 0x     80 - 0x     800-1)
+// 1110xxxx is the first byte of a 3-byte character (16 effective bits; values 0x    800 - 0x   10000-1)
+// 11110xxx is the first byte of a 4-byte character (21 effective bits; values 0x  10000 - 0x  200000-1)
+// 111110xx is the first byte of a 5-byte character (26 effective bits; values 0x 200000 - 0x 4000000-1)
+// 1111110x is the first byte of a 6-byte character (31 effective bits; values 0x4000000 - 0x80000000-1)
+//
+// UTF-16 surrogate pairs are used in UTF-16 to encode values larger than 0xFFFF.
+// Although UTF-16 surrogate pairs are not supposed to appear in legal UTF-8, we want to be defensive
+// about that too. (See <http://www.unicode.org/faq/utf_bom.html#34>, "What are surrogates?")
+// The first of pair is a UTF-16 value in the range 0xD800-0xDBFF (11101101 1010xxxx 10xxxxxx in UTF-8),
+// and the second    is a UTF-16 value in the range 0xDC00-0xDFFF (11101101 1011xxxx 10xxxxxx in UTF-8).
+
+mDNSexport mDNSu32 TruncateUTF8ToLength(mDNSu8 *string, mDNSu32 length, mDNSu32 max)
+	{
+	if (length > max)
+		{
+		mDNSu8 c1 = string[max];										// First byte after cut point
+		mDNSu8 c2 = (max+1 < length) ? string[max+1] : (mDNSu8)0xB0;	// Second byte after cut point
+		length = max;	// Trim length down
+		while (length > 0)
+			{
+			// Check if the byte right after the chop point is a UTF-8 continuation byte,
+			// or if the character right after the chop point is the second of a UTF-16 surrogate pair.
+			// If so, then we continue to chop more bytes until we get to a legal chop point.
+			mDNSBool continuation    = ((c1 & 0xC0) == 0x80);
+			mDNSBool secondsurrogate = (c1 == 0xED && (c2 & 0xF0) == 0xB0);
+			if (!continuation && !secondsurrogate) break;
+			c2 = c1;
+			c1 = string[--length];
+			}
+		// Having truncated characters off the end of our string, also cut off any residual white space
+		while (length > 0 && string[length-1] <= ' ') length--;
+		}
+	return(length);
+	}
+
+// Returns true if a rich text label ends in " (nnn)", or if an RFC 1034
+// name ends in "-nnn", where n is some decimal number.
+mDNSexport mDNSBool LabelContainsSuffix(const domainlabel *const name, const mDNSBool RichText)
+	{
+	mDNSu16 l = name->c[0];
+
+	if (RichText)
+		{
+		if (l < 4) return mDNSfalse;							// Need at least " (2)"
+		if (name->c[l--] != ')') return mDNSfalse;				// Last char must be ')'
+		if (!mDNSIsDigit(name->c[l])) return mDNSfalse;			// Preceeded by a digit
+		l--;
+		while (l > 2 && mDNSIsDigit(name->c[l])) l--;			// Strip off digits
+		return (name->c[l] == '(' && name->c[l - 1] == ' ');
+		}
+	else
+		{
+		if (l < 2) return mDNSfalse;							// Need at least "-2"
+		if (!mDNSIsDigit(name->c[l])) return mDNSfalse;			// Last char must be a digit
+		l--;
+		while (l > 2 && mDNSIsDigit(name->c[l])) l--;			// Strip off digits
+		return (name->c[l] == '-');
+		}
+	}
+
+// removes an auto-generated suffix (appended on a name collision) from a label.  caller is
+// responsible for ensuring that the label does indeed contain a suffix.  returns the number
+// from the suffix that was removed.
+mDNSexport mDNSu32 RemoveLabelSuffix(domainlabel *name, mDNSBool RichText)
+	{
+	mDNSu32 val = 0, multiplier = 1;
+
+	// Chop closing parentheses from RichText suffix
+	if (RichText && name->c[0] >= 1 && name->c[name->c[0]] == ')') name->c[0]--;
+
+	// Get any existing numerical suffix off the name
+	while (mDNSIsDigit(name->c[name->c[0]]))
+		{ val += (name->c[name->c[0]] - '0') * multiplier; multiplier *= 10; name->c[0]--; }
+
+	// Chop opening parentheses or dash from suffix
+	if (RichText)
+		{
+		if (name->c[0] >= 2 && name->c[name->c[0]] == '(' && name->c[name->c[0]-1] == ' ') name->c[0] -= 2;
+		}
+	else
+		{
+		if (name->c[0] >= 1 && name->c[name->c[0]] == '-') name->c[0] -= 1;
+		}
+
+	return(val);
+	}
+
+// appends a numerical suffix to a label, with the number following a whitespace and enclosed
+// in parentheses (rich text) or following two consecutive hyphens (RFC 1034 domain label).
+mDNSexport void AppendLabelSuffix(domainlabel *const name, mDNSu32 val, const mDNSBool RichText)
+	{
+	mDNSu32 divisor = 1, chars = 2;	// Shortest possible RFC1034 name suffix is 2 characters ("-2")
+	if (RichText) chars = 4;		// Shortest possible RichText suffix is 4 characters (" (2)")
+
+	// Truncate trailing spaces from RichText names
+	if (RichText) while (name->c[name->c[0]] == ' ') name->c[0]--;
+
+	while (divisor < 0xFFFFFFFFUL/10 && val >= divisor * 10) { divisor *= 10; chars++; }
+
+	name->c[0] = (mDNSu8) TruncateUTF8ToLength(name->c+1, name->c[0], MAX_DOMAIN_LABEL - chars);
+
+	if (RichText) { name->c[++name->c[0]] = ' '; name->c[++name->c[0]] = '('; }
+	else          { name->c[++name->c[0]] = '-'; }
+
+	while (divisor)
+		{
+		name->c[++name->c[0]] = (mDNSu8)('0' + val / divisor);
+		val     %= divisor;
+		divisor /= 10;
+		}
+
+	if (RichText) name->c[++name->c[0]] = ')';
+	}
+
+mDNSexport void IncrementLabelSuffix(domainlabel *name, mDNSBool RichText)
+	{
+	mDNSu32 val = 0;
+
+	if (LabelContainsSuffix(name, RichText))
+		val = RemoveLabelSuffix(name, RichText);
+
+	// If no existing suffix, start by renaming "Foo" as "Foo (2)" or "Foo-2" as appropriate.
+	// If existing suffix in the range 2-9, increment it.
+	// If we've had ten conflicts already, there are probably too many hosts trying to use the same name,
+	// so add a random increment to improve the chances of finding an available name next time.
+	if      (val == 0) val = 2;
+	else if (val < 10) val++;
+	else               val += 1 + mDNSRandom(99);
+
+	AppendLabelSuffix(name, val, RichText);
+	}
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - Resource Record Utility Functions
+#endif
+
+// Set up a AuthRecord with sensible default values.
+// These defaults may be overwritten with new values before mDNS_Register is called
+mDNSexport void mDNS_SetupResourceRecord(AuthRecord *rr, RData *RDataStorage, mDNSInterfaceID InterfaceID,
+	mDNSu16 rrtype, mDNSu32 ttl, mDNSu8 RecordType, AuthRecType artype, mDNSRecordCallback Callback, void *Context)
+	{
+	//
+	// LocalOnly auth record can be created with LocalOnly InterfaceID or a valid InterfaceID.
+	// Most of the applications normally create with LocalOnly InterfaceID and we store them as
+	// such, so that we can deliver the response to questions that specify LocalOnly InterfaceID.
+	// LocalOnly resource records can also be created with valid InterfaceID which happens today
+	// when we create LocalOnly records for /etc/hosts.
+
+	if (InterfaceID == mDNSInterface_LocalOnly && artype != AuthRecordLocalOnly)
+		{
+		LogMsg("mDNS_SetupResourceRecord: ERROR!! Mismatch LocalOnly record InterfaceID %p called with artype %d", InterfaceID, artype);
+		return;
+		}
+	else if (InterfaceID == mDNSInterface_P2P && artype != AuthRecordP2P)
+		{
+		LogMsg("mDNS_SetupResourceRecord: ERROR!! Mismatch P2P record InterfaceID %p called with artype %d", InterfaceID, artype);
+		return;
+		}
+	else if (!InterfaceID && (artype == AuthRecordP2P || artype == AuthRecordLocalOnly))
+		{
+		LogMsg("mDNS_SetupResourceRecord: ERROR!! Mismatch InterfaceAny record InterfaceID %p called with artype %d", InterfaceID, artype);
+		return;
+		}
+
+	// Don't try to store a TTL bigger than we can represent in platform time units
+	if (ttl > 0x7FFFFFFFUL / mDNSPlatformOneSecond)
+		ttl = 0x7FFFFFFFUL / mDNSPlatformOneSecond;
+	else if (ttl == 0)		// And Zero TTL is illegal
+		ttl = DefaultTTLforRRType(rrtype);
+
+	// Field Group 1: The actual information pertaining to this resource record
+	rr->resrec.RecordType        = RecordType;
+	rr->resrec.InterfaceID       = InterfaceID;
+	rr->resrec.name              = &rr->namestorage;
+	rr->resrec.rrtype            = rrtype;
+	rr->resrec.rrclass           = kDNSClass_IN;
+	rr->resrec.rroriginalttl     = ttl;
+	rr->resrec.rDNSServer		 = mDNSNULL;
+//	rr->resrec.rdlength          = MUST set by client and/or in mDNS_Register_internal
+//	rr->resrec.rdestimate        = set in mDNS_Register_internal
+//	rr->resrec.rdata             = MUST be set by client
+
+	if (RDataStorage)
+		rr->resrec.rdata = RDataStorage;
+	else
+		{
+		rr->resrec.rdata = &rr->rdatastorage;
+		rr->resrec.rdata->MaxRDLength = sizeof(RDataBody);
+		}
+
+	// Field Group 2: Persistent metadata for Authoritative Records
+	rr->Additional1       = mDNSNULL;
+	rr->Additional2       = mDNSNULL;
+	rr->DependentOn       = mDNSNULL;
+	rr->RRSet             = mDNSNULL;
+	rr->RecordCallback    = Callback;
+	rr->RecordContext     = Context;
+
+	rr->AutoTarget        = Target_Manual;
+	rr->AllowRemoteQuery  = mDNSfalse;
+	rr->ForceMCast        = mDNSfalse;
+
+	rr->WakeUp            = zeroOwner;
+	rr->AddressProxy      = zeroAddr;
+	rr->TimeRcvd          = 0;
+	rr->TimeExpire        = 0;
+	rr->ARType            = artype;
+
+	// Field Group 3: Transient state for Authoritative Records (set in mDNS_Register_internal)
+	// Field Group 4: Transient uDNS state for Authoritative Records (set in mDNS_Register_internal)
+
+	// For now, until the uDNS code is fully integrated, it's helpful to zero the uDNS state fields here too, just in case
+	// (e.g. uDNS_RegisterService short-circuits the usual mDNS_Register_internal record registration calls, so a bunch
+	// of fields don't get set up properly. In particular, if we don't zero rr->QueuedRData then the uDNS code crashes.)
+	rr->state             = regState_Zero;
+	rr->uselease          = 0;
+	rr->expire            = 0;
+	rr->Private           = 0;
+	rr->updateid          = zeroID;
+	rr->zone              = rr->resrec.name;
+	rr->nta               = mDNSNULL;
+	rr->tcp               = mDNSNULL;
+	rr->OrigRData         = 0;
+	rr->OrigRDLen         = 0;
+	rr->InFlightRData     = 0;
+	rr->InFlightRDLen     = 0;
+	rr->QueuedRData       = 0;
+	rr->QueuedRDLen       = 0;	
+	mDNSPlatformMemZero(&rr->NATinfo, sizeof(rr->NATinfo));
+	rr->SRVChanged = mDNSfalse;
+	rr->mState = mergeState_Zero;
+
+	rr->namestorage.c[0]  = 0;		// MUST be set by client before calling mDNS_Register()
+	}
+
+mDNSexport void mDNS_SetupQuestion(DNSQuestion *const q, const mDNSInterfaceID InterfaceID, const domainname *const name,
+               const mDNSu16 qtype, mDNSQuestionCallback *const callback, void *const context)
+	{
+	q->InterfaceID         = InterfaceID;
+	q->Target              = zeroAddr;
+	AssignDomainName(&q->qname, name);
+	q->qtype               = qtype;
+	q->qclass              = kDNSClass_IN;
+	q->LongLived           = (qtype == kDNSType_PTR);
+	q->ExpectUnique        = (qtype != kDNSType_PTR);
+	q->ForceMCast          = mDNSfalse;
+	q->ReturnIntermed      = mDNSfalse;
+	q->SuppressUnusable    = mDNSfalse;
+	q->SearchListIndex     = 0;
+	q->AppendSearchDomains = 0;
+	q->RetryWithSearchDomains = mDNSfalse;
+	q->TimeoutQuestion     = 0;
+	q->WakeOnResolve       = 0;
+	q->qnameOrig           = mDNSNULL;
+	q->QuestionCallback    = callback;
+	q->QuestionContext     = context;
+	}
+
+mDNSexport mDNSu32 RDataHashValue(const ResourceRecord *const rr)
+	{
+	int len = rr->rdlength;
+	const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data;
+	switch(rr->rrtype)
+		{
+		case kDNSType_NS:
+		case kDNSType_CNAME:
+		case kDNSType_PTR:
+		case kDNSType_DNAME: return DomainNameHashValue(&rdb->name);
+
+		case kDNSType_SOA:   return rdb->soa.serial  +
+									rdb->soa.refresh +
+									rdb->soa.retry   +
+									rdb->soa.expire  +
+									rdb->soa.min     +
+									DomainNameHashValue(&rdb->soa.mname) +
+									DomainNameHashValue(&rdb->soa.rname);
+
+		case kDNSType_MX:
+		case kDNSType_AFSDB:
+		case kDNSType_RT:
+		case kDNSType_KX:	 return DomainNameHashValue(&rdb->mx.exchange);
+
+		case kDNSType_RP:	 return DomainNameHashValue(&rdb->rp.mbox)   + DomainNameHashValue(&rdb->rp.txt);
+
+		case kDNSType_PX:	 return DomainNameHashValue(&rdb->px.map822) + DomainNameHashValue(&rdb->px.mapx400);
+
+		case kDNSType_SRV:	 return DomainNameHashValue(&rdb->srv.target);
+
+		case kDNSType_OPT:	 return 0;	// OPT is a pseudo-RR container structure; makes no sense to compare
+
+		case kDNSType_NSEC:	 len = sizeof(rdataNSEC);	// Use in-memory length of 32, and fall through default checksum computation below
+
+		default:
+			{
+			mDNSu32 sum = 0;
+			int i;
+			for (i=0; i+1 < len; i+=2)
+				{
+				sum += (((mDNSu32)(rdb->data[i])) << 8) | rdb->data[i+1];
+				sum = (sum<<3) | (sum>>29);
+				}
+			if (i < len)
+				{
+				sum += ((mDNSu32)(rdb->data[i])) << 8;
+				}
+			return(sum);
+			}
+		}
+	}
+
+// r1 has to be a full ResourceRecord including rrtype and rdlength
+// r2 is just a bare RDataBody, which MUST be the same rrtype and rdlength as r1
+mDNSexport mDNSBool SameRDataBody(const ResourceRecord *const r1, const RDataBody *const r2, DomainNameComparisonFn *samename)
+	{
+	const RDataBody2 *const b1 = (RDataBody2 *)r1->rdata->u.data;
+	const RDataBody2 *const b2 = (RDataBody2 *)r2;
+	switch(r1->rrtype)
+		{
+		case kDNSType_NS:
+		case kDNSType_CNAME:
+		case kDNSType_PTR:
+		case kDNSType_DNAME:return(SameDomainName(&b1->name, &b2->name));
+
+		case kDNSType_SOA:	return(mDNSBool)(  	b1->soa.serial   == b2->soa.serial             &&
+												b1->soa.refresh  == b2->soa.refresh            &&
+												b1->soa.retry    == b2->soa.retry              &&
+												b1->soa.expire   == b2->soa.expire             &&
+												b1->soa.min      == b2->soa.min                &&
+												samename(&b1->soa.mname, &b2->soa.mname) &&
+												samename(&b1->soa.rname, &b2->soa.rname));
+
+		case kDNSType_MX:
+		case kDNSType_AFSDB:
+		case kDNSType_RT:
+		case kDNSType_KX:	return(mDNSBool)(  	b1->mx.preference == b2->mx.preference &&
+												samename(&b1->mx.exchange, &b2->mx.exchange));
+
+		case kDNSType_RP:	return(mDNSBool)(  	samename(&b1->rp.mbox, &b2->rp.mbox) &&
+												samename(&b1->rp.txt,  &b2->rp.txt));
+
+		case kDNSType_PX:	return(mDNSBool)(  	b1->px.preference == b2->px.preference          &&
+												samename(&b1->px.map822,  &b2->px.map822) &&
+												samename(&b1->px.mapx400, &b2->px.mapx400));
+
+		case kDNSType_SRV:	return(mDNSBool)(  	b1->srv.priority == b2->srv.priority       &&
+												b1->srv.weight   == b2->srv.weight         &&
+												mDNSSameIPPort(b1->srv.port, b2->srv.port) &&
+												samename(&b1->srv.target, &b2->srv.target));
+
+		case kDNSType_OPT:	return mDNSfalse;	// OPT is a pseudo-RR container structure; makes no sense to compare
+
+		case kDNSType_NSEC: return(mDNSPlatformMemSame(b1->data, b2->data, sizeof(rdataNSEC)));
+
+		default:			return(mDNSPlatformMemSame(b1->data, b2->data, r1->rdlength));
+		}
+	}
+
+// ResourceRecordAnswersQuestion returns mDNStrue if the given resource record is a valid answer to the given question.
+// SameNameRecordAnswersQuestion is the same, except it skips the expensive SameDomainName() call.
+// SameDomainName() is generally cheap when the names don't match, but expensive when they do match,
+// because it has to check all the way to the end of the names to be sure.
+// In cases where we know in advance that the names match it's especially advantageous to skip the
+// SameDomainName() call because that's precisely the time when it's most expensive and least useful.
+
+mDNSexport mDNSBool SameNameRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q)
+	{
+	// LocalOnly/P2P questions can be answered with AuthRecordAny in this function. LocalOnly/P2P records
+	// are handled in LocalOnlyRecordAnswersQuestion
+	if ((rr->InterfaceID == mDNSInterface_LocalOnly) || (rr->InterfaceID == mDNSInterface_P2P))
+		{
+		LogMsg("SameNameRecordAnswersQuestion: ERROR!! called with LocalOnly ResourceRecord %p, Question %p", rr->InterfaceID, q->InterfaceID);
+		return mDNSfalse;
+		}
+	if (rr->InterfaceID &&
+		q ->InterfaceID && q->InterfaceID != mDNSInterface_LocalOnly &&
+		rr->InterfaceID != q->InterfaceID) return(mDNSfalse);
+
+	// Resource record received via unicast, the DNSServer entries should match ?
+	if (!rr->InterfaceID && rr->rDNSServer != q->qDNSServer) return(mDNSfalse);
+
+	// If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question
+	if (rr->InterfaceID && !mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse);
+
+	// RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class.
+	if (!RRTypeAnswersQuestionType(rr,q->qtype)) return(mDNSfalse);
+	if (rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse);
+
+	return(mDNStrue);
+	}
+
+mDNSexport mDNSBool ResourceRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q)
+	{
+	// LocalOnly/P2P questions can be answered with AuthRecordAny in this function. LocalOnly/P2P records
+	// are handled in LocalOnlyRecordAnswersQuestion
+	if ((rr->InterfaceID == mDNSInterface_LocalOnly) || (rr->InterfaceID == mDNSInterface_P2P))
+		{
+		LogMsg("ResourceRecordAnswersQuestion: ERROR!! called with LocalOnly/P2P ResourceRecord %p, Question %p", rr->InterfaceID, q->InterfaceID);
+		return mDNSfalse;
+		}
+
+	if (rr->InterfaceID &&
+		q ->InterfaceID && q->InterfaceID != mDNSInterface_LocalOnly &&
+		rr->InterfaceID != q->InterfaceID) return(mDNSfalse);
+
+	// Resource record received via unicast, the DNSServer entries should match ?
+	if (!rr->InterfaceID && rr->rDNSServer != q->qDNSServer) return(mDNSfalse);
+
+	// If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question.
+	if (rr->InterfaceID && !mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse);
+
+	// RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class.
+	if (!RRTypeAnswersQuestionType(rr,q->qtype)) return(mDNSfalse);
+	if (rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse);
+
+	return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname));
+	}
+
+// We have a separate function to handle LocalOnly AuthRecords because they can be created with
+// a valid InterfaceID (e.g., scoped /etc/hosts) and can be used to answer unicast questions unlike
+// multicast resource records (which has a valid InterfaceID) which can't be used to answer
+// unicast questions. ResourceRecordAnswersQuestion/SameNameRecordAnswersQuestion can't tell whether
+// a resource record is multicast or LocalOnly by just looking at the ResourceRecord because
+// LocalOnly records are truly identified by ARType in the AuthRecord.  As P2P and LocalOnly record
+// are kept in the same hash table, we use the same function to make it easy for the callers when
+// they walk the hash table to answer LocalOnly/P2P questions
+//
+mDNSexport mDNSBool LocalOnlyRecordAnswersQuestion(AuthRecord *const ar, const DNSQuestion *const q)
+	{
+	ResourceRecord *rr = &ar->resrec;
+	
+	// mDNSInterface_Any questions can be answered with LocalOnly/P2P records in this function. AuthRecord_Any
+	// records are handled in ResourceRecordAnswersQuestion/SameNameRecordAnswersQuestion
+	if (RRAny(ar))
+		{
+		LogMsg("LocalOnlyRecordAnswersQuestion: ERROR!! called with regular AuthRecordAny %##s", rr->name->c);
+		return mDNSfalse;
+		}
+		
+	// Questions with mDNSInterface_LocalOnly InterfaceID should be answered with all resource records that are
+	// *local* to the machine. These include resource records that have InterfaceID set to mDNSInterface_LocalOnly,
+	// mDNSInterface_Any and any other real InterfaceID. Hence, LocalOnly questions should not be checked against
+	// the InterfaceID in the resource record.
+	//
+	// mDNSInterface_Unicast does not indicate any scope and hence treat them like mDNSInterface_Any.
+
+	if (rr->InterfaceID && 
+		q->InterfaceID && q->InterfaceID != mDNSInterface_LocalOnly && q->InterfaceID != mDNSInterface_Unicast &&
+		rr->InterfaceID != q->InterfaceID) return(mDNSfalse);
+
+	// Entries in /etc/hosts are added as LocalOnly resource records. The LocalOnly resource records
+	// may have a scope e.g., fe80::1%en0. The question may be scoped or not: the InterfaceID may be set
+	// to mDNSInterface_Any, mDNSInterface_LocalOnly or a real InterfaceID (scoped). 
+	//
+	// 1) Question: Any, LocalOnly Record: no scope. This question should be answered with this record.
+	//
+	// 2) Question: Any, LocalOnly Record: scoped.  This question should be answered with the record because
+	//    traditionally applications never specify scope e.g., getaddrinfo, but need to be able
+	//    to get to /etc/hosts entries.
+	//
+	// 3) Question: Scoped (LocalOnly or InterfaceID), LocalOnly Record: no scope. This is the inverse of (2).
+	//    If we register a LocalOnly record, we need to answer a LocalOnly question. If the /etc/hosts has a
+	//    non scoped entry, it may not make sense to answer a scoped question. But we can't tell these two
+	//    cases apart. As we currently answer LocalOnly question with LocalOnly record, we continue to do so.
+	//
+	// 4) Question: Scoped (LocalOnly or InterfaceID), LocalOnly Record: scoped. LocalOnly questions should be
+	//    answered with any resource record where as if it has a valid InterfaceID, the scope should match.
+	//
+	// (1) and (2) is bypassed because we check for a non-NULL InterfaceID above. For (3), the InterfaceID is NULL
+	// and hence bypassed above. For (4) we bypassed LocalOnly questions and checked the scope of the record
+	// against the question.
+	//
+	// For P2P, InterfaceIDs of the question and the record should match.
+
+	// If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question.
+	// LocalOnly authoritative answers are exempt. LocalOnly authoritative answers are used for /etc/host entries.
+	// We don't want a local process to be able to create a fake LocalOnly address record for "www.bigbank.com" which would then
+	// cause other applications (e.g. Safari) to connect to the wrong address. The rpc to register records filters out records
+	// with names that don't end in local and have mDNSInterface_LocalOnly set.
+	//
+	// Note: The check is bypassed for LocalOnly and for P2P it is not needed as only .local records are registered and for
+	// a question to match its names, it also has to end in .local and that question can't be a unicast question (See
+	// Question_uDNS macro and its usage). As P2P does not enforce .local only registrations we still make this check
+	// and also makes it future proof.
+
+	if (ar->ARType != AuthRecordLocalOnly && rr->InterfaceID && !mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse);
+
+	// RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class.
+	if (!RRTypeAnswersQuestionType(rr,q->qtype)) return(mDNSfalse);
+	if (rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse);
+
+	return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname));
+	}
+
+mDNSexport mDNSBool AnyTypeRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q)
+	{
+	// LocalOnly/P2P questions can be answered with AuthRecordAny in this function. LocalOnly/P2P records
+	// are handled in LocalOnlyRecordAnswersQuestion
+	if ((rr->InterfaceID == mDNSInterface_LocalOnly) || (rr->InterfaceID == mDNSInterface_P2P))
+		{
+		LogMsg("AnyTypeRecordAnswersQuestion: ERROR!! called with LocalOnly ResourceRecord %p, Question %p", rr->InterfaceID, q->InterfaceID);
+		return mDNSfalse;
+		}
+	if (rr->InterfaceID &&
+		q ->InterfaceID && q->InterfaceID != mDNSInterface_LocalOnly &&
+		rr->InterfaceID != q->InterfaceID) return(mDNSfalse);
+
+	// Resource record received via unicast, the DNSServer entries should match ?
+	// Note that Auth Records are normally setup with NULL InterfaceID and
+	// both the DNSServers are assumed to be NULL in that case
+	if (!rr->InterfaceID && rr->rDNSServer != q->qDNSServer) return(mDNSfalse);
+
+	// If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question
+	if (rr->InterfaceID && !mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse);
+
+	if (rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse);
+
+	return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname));
+	}
+
+// This is called with both unicast resource record and multicast resource record. The question that
+// received the unicast response could be the regular unicast response from a DNS server or a response
+// to a mDNS QU query. The main reason we need this function is that we can't compare DNSServers between the
+// question and the resource record because the resource record is not completely initialized in
+// mDNSCoreReceiveResponse when this function is called.
+mDNSexport mDNSBool ResourceRecordAnswersUnicastResponse(const ResourceRecord *const rr, const DNSQuestion *const q)
+	{
+	// For resource records created using multicast, the InterfaceIDs have to match
+	if (rr->InterfaceID &&
+		q->InterfaceID && rr->InterfaceID != q->InterfaceID) return(mDNSfalse);
+
+	// If ResourceRecord received via multicast, but question was unicast, then shouldn't use record to answer this question.
+	if (rr->InterfaceID && !mDNSOpaque16IsZero(q->TargetQID)) return(mDNSfalse);
+
+	// RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class.
+	if (!RRTypeAnswersQuestionType(rr,q->qtype)) return(mDNSfalse);
+
+	if (rr->rrclass != q->qclass && q->qclass != kDNSQClass_ANY) return(mDNSfalse);
+
+	return(rr->namehash == q->qnamehash && SameDomainName(rr->name, &q->qname));
+	}
+
+mDNSexport mDNSu16 GetRDLength(const ResourceRecord *const rr, mDNSBool estimate)
+	{
+	const RDataBody2 *const rd = (RDataBody2 *)rr->rdata->u.data;
+	const domainname *const name = estimate ? rr->name : mDNSNULL;
+	if (rr->rrclass == kDNSQClass_ANY) return(rr->rdlength);	// Used in update packets to mean "Delete An RRset" (RFC 2136)
+	else switch (rr->rrtype)
+		{
+		case kDNSType_A:	return(sizeof(rd->ipv4));
+
+		case kDNSType_NS:
+		case kDNSType_CNAME:
+		case kDNSType_PTR:
+		case kDNSType_DNAME:return(CompressedDomainNameLength(&rd->name, name));
+
+		case kDNSType_SOA:  return(mDNSu16)(CompressedDomainNameLength(&rd->soa.mname, name) +
+											CompressedDomainNameLength(&rd->soa.rname, name) +
+											5 * sizeof(mDNSOpaque32));
+
+		case kDNSType_NULL:
+		case kDNSType_TSIG:
+		case kDNSType_TXT:
+		case kDNSType_X25:
+		case kDNSType_ISDN:
+		case kDNSType_LOC:
+		case kDNSType_DHCID:return(rr->rdlength); // Not self-describing, so have to just trust rdlength
+
+		case kDNSType_HINFO:return(mDNSu16)(2 + (int)rd->data[0] + (int)rd->data[1 + (int)rd->data[0]]);
+
+		case kDNSType_MX:
+		case kDNSType_AFSDB:
+		case kDNSType_RT:
+		case kDNSType_KX:	return(mDNSu16)(2 + CompressedDomainNameLength(&rd->mx.exchange, name));
+
+		case kDNSType_RP:	return(mDNSu16)(CompressedDomainNameLength(&rd->rp.mbox, name) +
+											CompressedDomainNameLength(&rd->rp.txt, name));
+
+		case kDNSType_PX:	return(mDNSu16)(2 + CompressedDomainNameLength(&rd->px.map822, name) +
+												CompressedDomainNameLength(&rd->px.mapx400, name));
+
+		case kDNSType_AAAA:	return(sizeof(rd->ipv6));
+
+		case kDNSType_SRV:	return(mDNSu16)(6 + CompressedDomainNameLength(&rd->srv.target, name));
+
+		case kDNSType_OPT:  return(rr->rdlength);
+
+		case kDNSType_NSEC: {
+							int i;
+							for (i=sizeof(rdataNSEC); i>0; i--) if (rd->nsec.bitmap[i-1]) break;
+							// For our simplified use of NSEC synthetic records:
+							// nextname is always the record's own name,
+							// and if we have at least one record type that exists,
+							//  - the block number is always 0,
+							//  - the count byte is a value in the range 1-32,
+							//  - followed by the 1-32 data bytes
+							return(mDNSu16)((estimate ? 2 : DomainNameLength(rr->name)) + (i ? (2 + i) : 0));
+							}
+
+		default:			debugf("Warning! Don't know how to get length of resource type %d", rr->rrtype);
+							return(rr->rdlength);
+		}
+	}
+
+// When a local client registers (or updates) a record, we use this routine to do some simple validation checks
+// to help reduce the risk of bogus malformed data on the network
+mDNSexport mDNSBool ValidateRData(const mDNSu16 rrtype, const mDNSu16 rdlength, const RData *const rd)
+	{
+	mDNSu16 len;
+
+	switch(rrtype)
+		{
+		case kDNSType_A:	return(rdlength == sizeof(mDNSv4Addr));
+
+		case kDNSType_NS:	// Same as PTR
+		case kDNSType_MD:	// Same as PTR
+		case kDNSType_MF:	// Same as PTR
+		case kDNSType_CNAME:// Same as PTR
+		//case kDNSType_SOA not checked
+		case kDNSType_MB:	// Same as PTR
+		case kDNSType_MG:	// Same as PTR
+		case kDNSType_MR:	// Same as PTR
+		//case kDNSType_NULL not checked (no specified format, so always valid)
+		//case kDNSType_WKS not checked
+		case kDNSType_PTR:	len = DomainNameLengthLimit(&rd->u.name, rd->u.data + rdlength);
+							return(len <= MAX_DOMAIN_NAME && rdlength == len);
+
+		case kDNSType_HINFO:// Same as TXT (roughly)
+		case kDNSType_MINFO:// Same as TXT (roughly)
+		case kDNSType_TXT:  if (!rdlength) return(mDNSfalse); // TXT record has to be at least one byte (RFC 1035)
+							{
+							const mDNSu8 *ptr = rd->u.txt.c;
+							const mDNSu8 *end = rd->u.txt.c + rdlength;
+							while (ptr < end) ptr += 1 + ptr[0];
+							return (ptr == end);
+							}
+
+		case kDNSType_AAAA:	return(rdlength == sizeof(mDNSv6Addr));
+
+		case kDNSType_MX:   // Must be at least two-byte preference, plus domainname
+							// Call to DomainNameLengthLimit() implicitly enforces both requirements for us
+							len = DomainNameLengthLimit(&rd->u.mx.exchange, rd->u.data + rdlength);
+							return(len <= MAX_DOMAIN_NAME && rdlength == 2+len);
+
+		case kDNSType_SRV:	// Must be at least priority+weight+port, plus domainname
+							// Call to DomainNameLengthLimit() implicitly enforces both requirements for us
+							len = DomainNameLengthLimit(&rd->u.srv.target, rd->u.data + rdlength);
+							return(len <= MAX_DOMAIN_NAME && rdlength == 6+len);
+
+		//case kDNSType_NSEC not checked
+
+		default:			return(mDNStrue);	// Allow all other types without checking
+		}
+	}
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - DNS Message Creation Functions
+#endif
+
+mDNSexport void InitializeDNSMessage(DNSMessageHeader *h, mDNSOpaque16 id, mDNSOpaque16 flags)
+	{
+	h->id             = id;
+	h->flags          = flags;
+	h->numQuestions   = 0;
+	h->numAnswers     = 0;
+	h->numAuthorities = 0;
+	h->numAdditionals = 0;
+	}
+
+mDNSexport const mDNSu8 *FindCompressionPointer(const mDNSu8 *const base, const mDNSu8 *const end, const mDNSu8 *const domname)
+	{
+	const mDNSu8 *result = end - *domname - 1;
+
+	if (*domname == 0) return(mDNSNULL);	// There's no point trying to match just the root label
+
+	// This loop examines each possible starting position in packet, starting end of the packet and working backwards
+	while (result >= base)
+		{
+		// If the length byte and first character of the label match, then check further to see
+		// if this location in the packet will yield a useful name compression pointer.
+		if (result[0] == domname[0] && result[1] == domname[1])
+			{
+			const mDNSu8 *name = domname;
+			const mDNSu8 *targ = result;
+			while (targ + *name < end)
+				{
+				// First see if this label matches
+				int i;
+				const mDNSu8 *pointertarget;
+				for (i=0; i <= *name; i++) if (targ[i] != name[i]) break;
+				if (i <= *name) break;							// If label did not match, bail out
+				targ += 1 + *name;								// Else, did match, so advance target pointer
+				name += 1 + *name;								// and proceed to check next label
+				if (*name == 0 && *targ == 0) return(result);	// If no more labels, we found a match!
+				if (*name == 0) break;							// If no more labels to match, we failed, so bail out
+
+				// The label matched, so now follow the pointer (if appropriate) and then see if the next label matches
+				if (targ[0] < 0x40) continue;					// If length value, continue to check next label
+				if (targ[0] < 0xC0) break;						// If 40-BF, not valid
+				if (targ+1 >= end) break;						// Second byte not present!
+				pointertarget = base + (((mDNSu16)(targ[0] & 0x3F)) << 8) + targ[1];
+				if (targ < pointertarget) break;				// Pointertarget must point *backwards* in the packet
+				if (pointertarget[0] >= 0x40) break;			// Pointertarget must point to a valid length byte
+				targ = pointertarget;
+				}
+			}
+		result--;	// We failed to match at this search position, so back up the tentative result pointer and try again
+		}
+	return(mDNSNULL);
+	}
+
+// Put a string of dot-separated labels as length-prefixed labels
+// domainname is a fully-qualified name (i.e. assumed to be ending in a dot, even if it doesn't)
+// msg points to the message we're building (pass mDNSNULL if we don't want to use compression pointers)
+// end points to the end of the message so far
+// ptr points to where we want to put the name
+// limit points to one byte past the end of the buffer that we must not overrun
+// domainname is the name to put
+mDNSexport mDNSu8 *putDomainNameAsLabels(const DNSMessage *const msg,
+	mDNSu8 *ptr, const mDNSu8 *const limit, const domainname *const name)
+	{
+	const mDNSu8 *const base        = (const mDNSu8 *)msg;
+	const mDNSu8 *      np          = name->c;
+	const mDNSu8 *const max         = name->c + MAX_DOMAIN_NAME;	// Maximum that's valid
+	const mDNSu8 *      pointer     = mDNSNULL;
+	const mDNSu8 *const searchlimit = ptr;
+
+	if (!ptr) { LogMsg("putDomainNameAsLabels %##s ptr is null", name->c); return(mDNSNULL); }
+
+	if (!*np)		// If just writing one-byte root label, make sure we have space for that
+		{
+		if (ptr >= limit) return(mDNSNULL);
+		}
+	else			// else, loop through writing labels and/or a compression offset
+		{
+		do	{
+			if (*np > MAX_DOMAIN_LABEL)
+				{ LogMsg("Malformed domain name %##s (label more than 63 bytes)", name->c); return(mDNSNULL); }
+	
+			// This check correctly allows for the final trailing root label:
+			// e.g.
+			// Suppose our domain name is exactly 256 bytes long, including the final trailing root label.
+			// Suppose np is now at name->c[249], and we're about to write our last non-null label ("local").
+			// We know that max will be at name->c[256]
+			// That means that np + 1 + 5 == max - 1, so we (just) pass the "if" test below, write our
+			// six bytes, then exit the loop, write the final terminating root label, and the domain
+			// name we've written is exactly 256 bytes long, exactly at the correct legal limit.
+			// If the name is one byte longer, then we fail the "if" test below, and correctly bail out.
+			if (np + 1 + *np >= max)
+				{ LogMsg("Malformed domain name %##s (more than 256 bytes)", name->c); return(mDNSNULL); }
+	
+			if (base) pointer = FindCompressionPointer(base, searchlimit, np);
+			if (pointer)					// Use a compression pointer if we can
+				{
+				const mDNSu16 offset = (mDNSu16)(pointer - base);
+				if (ptr+2 > limit) return(mDNSNULL);	// If we don't have two bytes of space left, give up
+				*ptr++ = (mDNSu8)(0xC0 | (offset >> 8));
+				*ptr++ = (mDNSu8)(        offset &  0xFF);
+				return(ptr);
+				}
+			else							// Else copy one label and try again
+				{
+				int i;
+				mDNSu8 len = *np++;
+				// If we don't at least have enough space for this label *plus* a terminating zero on the end, give up
+				if (ptr + 1 + len >= limit) return(mDNSNULL);
+				*ptr++ = len;
+				for (i=0; i<len; i++) *ptr++ = *np++;
+				}
+			} while (*np);					// While we've got characters remaining in the name, continue
+		}
+
+	*ptr++ = 0;		// Put the final root label
+	return(ptr);
+	}
+
+mDNSlocal mDNSu8 *putVal16(mDNSu8 *ptr, mDNSu16 val)
+	{
+	ptr[0] = (mDNSu8)((val >> 8 ) & 0xFF);
+	ptr[1] = (mDNSu8)((val      ) & 0xFF);
+	return ptr + sizeof(mDNSOpaque16);
+	}
+
+mDNSlocal mDNSu8 *putVal32(mDNSu8 *ptr, mDNSu32 val)
+	{
+	ptr[0] = (mDNSu8)((val >> 24) & 0xFF);
+	ptr[1] = (mDNSu8)((val >> 16) & 0xFF);
+	ptr[2] = (mDNSu8)((val >>  8) & 0xFF);
+	ptr[3] = (mDNSu8)((val      ) & 0xFF);
+	return ptr + sizeof(mDNSu32);
+	}
+
+// msg points to the message we're building (pass mDNSNULL if we don't want to use compression pointers)
+mDNSexport mDNSu8 *putRData(const DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, const ResourceRecord *const rr)
+	{
+	const RDataBody2 *const rdb = (RDataBody2 *)rr->rdata->u.data;
+	switch (rr->rrtype)
+		{
+		case kDNSType_A:	if (rr->rdlength != 4)
+								{ debugf("putRData: Illegal length %d for kDNSType_A", rr->rdlength); return(mDNSNULL); }
+							if (ptr + 4 > limit) return(mDNSNULL);
+							*ptr++ = rdb->ipv4.b[0];
+							*ptr++ = rdb->ipv4.b[1];
+							*ptr++ = rdb->ipv4.b[2];
+							*ptr++ = rdb->ipv4.b[3];
+							return(ptr);
+
+		case kDNSType_NS:
+		case kDNSType_CNAME:
+		case kDNSType_PTR:
+		case kDNSType_DNAME:return(putDomainNameAsLabels(msg, ptr, limit, &rdb->name));
+
+		case kDNSType_SOA:  ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->soa.mname);
+							if (!ptr) return(mDNSNULL);
+							ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->soa.rname);
+							if (!ptr || ptr + 20 > limit) return(mDNSNULL);
+							ptr = putVal32(ptr, rdb->soa.serial);
+							ptr = putVal32(ptr, rdb->soa.refresh);
+							ptr = putVal32(ptr, rdb->soa.retry);
+							ptr = putVal32(ptr, rdb->soa.expire);
+							ptr = putVal32(ptr, rdb->soa.min);
+			                return(ptr);
+
+		case kDNSType_NULL:
+		case kDNSType_HINFO:
+		case kDNSType_TSIG:
+		case kDNSType_TXT:
+		case kDNSType_X25:
+		case kDNSType_ISDN:
+		case kDNSType_LOC:
+		case kDNSType_DHCID:if (ptr + rr->rdlength > limit) return(mDNSNULL);
+							mDNSPlatformMemCopy(ptr, rdb->data, rr->rdlength);
+							return(ptr + rr->rdlength);
+
+		case kDNSType_MX:
+		case kDNSType_AFSDB:
+		case kDNSType_RT:
+		case kDNSType_KX:	if (ptr + 3 > limit) return(mDNSNULL);
+							ptr = putVal16(ptr, rdb->mx.preference);
+							return(putDomainNameAsLabels(msg, ptr, limit, &rdb->mx.exchange));
+
+		case kDNSType_RP:	ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->rp.mbox);
+							if (!ptr) return(mDNSNULL);
+							ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->rp.txt);
+			                return(ptr);
+
+		case kDNSType_PX:	if (ptr + 5 > limit) return(mDNSNULL);
+							ptr = putVal16(ptr, rdb->px.preference);
+							ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->px.map822);
+							if (!ptr) return(mDNSNULL);
+							ptr = putDomainNameAsLabels(msg, ptr, limit, &rdb->px.mapx400);
+			                return(ptr);
+
+		case kDNSType_AAAA:	if (rr->rdlength != sizeof(rdb->ipv6))
+								{ debugf("putRData: Illegal length %d for kDNSType_AAAA", rr->rdlength); return(mDNSNULL); }
+							if (ptr + sizeof(rdb->ipv6) > limit) return(mDNSNULL);
+							mDNSPlatformMemCopy(ptr, &rdb->ipv6, sizeof(rdb->ipv6));
+							return(ptr + sizeof(rdb->ipv6));
+
+		case kDNSType_SRV:	if (ptr + 7 > limit) return(mDNSNULL);
+							*ptr++ = (mDNSu8)(rdb->srv.priority >> 8);
+							*ptr++ = (mDNSu8)(rdb->srv.priority &  0xFF);
+							*ptr++ = (mDNSu8)(rdb->srv.weight   >> 8);
+							*ptr++ = (mDNSu8)(rdb->srv.weight   &  0xFF);
+							*ptr++ = rdb->srv.port.b[0];
+							*ptr++ = rdb->srv.port.b[1];
+							return(putDomainNameAsLabels(msg, ptr, limit, &rdb->srv.target));
+
+		case kDNSType_OPT:	{
+							int len = 0;
+							const rdataOPT *opt;
+							const rdataOPT *const end = (const rdataOPT *)&rr->rdata->u.data[rr->rdlength];
+							for (opt = &rr->rdata->u.opt[0]; opt < end; opt++) len += DNSOpt_Data_Space(opt);
+							if (ptr + len > limit) { LogMsg("ERROR: putOptRData - out of space"); return mDNSNULL; }
+						
+							for (opt = &rr->rdata->u.opt[0]; opt < end; opt++)
+								{
+								const int space = DNSOpt_Data_Space(opt);
+								ptr = putVal16(ptr, opt->opt);
+								ptr = putVal16(ptr, (mDNSu16)space - 4);
+								switch (opt->opt)
+									{
+									case kDNSOpt_LLQ:
+										ptr = putVal16(ptr, opt->u.llq.vers);
+										ptr = putVal16(ptr, opt->u.llq.llqOp);
+										ptr = putVal16(ptr, opt->u.llq.err);
+										mDNSPlatformMemCopy(ptr, opt->u.llq.id.b, 8);  // 8-byte id
+										ptr += 8;
+										ptr = putVal32(ptr, opt->u.llq.llqlease);
+										break;
+									case kDNSOpt_Lease:
+										ptr = putVal32(ptr, opt->u.updatelease);
+										break;
+									case kDNSOpt_Owner:
+										*ptr++ = opt->u.owner.vers;
+										*ptr++ = opt->u.owner.seq;
+										mDNSPlatformMemCopy(ptr, opt->u.owner.HMAC.b, 6);  // 6-byte Host identifier
+										ptr += 6;
+										if (space >= DNSOpt_OwnerData_ID_Wake_Space)
+											{
+											mDNSPlatformMemCopy(ptr, opt->u.owner.IMAC.b, 6);	// 6-byte interface MAC
+											ptr += 6;
+											if (space > DNSOpt_OwnerData_ID_Wake_Space)
+												{
+												mDNSPlatformMemCopy(ptr, opt->u.owner.password.b, space - DNSOpt_OwnerData_ID_Wake_Space);
+												ptr += space - DNSOpt_OwnerData_ID_Wake_Space;
+												}
+											}
+										break;
+									}
+								}
+							return ptr;
+							}
+
+		case kDNSType_NSEC: {
+							// For our simplified use of NSEC synthetic records:
+							// nextname is always the record's own name,
+							// the block number is always 0,
+							// the count byte is a value in the range 1-32,
+							// followed by the 1-32 data bytes
+							int i, j;
+							for (i=sizeof(rdataNSEC); i>0; i--) if (rdb->nsec.bitmap[i-1]) break;
+							ptr = putDomainNameAsLabels(msg, ptr, limit, rr->name);
+							if (!ptr) return(mDNSNULL);
+							if (i)		// Only put a block if at least one type exists for this name
+								{
+								if (ptr + 2 + i > limit) return(mDNSNULL);
+								*ptr++ = 0;
+								*ptr++ = (mDNSu8)i;
+								for (j=0; j<i; j++) *ptr++ = rdb->nsec.bitmap[j];
+								}
+							return ptr;
+							}
+
+		default:			debugf("putRData: Warning! Writing unknown resource type %d as raw data", rr->rrtype);
+							if (ptr + rr->rdlength > limit) return(mDNSNULL);
+							mDNSPlatformMemCopy(ptr, rdb->data, rr->rdlength);
+							return(ptr + rr->rdlength);
+		}
+	}
+
+#define IsUnicastUpdate(X) (!mDNSOpaque16IsZero((X)->h.id) && ((X)->h.flags.b[0] & kDNSFlag0_OP_Mask) == kDNSFlag0_OP_Update)
+
+mDNSexport mDNSu8 *PutResourceRecordTTLWithLimit(DNSMessage *const msg, mDNSu8 *ptr, mDNSu16 *count, ResourceRecord *rr, mDNSu32 ttl, const mDNSu8 *limit)
+	{
+	mDNSu8 *endofrdata;
+	mDNSu16 actualLength;
+	// When sending SRV to conventional DNS server (i.e. in DNS update requests) we should not do name compression on the rdata (RFC 2782)
+	const DNSMessage *const rdatacompressionbase = (IsUnicastUpdate(msg) && rr->rrtype == kDNSType_SRV) ? mDNSNULL : msg;
+
+	if (rr->RecordType == kDNSRecordTypeUnregistered)
+		{
+		LogMsg("PutResourceRecord ERROR! Attempt to put kDNSRecordTypeUnregistered %##s (%s)", rr->name->c, DNSTypeName(rr->rrtype));
+		return(ptr);
+		}
+
+	if (!ptr) { LogMsg("PutResourceRecordTTLWithLimit ptr is null"); return(mDNSNULL); }
+
+	ptr = putDomainNameAsLabels(msg, ptr, limit, rr->name);
+	if (!ptr || ptr + 10 >= limit) return(mDNSNULL);	// If we're out-of-space, return mDNSNULL
+	ptr[0] = (mDNSu8)(rr->rrtype  >> 8);
+	ptr[1] = (mDNSu8)(rr->rrtype  &  0xFF);
+	ptr[2] = (mDNSu8)(rr->rrclass >> 8);
+	ptr[3] = (mDNSu8)(rr->rrclass &  0xFF);
+	ptr[4] = (mDNSu8)((ttl >> 24) &  0xFF);
+	ptr[5] = (mDNSu8)((ttl >> 16) &  0xFF);
+	ptr[6] = (mDNSu8)((ttl >>  8) &  0xFF);
+	ptr[7] = (mDNSu8)( ttl        &  0xFF);
+	// ptr[8] and ptr[9] filled in *after* we find out how much space the rdata takes
+	
+	endofrdata = putRData(rdatacompressionbase, ptr+10, limit, rr);
+	if (!endofrdata) { verbosedebugf("Ran out of space in PutResourceRecord for %##s (%s)", rr->name->c, DNSTypeName(rr->rrtype)); return(mDNSNULL); }
+
+	// Go back and fill in the actual number of data bytes we wrote
+	// (actualLength can be less than rdlength when domain name compression is used)
+	actualLength = (mDNSu16)(endofrdata - ptr - 10);
+	ptr[8] = (mDNSu8)(actualLength >> 8);
+	ptr[9] = (mDNSu8)(actualLength &  0xFF);
+
+	if (count) (*count)++;
+	else LogMsg("PutResourceRecordTTL: ERROR: No target count to update for %##s (%s)", rr->name->c, DNSTypeName(rr->rrtype));
+	return(endofrdata);
+	}
+
+mDNSlocal mDNSu8 *putEmptyResourceRecord(DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, mDNSu16 *count, const AuthRecord *rr)
+	{
+	ptr = putDomainNameAsLabels(msg, ptr, limit, rr->resrec.name);
+	if (!ptr || ptr + 10 > limit) return(mDNSNULL);		// If we're out-of-space, return mDNSNULL
+	ptr[0] = (mDNSu8)(rr->resrec.rrtype  >> 8);				// Put type
+	ptr[1] = (mDNSu8)(rr->resrec.rrtype  &  0xFF);
+	ptr[2] = (mDNSu8)(rr->resrec.rrclass >> 8);				// Put class
+	ptr[3] = (mDNSu8)(rr->resrec.rrclass &  0xFF);
+	ptr[4] = ptr[5] = ptr[6] = ptr[7] = 0;				// TTL is zero
+	ptr[8] = ptr[9] = 0;								// RDATA length is zero
+	(*count)++;
+	return(ptr + 10);
+	}
+
+mDNSexport mDNSu8 *putQuestion(DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, const domainname *const name, mDNSu16 rrtype, mDNSu16 rrclass)
+	{
+	ptr = putDomainNameAsLabels(msg, ptr, limit, name);
+	if (!ptr || ptr+4 >= limit) return(mDNSNULL);			// If we're out-of-space, return mDNSNULL
+	ptr[0] = (mDNSu8)(rrtype  >> 8);
+	ptr[1] = (mDNSu8)(rrtype  &  0xFF);
+	ptr[2] = (mDNSu8)(rrclass >> 8);
+	ptr[3] = (mDNSu8)(rrclass &  0xFF);
+	msg->h.numQuestions++;
+	return(ptr+4);
+	}
+
+// for dynamic updates
+mDNSexport mDNSu8 *putZone(DNSMessage *const msg, mDNSu8 *ptr, mDNSu8 *limit, const domainname *zone, mDNSOpaque16 zoneClass)
+	{
+	ptr = putDomainNameAsLabels(msg, ptr, limit, zone);
+	if (!ptr || ptr + 4 > limit) return mDNSNULL;		// If we're out-of-space, return NULL
+	*ptr++ = (mDNSu8)(kDNSType_SOA  >> 8);
+	*ptr++ = (mDNSu8)(kDNSType_SOA  &  0xFF);
+	*ptr++ = zoneClass.b[0];
+	*ptr++ = zoneClass.b[1];
+	msg->h.mDNS_numZones++;
+	return ptr;
+	}
+
+// for dynamic updates
+mDNSexport mDNSu8 *putPrereqNameNotInUse(const domainname *const name, DNSMessage *const msg, mDNSu8 *const ptr, mDNSu8 *const end)
+	{
+	AuthRecord prereq;
+	mDNS_SetupResourceRecord(&prereq, mDNSNULL, mDNSInterface_Any, kDNSQType_ANY, kStandardTTL, 0, AuthRecordAny, mDNSNULL, mDNSNULL);
+	AssignDomainName(&prereq.namestorage, name);
+	prereq.resrec.rrtype = kDNSQType_ANY;
+	prereq.resrec.rrclass = kDNSClass_NONE;
+	return putEmptyResourceRecord(msg, ptr, end, &msg->h.mDNS_numPrereqs, &prereq);
+	}
+
+// for dynamic updates
+mDNSexport mDNSu8 *putDeletionRecord(DNSMessage *msg, mDNSu8 *ptr, ResourceRecord *rr)
+	{
+	// deletion: specify record w/ TTL 0, class NONE
+	const mDNSu16 origclass = rr->rrclass;
+	rr->rrclass = kDNSClass_NONE;
+	ptr = PutResourceRecordTTLJumbo(msg, ptr, &msg->h.mDNS_numUpdates, rr, 0);
+	rr->rrclass = origclass;
+	return ptr;
+	}
+
+// for dynamic updates
+mDNSexport mDNSu8 *putDeletionRecordWithLimit(DNSMessage *msg, mDNSu8 *ptr, ResourceRecord *rr, mDNSu8 *limit)
+	{
+	// deletion: specify record w/ TTL 0, class NONE
+	const mDNSu16 origclass = rr->rrclass;
+	rr->rrclass = kDNSClass_NONE;
+	ptr = PutResourceRecordTTLWithLimit(msg, ptr, &msg->h.mDNS_numUpdates, rr, 0, limit);
+	rr->rrclass = origclass;
+	return ptr;
+	}
+
+mDNSexport mDNSu8 *putDeleteRRSetWithLimit(DNSMessage *msg, mDNSu8 *ptr, const domainname *name, mDNSu16 rrtype, mDNSu8 *limit)
+	{
+	mDNSu16 class = kDNSQClass_ANY;
+	
+	ptr = putDomainNameAsLabels(msg, ptr, limit, name);
+	if (!ptr || ptr + 10 >= limit) return mDNSNULL;	// If we're out-of-space, return mDNSNULL
+	ptr[0] = (mDNSu8)(rrtype  >> 8);
+	ptr[1] = (mDNSu8)(rrtype  &  0xFF);
+	ptr[2] = (mDNSu8)(class >> 8);
+	ptr[3] = (mDNSu8)(class &  0xFF);
+	ptr[4] = ptr[5] = ptr[6] = ptr[7] = 0; // zero ttl
+	ptr[8] = ptr[9] = 0; // zero rdlength/rdata
+
+	msg->h.mDNS_numUpdates++;
+	return ptr + 10;
+	}
+
+// for dynamic updates
+mDNSexport mDNSu8 *putDeleteAllRRSets(DNSMessage *msg, mDNSu8 *ptr, const domainname *name)
+	{
+	const mDNSu8 *limit = msg->data + AbsoluteMaxDNSMessageData;
+	mDNSu16 class = kDNSQClass_ANY;
+	mDNSu16 rrtype = kDNSQType_ANY;
+	
+	ptr = putDomainNameAsLabels(msg, ptr, limit, name);
+	if (!ptr || ptr + 10 >= limit) return mDNSNULL;	// If we're out-of-space, return mDNSNULL
+	ptr[0] = (mDNSu8)(rrtype >> 8);
+	ptr[1] = (mDNSu8)(rrtype &  0xFF);
+	ptr[2] = (mDNSu8)(class  >> 8);
+	ptr[3] = (mDNSu8)(class  &  0xFF);
+	ptr[4] = ptr[5] = ptr[6] = ptr[7] = 0; // zero ttl
+	ptr[8] = ptr[9] = 0; // zero rdlength/rdata
+
+	msg->h.mDNS_numUpdates++;
+	return ptr + 10;
+	}
+
+// for dynamic updates
+mDNSexport mDNSu8 *putUpdateLease(DNSMessage *msg, mDNSu8 *end, mDNSu32 lease)
+	{
+	AuthRecord rr;
+	mDNS_SetupResourceRecord(&rr, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
+	rr.resrec.rrclass    = NormalMaxDNSMessageData;
+	rr.resrec.rdlength   = sizeof(rdataOPT);	// One option in this OPT record
+	rr.resrec.rdestimate = sizeof(rdataOPT);
+	rr.resrec.rdata->u.opt[0].opt           = kDNSOpt_Lease;
+	rr.resrec.rdata->u.opt[0].u.updatelease = lease;
+	end = PutResourceRecordTTLJumbo(msg, end, &msg->h.numAdditionals, &rr.resrec, 0);
+	if (!end) { LogMsg("ERROR: putUpdateLease - PutResourceRecordTTL"); return mDNSNULL; }
+	return end;
+	}
+
+// for dynamic updates
+mDNSexport mDNSu8 *putUpdateLeaseWithLimit(DNSMessage *msg, mDNSu8 *end, mDNSu32 lease, mDNSu8 *limit)
+	{
+	AuthRecord rr;
+	mDNS_SetupResourceRecord(&rr, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
+	rr.resrec.rrclass    = NormalMaxDNSMessageData;
+	rr.resrec.rdlength   = sizeof(rdataOPT);	// One option in this OPT record
+	rr.resrec.rdestimate = sizeof(rdataOPT);
+	rr.resrec.rdata->u.opt[0].opt           = kDNSOpt_Lease;
+	rr.resrec.rdata->u.opt[0].u.updatelease = lease;
+	end = PutResourceRecordTTLWithLimit(msg, end, &msg->h.numAdditionals, &rr.resrec, 0, limit);
+	if (!end) { LogMsg("ERROR: putUpdateLease - PutResourceRecordTTLWithLimit"); return mDNSNULL; }
+	return end;
+	}
+
+mDNSexport mDNSu8 *putHINFO(const mDNS *const m, DNSMessage *const msg, mDNSu8 *end, DomainAuthInfo *authInfo, mDNSu8 *limit)
+	{
+	if (authInfo && authInfo->AutoTunnel)
+		{
+		AuthRecord hinfo;
+		mDNSu8 *h = hinfo.rdatastorage.u.data;
+		mDNSu16 len = 2 + m->HIHardware.c[0] + m->HISoftware.c[0];
+		mDNSu8 *newptr;
+		mDNS_SetupResourceRecord(&hinfo, mDNSNULL, mDNSInterface_Any, kDNSType_HINFO, 0, kDNSRecordTypeUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
+		AppendDomainLabel(&hinfo.namestorage, &m->hostlabel);
+		AppendDomainName (&hinfo.namestorage, &authInfo->domain);
+		hinfo.resrec.rroriginalttl = 0;
+		mDNSPlatformMemCopy(h, &m->HIHardware, 1 + (mDNSu32)m->HIHardware.c[0]);
+		h += 1 + (int)h[0];
+		mDNSPlatformMemCopy(h, &m->HISoftware, 1 + (mDNSu32)m->HISoftware.c[0]);
+		hinfo.resrec.rdlength   = len;
+		hinfo.resrec.rdestimate = len;
+		newptr = PutResourceRecordTTLWithLimit(msg, end, &msg->h.numAdditionals, &hinfo.resrec, 0, limit);
+		return newptr;
+		}
+	else
+		return end;
+	}
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - DNS Message Parsing Functions
+#endif
+
+mDNSexport mDNSu32 DomainNameHashValue(const domainname *const name)
+	{
+	mDNSu32 sum = 0;
+	const mDNSu8 *c;
+
+	for (c = name->c; c[0] != 0 && c[1] != 0; c += 2)
+		{
+		sum += ((mDNSIsUpperCase(c[0]) ? c[0] + 'a' - 'A' : c[0]) << 8) |
+				(mDNSIsUpperCase(c[1]) ? c[1] + 'a' - 'A' : c[1]);
+		sum = (sum<<3) | (sum>>29);
+		}
+	if (c[0]) sum += ((mDNSIsUpperCase(c[0]) ? c[0] + 'a' - 'A' : c[0]) << 8);
+	return(sum);
+	}
+
+mDNSexport void SetNewRData(ResourceRecord *const rr, RData *NewRData, mDNSu16 rdlength)
+	{
+	domainname *target;
+	if (NewRData)
+		{
+		rr->rdata    = NewRData;
+		rr->rdlength = rdlength;
+		}
+	// Must not try to get target pointer until after updating rr->rdata
+	target = GetRRDomainNameTarget(rr);
+	rr->rdlength   = GetRDLength(rr, mDNSfalse);
+	rr->rdestimate = GetRDLength(rr, mDNStrue);
+	rr->rdatahash  = target ? DomainNameHashValue(target) : RDataHashValue(rr);
+	}
+
+mDNSexport const mDNSu8 *skipDomainName(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end)
+	{
+	mDNSu16 total = 0;
+
+	if (ptr < (mDNSu8*)msg || ptr >= end)
+		{ debugf("skipDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL); }
+
+	while (1)						// Read sequence of labels
+		{
+		const mDNSu8 len = *ptr++;	// Read length of this label
+		if (len == 0) return(ptr);	// If length is zero, that means this name is complete
+		switch (len & 0xC0)
+			{
+			case 0x00:	if (ptr + len >= end)					// Remember: expect at least one more byte for the root label
+							{ debugf("skipDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL); }
+						if (total + 1 + len >= MAX_DOMAIN_NAME)	// Remember: expect at least one more byte for the root label
+							{ debugf("skipDomainName: Malformed domain name (more than 256 characters)"); return(mDNSNULL); }
+						ptr += len;
+						total += 1 + len;
+						break;
+
+			case 0x40:	debugf("skipDomainName: Extended EDNS0 label types 0x%X not supported", len); return(mDNSNULL);
+			case 0x80:	debugf("skipDomainName: Illegal label length 0x%X", len); return(mDNSNULL);
+			case 0xC0:	return(ptr+1);
+			}
+		}
+	}
+
+// Routine to fetch an FQDN from the DNS message, following compression pointers if necessary.
+mDNSexport const mDNSu8 *getDomainName(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end,
+	domainname *const name)
+	{
+	const mDNSu8 *nextbyte = mDNSNULL;					// Record where we got to before we started following pointers
+	mDNSu8       *np = name->c;							// Name pointer
+	const mDNSu8 *const limit = np + MAX_DOMAIN_NAME;	// Limit so we don't overrun buffer
+
+	if (ptr < (mDNSu8*)msg || ptr >= end)
+		{ debugf("getDomainName: Illegal ptr not within packet boundaries"); return(mDNSNULL); }
+
+	*np = 0;						// Tentatively place the root label here (may be overwritten if we have more labels)
+
+	while (1)						// Read sequence of labels
+		{
+		const mDNSu8 len = *ptr++;	// Read length of this label
+		if (len == 0) break;		// If length is zero, that means this name is complete
+		switch (len & 0xC0)
+			{
+			int i;
+			mDNSu16 offset;
+
+			case 0x00:	if (ptr + len >= end)		// Remember: expect at least one more byte for the root label
+							{ debugf("getDomainName: Malformed domain name (overruns packet end)"); return(mDNSNULL); }
+						if (np + 1 + len >= limit)	// Remember: expect at least one more byte for the root label
+							{ debugf("getDomainName: Malformed domain name (more than 256 characters)"); return(mDNSNULL); }
+						*np++ = len;
+						for (i=0; i<len; i++) *np++ = *ptr++;
+						*np = 0;	// Tentatively place the root label here (may be overwritten if we have more labels)
+						break;
+
+			case 0x40:	debugf("getDomainName: Extended EDNS0 label types 0x%X not supported in name %##s", len, name->c);
+						return(mDNSNULL);
+
+			case 0x80:	debugf("getDomainName: Illegal label length 0x%X in domain name %##s", len, name->c); return(mDNSNULL);
+
+			case 0xC0:	offset = (mDNSu16)((((mDNSu16)(len & 0x3F)) << 8) | *ptr++);
+						if (!nextbyte) nextbyte = ptr;	// Record where we got to before we started following pointers
+						ptr = (mDNSu8 *)msg + offset;
+						if (ptr < (mDNSu8*)msg || ptr >= end)
+							{ debugf("getDomainName: Illegal compression pointer not within packet boundaries"); return(mDNSNULL); }
+						if (*ptr & 0xC0)
+							{ debugf("getDomainName: Compression pointer must point to real label"); return(mDNSNULL); }
+						break;
+			}
+		}
+
+	if (nextbyte) return(nextbyte);
+	else return(ptr);
+	}
+
+mDNSexport const mDNSu8 *skipResourceRecord(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end)
+	{
+	mDNSu16 pktrdlength;
+
+	ptr = skipDomainName(msg, ptr, end);
+	if (!ptr) { debugf("skipResourceRecord: Malformed RR name"); return(mDNSNULL); }
+
+	if (ptr + 10 > end) { debugf("skipResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL); }
+	pktrdlength = (mDNSu16)((mDNSu16)ptr[8] <<  8 | ptr[9]);
+	ptr += 10;
+	if (ptr + pktrdlength > end) { debugf("skipResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL); }
+
+	return(ptr + pktrdlength);
+	}
+
+mDNSexport const mDNSu8 *GetLargeResourceRecord(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *ptr,
+    const mDNSu8 *end, const mDNSInterfaceID InterfaceID, mDNSu8 RecordType, LargeCacheRecord *const largecr)
+	{
+	CacheRecord *const rr = &largecr->r;
+	RDataBody2 *const rdb = (RDataBody2 *)rr->smallrdatastorage.data;
+	mDNSu16 pktrdlength;
+	
+	if (largecr == &m->rec && m->rec.r.resrec.RecordType)
+		{
+		LogMsg("GetLargeResourceRecord: m->rec appears to be already in use for %s", CRDisplayString(m, &m->rec.r));
+#if ForceAlerts
+		*(long*)0 = 0;
+#endif
+		}
+
+	rr->next              = mDNSNULL;
+	rr->resrec.name       = &largecr->namestorage;
+
+	rr->NextInKAList      = mDNSNULL;
+	rr->TimeRcvd          = m ? m->timenow : 0;
+	rr->DelayDelivery     = 0;
+	rr->NextRequiredQuery = m ? m->timenow : 0;		// Will be updated to the real value when we call SetNextCacheCheckTimeForRecord()
+	rr->LastUsed          = m ? m->timenow : 0;
+	rr->CRActiveQuestion  = mDNSNULL;
+	rr->UnansweredQueries = 0;
+	rr->LastUnansweredTime= 0;
+#if ENABLE_MULTI_PACKET_QUERY_SNOOPING
+	rr->MPUnansweredQ     = 0;
+	rr->MPLastUnansweredQT= 0;
+	rr->MPUnansweredKA    = 0;
+	rr->MPExpectingKA     = mDNSfalse;
+#endif
+	rr->NextInCFList      = mDNSNULL;
+
+	rr->resrec.InterfaceID       = InterfaceID;
+	rr->resrec.rDNSServer = mDNSNULL;
+
+	ptr = getDomainName(msg, ptr, end, &largecr->namestorage);		// Will bail out correctly if ptr is NULL
+	if (!ptr) { debugf("GetLargeResourceRecord: Malformed RR name"); return(mDNSNULL); }
+	rr->resrec.namehash = DomainNameHashValue(rr->resrec.name);
+
+	if (ptr + 10 > end) { debugf("GetLargeResourceRecord: Malformed RR -- no type/class/ttl/len!"); return(mDNSNULL); }
+
+	rr->resrec.rrtype            = (mDNSu16) ((mDNSu16)ptr[0] <<  8 | ptr[1]);
+	rr->resrec.rrclass           = (mDNSu16)(((mDNSu16)ptr[2] <<  8 | ptr[3]) & kDNSClass_Mask);
+	rr->resrec.rroriginalttl     = (mDNSu32) ((mDNSu32)ptr[4] << 24 | (mDNSu32)ptr[5] << 16 | (mDNSu32)ptr[6] << 8 | ptr[7]);
+	if (rr->resrec.rroriginalttl > 0x70000000UL / mDNSPlatformOneSecond && (mDNSs32)rr->resrec.rroriginalttl != -1)
+		rr->resrec.rroriginalttl = 0x70000000UL / mDNSPlatformOneSecond;
+	// Note: We don't have to adjust m->NextCacheCheck here -- this is just getting a record into memory for
+	// us to look at. If we decide to copy it into the cache, then we'll update m->NextCacheCheck accordingly.
+	pktrdlength           = (mDNSu16)((mDNSu16)ptr[8] <<  8 | ptr[9]);
+
+	// If mDNS record has cache-flush bit set, we mark it unique
+	// For uDNS records, all are implicitly deemed unique (a single DNS server is always
+	// authoritative for the entire RRSet), unless this is a truncated response
+	if (ptr[2] & (kDNSClass_UniqueRRSet >> 8) || (!InterfaceID && !(msg->h.flags.b[0] & kDNSFlag0_TC)))
+		RecordType |= kDNSRecordTypePacketUniqueMask;
+	ptr += 10;
+	if (ptr + pktrdlength > end) { debugf("GetLargeResourceRecord: RDATA exceeds end of packet"); return(mDNSNULL); }
+	end = ptr + pktrdlength;		// Adjust end to indicate the end of the rdata for this resource record
+
+	rr->resrec.rdata = (RData*)&rr->smallrdatastorage;
+	rr->resrec.rdata->MaxRDLength = MaximumRDSize;
+
+	if (!RecordType) LogMsg("GetLargeResourceRecord: No RecordType for %##s", rr->resrec.name->c);
+
+	// IMPORTANT: Any record type we understand and unpack into a structure containing domainnames needs to have corresponding
+	// cases in SameRDataBody() and RDataHashValue() to do a semantic comparison (or checksum) of the structure instead of a blind
+	// bitwise memory compare (or sum). This is because a domainname is a fixed size structure holding variable-length data.
+	// Any bytes past the logical end of the name are undefined, and a blind bitwise memory compare may indicate that
+	// two domainnames are different when semantically they are the same name and it's only the unused bytes that differ.
+	if (rr->resrec.rrclass == kDNSQClass_ANY && pktrdlength == 0)	// Used in update packets to mean "Delete An RRset" (RFC 2136)
+		rr->resrec.rdlength = 0;
+	else switch (rr->resrec.rrtype)
+		{
+		case kDNSType_A:	if (pktrdlength != sizeof(mDNSv4Addr)) goto fail;
+							rdb->ipv4.b[0] = ptr[0];
+							rdb->ipv4.b[1] = ptr[1];
+							rdb->ipv4.b[2] = ptr[2];
+							rdb->ipv4.b[3] = ptr[3];
+							break;
+
+		case kDNSType_NS:
+		case kDNSType_CNAME:
+		case kDNSType_PTR:
+		case kDNSType_DNAME:ptr = getDomainName(msg, ptr, end, &rdb->name);
+							if (ptr != end) { debugf("GetLargeResourceRecord: Malformed CNAME/PTR RDATA name"); goto fail; }
+							//debugf("%##s PTR %##s rdlen %d", rr->resrec.name.c, rdb->name.c, pktrdlength);
+							break;
+
+		case kDNSType_SOA:  ptr = getDomainName(msg, ptr, end, &rdb->soa.mname);
+							if (!ptr)              { debugf("GetLargeResourceRecord: Malformed SOA RDATA mname"); goto fail; }
+							ptr = getDomainName(msg, ptr, end, &rdb->soa.rname);
+							if (!ptr)              { debugf("GetLargeResourceRecord: Malformed SOA RDATA rname"); goto fail; }
+							if (ptr + 0x14 != end) { debugf("GetLargeResourceRecord: Malformed SOA RDATA");       goto fail; }
+							rdb->soa.serial  = (mDNSs32) ((mDNSs32)ptr[0x00] << 24 | (mDNSs32)ptr[0x01] << 16 | (mDNSs32)ptr[0x02] << 8 | ptr[0x03]);
+							rdb->soa.refresh = (mDNSu32) ((mDNSu32)ptr[0x04] << 24 | (mDNSu32)ptr[0x05] << 16 | (mDNSu32)ptr[0x06] << 8 | ptr[0x07]);
+							rdb->soa.retry   = (mDNSu32) ((mDNSu32)ptr[0x08] << 24 | (mDNSu32)ptr[0x09] << 16 | (mDNSu32)ptr[0x0A] << 8 | ptr[0x0B]);
+							rdb->soa.expire  = (mDNSu32) ((mDNSu32)ptr[0x0C] << 24 | (mDNSu32)ptr[0x0D] << 16 | (mDNSu32)ptr[0x0E] << 8 | ptr[0x0F]);
+							rdb->soa.min     = (mDNSu32) ((mDNSu32)ptr[0x10] << 24 | (mDNSu32)ptr[0x11] << 16 | (mDNSu32)ptr[0x12] << 8 | ptr[0x13]);
+							break;
+
+		case kDNSType_NULL:
+		case kDNSType_HINFO:
+		case kDNSType_TSIG:
+		case kDNSType_TXT:
+		case kDNSType_X25:
+		case kDNSType_ISDN:
+		case kDNSType_LOC:
+		case kDNSType_DHCID:if (pktrdlength > rr->resrec.rdata->MaxRDLength)
+								{
+								debugf("GetLargeResourceRecord: %s rdata size (%d) exceeds storage (%d)",
+									DNSTypeName(rr->resrec.rrtype), pktrdlength, rr->resrec.rdata->MaxRDLength);
+								goto fail;
+								}
+							rr->resrec.rdlength = pktrdlength;
+							mDNSPlatformMemCopy(rdb->data, ptr, pktrdlength);
+							break;
+
+		case kDNSType_MX:
+		case kDNSType_AFSDB:
+		case kDNSType_RT:
+		case kDNSType_KX:	if (pktrdlength < 3) goto fail;	// Preference + domainname
+							rdb->mx.preference = (mDNSu16)((mDNSu16)ptr[0] <<  8 | ptr[1]);
+							ptr = getDomainName(msg, ptr+2, end, &rdb->mx.exchange);
+							if (ptr != end) { debugf("GetLargeResourceRecord: Malformed MX name"); goto fail; }
+							//debugf("%##s SRV %##s rdlen %d", rr->resrec.name.c, rdb->srv.target.c, pktrdlength);
+							break;
+
+		case kDNSType_RP:	ptr = getDomainName(msg, ptr, end, &rdb->rp.mbox);	// Domainname + domainname
+							if (!ptr)       { debugf("GetLargeResourceRecord: Malformed RP mbox"); goto fail; }
+							ptr = getDomainName(msg, ptr, end, &rdb->rp.txt);
+							if (ptr != end) { debugf("GetLargeResourceRecord: Malformed RP txt"); goto fail; }
+							break;
+
+		case kDNSType_PX:	if (pktrdlength < 4) goto fail;	// Preference + domainname + domainname
+							rdb->px.preference = (mDNSu16)((mDNSu16)ptr[0] <<  8 | ptr[1]);
+							ptr = getDomainName(msg, ptr, end, &rdb->px.map822);
+							if (!ptr)       { debugf("GetLargeResourceRecord: Malformed PX map822"); goto fail; }
+							ptr = getDomainName(msg, ptr, end, &rdb->px.mapx400);
+							if (ptr != end) { debugf("GetLargeResourceRecord: Malformed PX mapx400"); goto fail; }
+							break;
+
+		case kDNSType_AAAA:	if (pktrdlength != sizeof(mDNSv6Addr)) goto fail;
+							mDNSPlatformMemCopy(&rdb->ipv6, ptr, sizeof(rdb->ipv6));
+							break;
+
+		case kDNSType_SRV:	if (pktrdlength < 7) goto fail;	// Priority + weight + port + domainname
+							rdb->srv.priority = (mDNSu16)((mDNSu16)ptr[0] <<  8 | ptr[1]);
+							rdb->srv.weight   = (mDNSu16)((mDNSu16)ptr[2] <<  8 | ptr[3]);
+							rdb->srv.port.b[0] = ptr[4];
+							rdb->srv.port.b[1] = ptr[5];
+							ptr = getDomainName(msg, ptr+6, end, &rdb->srv.target);
+							if (ptr != end) { debugf("GetLargeResourceRecord: Malformed SRV RDATA name"); goto fail; }
+							//debugf("%##s SRV %##s rdlen %d", rr->resrec.name.c, rdb->srv.target.c, pktrdlength);
+							break;
+
+		case kDNSType_OPT:	{
+							rdataOPT *opt = rr->resrec.rdata->u.opt;
+							rr->resrec.rdlength = 0;
+							while (ptr < end && (mDNSu8 *)(opt+1) < &rr->resrec.rdata->u.data[MaximumRDSize])
+								{
+								const rdataOPT *const currentopt = opt;
+								if (ptr + 4 > end) { LogInfo("GetLargeResourceRecord: OPT RDATA ptr + 4 > end"); goto fail; }
+								opt->opt    = (mDNSu16)((mDNSu16)ptr[0] <<  8 | ptr[1]);
+								opt->optlen = (mDNSu16)((mDNSu16)ptr[2] <<  8 | ptr[3]);
+								ptr += 4;
+								if (ptr + opt->optlen > end) { LogInfo("GetLargeResourceRecord: ptr + opt->optlen > end"); goto fail; }
+								switch (opt->opt)
+									{
+									case kDNSOpt_LLQ:
+										if (opt->optlen == DNSOpt_LLQData_Space - 4)
+											{
+											opt->u.llq.vers  = (mDNSu16)((mDNSu16)ptr[0] <<  8 | ptr[1]);
+											opt->u.llq.llqOp = (mDNSu16)((mDNSu16)ptr[2] <<  8 | ptr[3]);
+											opt->u.llq.err   = (mDNSu16)((mDNSu16)ptr[4] <<  8 | ptr[5]);
+											mDNSPlatformMemCopy(opt->u.llq.id.b, ptr+6, 8);
+											opt->u.llq.llqlease = (mDNSu32) ((mDNSu32)ptr[14] << 24 | (mDNSu32)ptr[15] << 16 | (mDNSu32)ptr[16] << 8 | ptr[17]);
+											if (opt->u.llq.llqlease > 0x70000000UL / mDNSPlatformOneSecond)
+												opt->u.llq.llqlease = 0x70000000UL / mDNSPlatformOneSecond;
+											opt++;
+											}
+										break;
+									case kDNSOpt_Lease:
+										if (opt->optlen == DNSOpt_LeaseData_Space - 4)
+											{
+											opt->u.updatelease = (mDNSu32) ((mDNSu32)ptr[0] << 24 | (mDNSu32)ptr[1] << 16 | (mDNSu32)ptr[2] << 8 | ptr[3]);
+											if (opt->u.updatelease > 0x70000000UL / mDNSPlatformOneSecond)
+												opt->u.updatelease = 0x70000000UL / mDNSPlatformOneSecond;
+											opt++;
+											}
+										break;
+									case kDNSOpt_Owner:
+										if (ValidOwnerLength(opt->optlen))
+											{
+											opt->u.owner.vers = ptr[0];
+											opt->u.owner.seq  = ptr[1];
+											mDNSPlatformMemCopy(opt->u.owner.HMAC.b, ptr+2, 6);		// 6-byte MAC address
+											mDNSPlatformMemCopy(opt->u.owner.IMAC.b, ptr+2, 6);		// 6-byte MAC address
+											opt->u.owner.password = zeroEthAddr;
+											if (opt->optlen >= DNSOpt_OwnerData_ID_Wake_Space-4)
+												{
+												mDNSPlatformMemCopy(opt->u.owner.IMAC.b, ptr+8, 6);	// 6-byte MAC address
+												// This mDNSPlatformMemCopy is safe because the ValidOwnerLength(opt->optlen) check above
+												// ensures that opt->optlen is no more than DNSOpt_OwnerData_ID_Wake_PW6_Space - 4
+												if (opt->optlen > DNSOpt_OwnerData_ID_Wake_Space-4)
+													mDNSPlatformMemCopy(opt->u.owner.password.b, ptr+14, opt->optlen - (DNSOpt_OwnerData_ID_Wake_Space-4));
+												}
+											opt++;
+											}
+										break;
+									}
+								ptr += currentopt->optlen;
+								}
+							rr->resrec.rdlength = (mDNSu16)((mDNSu8*)opt - rr->resrec.rdata->u.data);
+							if (ptr != end) { LogInfo("GetLargeResourceRecord: Malformed OptRdata"); goto fail; }
+							break;
+							}
+
+		case kDNSType_NSEC: {
+							unsigned int i, j;
+							domainname d;
+							ptr = getDomainName(msg, ptr, end, &d);		// Ignored for our simplified use of NSEC synthetic records
+							if (!ptr) { LogInfo("GetLargeResourceRecord: Malformed NSEC nextname"); goto fail; }
+							mDNSPlatformMemZero(rdb->nsec.bitmap, sizeof(rdb->nsec.bitmap));
+							if (ptr < end)
+								{
+								if (*ptr++ != 0) { debugf("GetLargeResourceRecord: We only handle block zero NSECs"); goto fail; }
+								i = *ptr++;
+								if (i > sizeof(rdataNSEC)) { debugf("GetLargeResourceRecord: invalid block length %d", i); goto fail; }
+								for (j=0; j<i; j++) rdb->nsec.bitmap[j] = *ptr++;
+								}
+							if (ptr != end) { debugf("GetLargeResourceRecord: Malformed NSEC"); goto fail; }
+							break;
+							}
+
+		default:			if (pktrdlength > rr->resrec.rdata->MaxRDLength)
+								{
+								debugf("GetLargeResourceRecord: rdata %d (%s) size (%d) exceeds storage (%d)",
+									rr->resrec.rrtype, DNSTypeName(rr->resrec.rrtype), pktrdlength, rr->resrec.rdata->MaxRDLength);
+								goto fail;
+								}
+							debugf("GetLargeResourceRecord: Warning! Reading resource type %d (%s) as opaque data",
+								rr->resrec.rrtype, DNSTypeName(rr->resrec.rrtype));
+							// Note: Just because we don't understand the record type, that doesn't
+							// mean we fail. The DNS protocol specifies rdlength, so we can
+							// safely skip over unknown records and ignore them.
+							// We also grab a binary copy of the rdata anyway, since the caller
+							// might know how to interpret it even if we don't.
+							rr->resrec.rdlength = pktrdlength;
+							mDNSPlatformMemCopy(rdb->data, ptr, pktrdlength);
+							break;
+		}
+
+	SetNewRData(&rr->resrec, mDNSNULL, 0);		// Sets rdlength, rdestimate, rdatahash for us
+
+	// Success! Now fill in RecordType to show this record contains valid data
+	rr->resrec.RecordType = RecordType;
+	return(end);
+
+fail:
+	// If we were unable to parse the rdata in this record, we indicate that by
+	// returing a 'kDNSRecordTypePacketNegative' record with rdlength set to zero
+	rr->resrec.RecordType = kDNSRecordTypePacketNegative;
+	rr->resrec.rdlength   = 0;
+	rr->resrec.rdestimate = 0;
+	rr->resrec.rdatahash  = 0;
+	return(end);
+	}
+
+mDNSexport const mDNSu8 *skipQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end)
+	{
+	ptr = skipDomainName(msg, ptr, end);
+	if (!ptr) { debugf("skipQuestion: Malformed domain name in DNS question section"); return(mDNSNULL); }
+	if (ptr+4 > end) { debugf("skipQuestion: Malformed DNS question section -- no query type and class!"); return(mDNSNULL); }
+	return(ptr+4);
+	}
+
+mDNSexport const mDNSu8 *getQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end, const mDNSInterfaceID InterfaceID,
+	DNSQuestion *question)
+	{
+	mDNSPlatformMemZero(question, sizeof(*question));
+	question->InterfaceID = InterfaceID;
+	if (!InterfaceID) question->TargetQID = onesID;	// In DNSQuestions we use TargetQID as the indicator of whether it's unicast or multicast
+	ptr = getDomainName(msg, ptr, end, &question->qname);
+	if (!ptr) { debugf("Malformed domain name in DNS question section"); return(mDNSNULL); }
+	if (ptr+4 > end) { debugf("Malformed DNS question section -- no query type and class!"); return(mDNSNULL); }
+
+	question->qnamehash = DomainNameHashValue(&question->qname);
+	question->qtype  = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]);			// Get type
+	question->qclass = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]);			// and class
+	return(ptr+4);
+	}
+
+mDNSexport const mDNSu8 *LocateAnswers(const DNSMessage *const msg, const mDNSu8 *const end)
+	{
+	int i;
+	const mDNSu8 *ptr = msg->data;
+	for (i = 0; i < msg->h.numQuestions && ptr; i++) ptr = skipQuestion(msg, ptr, end);
+	return(ptr);
+	}
+
+mDNSexport const mDNSu8 *LocateAuthorities(const DNSMessage *const msg, const mDNSu8 *const end)
+	{
+	int i;
+	const mDNSu8 *ptr = LocateAnswers(msg, end);
+	for (i = 0; i < msg->h.numAnswers && ptr; i++) ptr = skipResourceRecord(msg, ptr, end);
+	return(ptr);
+	}
+
+mDNSexport const mDNSu8 *LocateAdditionals(const DNSMessage *const msg, const mDNSu8 *const end)
+	{
+	int i;
+	const mDNSu8 *ptr = LocateAuthorities(msg, end);
+	for (i = 0; i < msg->h.numAuthorities; i++) ptr = skipResourceRecord(msg, ptr, end);
+	return (ptr);
+	}
+
+mDNSexport const mDNSu8 *LocateOptRR(const DNSMessage *const msg, const mDNSu8 *const end, int minsize)
+	{
+	int i;
+	const mDNSu8 *ptr = LocateAdditionals(msg, end);
+
+	// Locate the OPT record.
+	// According to RFC 2671, "One OPT pseudo-RR can be added to the additional data section of either a request or a response."
+	// This implies that there may be *at most* one OPT record per DNS message, in the Additional Section,
+	// but not necessarily the *last* entry in the Additional Section.
+	for (i = 0; ptr && i < msg->h.numAdditionals; i++)
+		{
+		if (ptr + DNSOpt_Header_Space + minsize <= end &&	// Make sure we have 11+minsize bytes of data
+			ptr[0] == 0                                &&	// Name must be root label
+			ptr[1] == (kDNSType_OPT >> 8  )            &&	// rrtype OPT
+			ptr[2] == (kDNSType_OPT & 0xFF)            &&
+			((mDNSu16)ptr[9] << 8 | (mDNSu16)ptr[10]) >= (mDNSu16)minsize)
+			return(ptr);
+		else
+			ptr = skipResourceRecord(msg, ptr, end);
+		}
+	return(mDNSNULL);
+	}
+
+// On success, GetLLQOptData returns pointer to storage within shared "m->rec";
+// it is caller's responsibilty to clear m->rec.r.resrec.RecordType after use
+// Note: An OPT RDataBody actually contains one or more variable-length rdataOPT objects packed together
+// The code that currently calls this assumes there's only one, instead of iterating through the set
+mDNSexport const rdataOPT *GetLLQOptData(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end)
+	{
+	const mDNSu8 *ptr = LocateOptRR(msg, end, DNSOpt_LLQData_Space);
+	if (ptr)
+		{
+		ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &m->rec);
+		if (ptr && m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative) return(&m->rec.r.resrec.rdata->u.opt[0]);
+		}
+	return(mDNSNULL);
+	}
+
+// Get the lease life of records in a dynamic update
+// returns 0 on error or if no lease present
+mDNSexport mDNSu32 GetPktLease(mDNS *m, DNSMessage *msg, const mDNSu8 *end)
+	{
+	mDNSu32 result = 0;
+	const mDNSu8 *ptr = LocateOptRR(msg, end, DNSOpt_LeaseData_Space);
+	if (ptr) ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &m->rec);
+	if (ptr && m->rec.r.resrec.rdlength >= DNSOpt_LeaseData_Space && m->rec.r.resrec.rdata->u.opt[0].opt == kDNSOpt_Lease)
+		result = m->rec.r.resrec.rdata->u.opt[0].u.updatelease;
+	m->rec.r.resrec.RecordType = 0;		// Clear RecordType to show we're not still using it
+	return(result);
+	}
+
+mDNSlocal const mDNSu8 *DumpRecords(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end, int count, char *label)
+	{
+	int i;
+	LogMsg("%2d %s", count, label);
+	for (i = 0; i < count && ptr; i++)
+		{
+		// This puts a LargeCacheRecord on the stack instead of using the shared m->rec storage,
+		// but since it's only used for debugging (and probably only on OS X, not on
+		// embedded systems) putting a 9kB object on the stack isn't a big problem.
+		LargeCacheRecord largecr;
+		ptr = GetLargeResourceRecord(m, msg, ptr, end, mDNSInterface_Any, kDNSRecordTypePacketAns, &largecr);
+		if (ptr) LogMsg("%2d TTL%8d %s", i, largecr.r.resrec.rroriginalttl, CRDisplayString(m, &largecr.r));
+		}
+	if (!ptr) LogMsg("ERROR: Premature end of packet data");
+	return(ptr);
+	}
+
+#define DNS_OP_Name(X) (                              \
+	(X) == kDNSFlag0_OP_StdQuery ? ""         :       \
+	(X) == kDNSFlag0_OP_Iquery   ? "Iquery "  :       \
+	(X) == kDNSFlag0_OP_Status   ? "Status "  :       \
+	(X) == kDNSFlag0_OP_Unused3  ? "Unused3 " :       \
+	(X) == kDNSFlag0_OP_Notify   ? "Notify "  :       \
+	(X) == kDNSFlag0_OP_Update   ? "Update "  : "?? " )
+
+#define DNS_RC_Name(X) (                             \
+	(X) == kDNSFlag1_RC_NoErr    ? "NoErr"    :      \
+	(X) == kDNSFlag1_RC_FormErr  ? "FormErr"  :      \
+	(X) == kDNSFlag1_RC_ServFail ? "ServFail" :      \
+	(X) == kDNSFlag1_RC_NXDomain ? "NXDomain" :      \
+	(X) == kDNSFlag1_RC_NotImpl  ? "NotImpl"  :      \
+	(X) == kDNSFlag1_RC_Refused  ? "Refused"  :      \
+	(X) == kDNSFlag1_RC_YXDomain ? "YXDomain" :      \
+	(X) == kDNSFlag1_RC_YXRRSet  ? "YXRRSet"  :      \
+	(X) == kDNSFlag1_RC_NXRRSet  ? "NXRRSet"  :      \
+	(X) == kDNSFlag1_RC_NotAuth  ? "NotAuth"  :      \
+	(X) == kDNSFlag1_RC_NotZone  ? "NotZone"  : "??" )
+
+// Note: DumpPacket expects the packet header fields in host byte order, not network byte order
+mDNSexport void DumpPacket(mDNS *const m, mStatus status, mDNSBool sent, char *transport,
+	const mDNSAddr *srcaddr, mDNSIPPort srcport,
+	const mDNSAddr *dstaddr, mDNSIPPort dstport, const DNSMessage *const msg, const mDNSu8 *const end)
+	{
+	mDNSBool IsUpdate = ((msg->h.flags.b[0] & kDNSFlag0_OP_Mask) == kDNSFlag0_OP_Update);
+	const mDNSu8 *ptr = msg->data;
+	int i;
+	DNSQuestion q;
+	char tbuffer[64], sbuffer[64], dbuffer[64] = "";
+	if (!status) tbuffer[mDNS_snprintf(tbuffer, sizeof(tbuffer), sent ? "Sent" : "Received"                        )] = 0;
+	else         tbuffer[mDNS_snprintf(tbuffer, sizeof(tbuffer), "ERROR %d %sing", status, sent ? "Send" : "Receiv")] = 0;
+	if (sent) sbuffer[mDNS_snprintf(sbuffer, sizeof(sbuffer), "port "        )] = 0;
+	else      sbuffer[mDNS_snprintf(sbuffer, sizeof(sbuffer), "%#a:", srcaddr)] = 0;
+	if (dstaddr || !mDNSIPPortIsZero(dstport))
+		dbuffer[mDNS_snprintf(dbuffer, sizeof(dbuffer), " to %#a:%d", dstaddr, mDNSVal16(dstport))] = 0;
+
+	LogMsg("-- %s %s DNS %s%s (flags %02X%02X) RCODE: %s (%d) %s%s%s%s%s%sID: %d %d bytes from %s%d%s%s --",
+		tbuffer, transport,
+		DNS_OP_Name(msg->h.flags.b[0] & kDNSFlag0_OP_Mask),
+		msg->h.flags.b[0] & kDNSFlag0_QR_Response ? "Response" : "Query",
+		msg->h.flags.b[0], msg->h.flags.b[1],
+		DNS_RC_Name(msg->h.flags.b[1] & kDNSFlag1_RC_Mask),
+		msg->h.flags.b[1] & kDNSFlag1_RC_Mask,
+		msg->h.flags.b[0] & kDNSFlag0_AA ? "AA " : "",
+		msg->h.flags.b[0] & kDNSFlag0_TC ? "TC " : "",
+		msg->h.flags.b[0] & kDNSFlag0_RD ? "RD " : "",
+		msg->h.flags.b[1] & kDNSFlag1_RA ? "RA " : "",
+		msg->h.flags.b[1] & kDNSFlag1_AD ? "AD " : "",
+		msg->h.flags.b[1] & kDNSFlag1_CD ? "CD " : "",
+		mDNSVal16(msg->h.id),
+		end - msg->data,
+		sbuffer, mDNSVal16(srcport), dbuffer,
+		(msg->h.flags.b[0] & kDNSFlag0_TC) ? " (truncated)" : ""
+		);
+
+	LogMsg("%2d %s", msg->h.numQuestions, IsUpdate ? "Zone" : "Questions");
+	for (i = 0; i < msg->h.numQuestions && ptr; i++)
+		{
+		ptr = getQuestion(msg, ptr, end, mDNSInterface_Any, &q);
+		if (ptr) LogMsg("%2d %##s %s", i, q.qname.c, DNSTypeName(q.qtype));
+		}
+	ptr = DumpRecords(m, msg, ptr, end, msg->h.numAnswers,     IsUpdate ? "Prerequisites" : "Answers");
+	ptr = DumpRecords(m, msg, ptr, end, msg->h.numAuthorities, IsUpdate ? "Updates"       : "Authorities");
+	ptr = DumpRecords(m, msg, ptr, end, msg->h.numAdditionals, "Additionals");
+	LogMsg("--------------");
+	}
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - Packet Sending Functions
+#endif
+
+// Stub definition of TCPSocket_struct so we can access flags field. (Rest of TCPSocket_struct is platform-dependent.)
+struct TCPSocket_struct { TCPSocketFlags flags; /* ... */ };
+
+struct UDPSocket_struct
+	{
+	mDNSIPPort port; // MUST BE FIRST FIELD -- mDNSCoreReceive expects every UDPSocket_struct to begin with mDNSIPPort port
+	};
+
+// Note: When we sign a DNS message using DNSDigest_SignMessage(), the current real-time clock value is used, which
+// is why we generally defer signing until we send the message, to ensure the signature is as fresh as possible.
+mDNSexport mStatus mDNSSendDNSMessage(mDNS *const m, DNSMessage *const msg, mDNSu8 *end,
+    mDNSInterfaceID InterfaceID, UDPSocket *src, const mDNSAddr *dst, mDNSIPPort dstport, TCPSocket *sock, DomainAuthInfo *authInfo)
+	{
+	mStatus status = mStatus_NoError;
+	const mDNSu16 numAdditionals = msg->h.numAdditionals;
+	mDNSu8 *newend;
+	mDNSu8 *limit = msg->data + AbsoluteMaxDNSMessageData;
+
+	// Zero-length message data is okay (e.g. for a DNS Update ack, where all we need is an ID and an error code
+	if (end < msg->data || end - msg->data > AbsoluteMaxDNSMessageData)
+		{
+		LogMsg("mDNSSendDNSMessage: invalid message %p %p %d", msg->data, end, end - msg->data);
+		return mStatus_BadParamErr;
+		}
+
+	newend = putHINFO(m, msg, end, authInfo, limit);
+	if (!newend) LogMsg("mDNSSendDNSMessage: putHINFO failed msg %p end %p, limit %p", msg->data, end, limit); // Not fatal
+	else end = newend;
+	
+	// Put all the integer values in IETF byte-order (MSB first, LSB second)
+	SwapDNSHeaderBytes(msg);
+	
+	if (authInfo) DNSDigest_SignMessage(msg, &end, authInfo, 0);	// DNSDigest_SignMessage operates on message in network byte order
+	if (!end) { LogMsg("mDNSSendDNSMessage: DNSDigest_SignMessage failed"); status = mStatus_NoMemoryErr; }
+	else
+		{
+		// Send the packet on the wire
+		if (!sock)
+			status = mDNSPlatformSendUDP(m, msg, end, InterfaceID, src, dst, dstport);
+		else
+			{
+			mDNSu16 msglen = (mDNSu16)(end - (mDNSu8 *)msg);
+			mDNSu8 lenbuf[2] = { (mDNSu8)(msglen >> 8), (mDNSu8)(msglen & 0xFF) };
+			long nsent = mDNSPlatformWriteTCP(sock, (char*)lenbuf, 2);		// Should do scatter/gather here -- this is probably going out as two packets
+			if (nsent != 2) { LogMsg("mDNSSendDNSMessage: write msg length failed %d/%d", nsent, 2); status = mStatus_ConnFailed; }
+			else
+				{
+				nsent = mDNSPlatformWriteTCP(sock, (char *)msg, msglen);
+				if (nsent != msglen) { LogMsg("mDNSSendDNSMessage: write msg body failed %d/%d", nsent, msglen); status = mStatus_ConnFailed; }
+				}
+			}
+		}
+
+	// Swap the integer values back the way they were (remember that numAdditionals may have been changed by putHINFO and/or SignMessage)
+	SwapDNSHeaderBytes(msg);
+
+	// Dump the packet with the HINFO and TSIG
+	if (mDNS_PacketLoggingEnabled && !mDNSOpaque16IsZero(msg->h.id))
+		DumpPacket(m, status, mDNStrue, sock && (sock->flags & kTCPSocketFlags_UseTLS) ? "TLS" : sock ? "TCP" : "UDP", mDNSNULL, src ? src->port : MulticastDNSPort, dst, dstport, msg, end);
+
+	// put the number of additionals back the way it was
+	msg->h.numAdditionals = numAdditionals;
+
+	return(status);
+	}
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - RR List Management & Task Management
+#endif
+
+mDNSexport void mDNS_Lock_(mDNS *const m, const char * const functionname)
+	{
+	// MUST grab the platform lock FIRST!
+	mDNSPlatformLock(m);
+
+	// Normally, mDNS_reentrancy is zero and so is mDNS_busy
+	// However, when we call a client callback mDNS_busy is one, and we increment mDNS_reentrancy too
+	// If that client callback does mDNS API calls, mDNS_reentrancy and mDNS_busy will both be one
+	// If mDNS_busy != mDNS_reentrancy that's a bad sign
+	if (m->mDNS_busy != m->mDNS_reentrancy)
+		{
+		LogMsg("%s: mDNS_Lock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", functionname, m->mDNS_busy, m->mDNS_reentrancy);
+#if ForceAlerts
+		*(long*)0 = 0;
+#endif
+		}
+
+	// If this is an initial entry into the mDNSCore code, set m->timenow
+	// else, if this is a re-entrant entry into the mDNSCore code, m->timenow should already be set
+	if (m->mDNS_busy == 0)
+		{
+		if (m->timenow)
+			LogMsg("%s: mDNS_Lock: m->timenow already set (%ld/%ld)", functionname, m->timenow, mDNS_TimeNow_NoLock(m));
+		m->timenow = mDNS_TimeNow_NoLock(m);
+		if (m->timenow == 0) m->timenow = 1;
+		}
+	else if (m->timenow == 0)
+		{
+		LogMsg("%s: mDNS_Lock: m->mDNS_busy is %ld but m->timenow not set", functionname, m->mDNS_busy);
+		m->timenow = mDNS_TimeNow_NoLock(m);
+		if (m->timenow == 0) m->timenow = 1;
+		}
+
+	if (m->timenow_last - m->timenow > 0)
+		{
+		m->timenow_adjust += m->timenow_last - m->timenow;
+		LogMsg("%s: mDNSPlatformRawTime went backwards by %ld ticks; setting correction factor to %ld", functionname, m->timenow_last - m->timenow, m->timenow_adjust);
+		m->timenow = m->timenow_last;
+		}
+	m->timenow_last = m->timenow;
+
+	// Increment mDNS_busy so we'll recognise re-entrant calls
+	m->mDNS_busy++;
+	}
+
+mDNSlocal AuthRecord *AnyLocalRecordReady(const mDNS *const m)
+	{
+	AuthRecord *rr;
+	for (rr = m->NewLocalRecords; rr; rr = rr->next)
+		if (LocalRecordReady(rr)) return rr;
+	return mDNSNULL;
+	}
+
+mDNSlocal mDNSs32 GetNextScheduledEvent(const mDNS *const m)
+	{
+	mDNSs32 e = m->timenow + 0x78000000;
+	if (m->mDNSPlatformStatus != mStatus_NoError) return(e);
+	if (m->NewQuestions)
+		{
+		if (m->NewQuestions->DelayAnswering) e = m->NewQuestions->DelayAnswering;
+		else return(m->timenow);
+		}
+	if (m->NewLocalOnlyQuestions)                     return(m->timenow);
+	if (m->NewLocalRecords && AnyLocalRecordReady(m)) return(m->timenow);
+	if (m->NewLocalOnlyRecords)                       return(m->timenow);
+	if (m->SPSProxyListChanged)                       return(m->timenow);
+	if (m->LocalRemoveEvents)                         return(m->timenow);
+
+#ifndef UNICAST_DISABLED
+	if (e - m->NextuDNSEvent         > 0) e = m->NextuDNSEvent;
+	if (e - m->NextScheduledNATOp    > 0) e = m->NextScheduledNATOp;
+	if (m->NextSRVUpdate && e - m->NextSRVUpdate > 0) e = m->NextSRVUpdate;
+#endif
+
+	if (e - m->NextCacheCheck        > 0) e = m->NextCacheCheck;
+	if (e - m->NextScheduledSPS      > 0) e = m->NextScheduledSPS;
+	// NextScheduledSPRetry only valid when DelaySleep not set
+	if (!m->DelaySleep && m->SleepLimit && e - m->NextScheduledSPRetry > 0) e = m->NextScheduledSPRetry;
+	if (m->DelaySleep && e - m->DelaySleep > 0) e = m->DelaySleep;
+
+	if (m->SuppressSending)
+		{
+		if (e - m->SuppressSending       > 0) e = m->SuppressSending;
+		}
+	else
+		{
+		if (e - m->NextScheduledQuery    > 0) e = m->NextScheduledQuery;
+		if (e - m->NextScheduledProbe    > 0) e = m->NextScheduledProbe;
+		if (e - m->NextScheduledResponse > 0) e = m->NextScheduledResponse;
+		}
+	if (e - m->NextScheduledStopTime > 0) e = m->NextScheduledStopTime;
+	return(e);
+	}
+
+mDNSexport void ShowTaskSchedulingError(mDNS *const m)
+	{
+	AuthRecord *rr;
+	mDNS_Lock(m);
+
+	LogMsg("Task Scheduling Error: Continuously busy for more than a second");
+	
+	// Note: To accurately diagnose *why* we're busy, the debugging code here needs to mirror the logic in GetNextScheduledEvent above
+
+	if (m->NewQuestions && (!m->NewQuestions->DelayAnswering || m->timenow - m->NewQuestions->DelayAnswering >= 0))
+		LogMsg("Task Scheduling Error: NewQuestion %##s (%s)",
+			m->NewQuestions->qname.c, DNSTypeName(m->NewQuestions->qtype));
+
+	if (m->NewLocalOnlyQuestions)
+		LogMsg("Task Scheduling Error: NewLocalOnlyQuestions %##s (%s)",
+			m->NewLocalOnlyQuestions->qname.c, DNSTypeName(m->NewLocalOnlyQuestions->qtype));
+
+	if (m->NewLocalRecords)
+		{
+		rr = AnyLocalRecordReady(m);
+		if (rr) LogMsg("Task Scheduling Error: NewLocalRecords %s", ARDisplayString(m, rr));
+		}
+	
+	if (m->NewLocalOnlyRecords) LogMsg("Task Scheduling Error: NewLocalOnlyRecords");
+
+	if (m->SPSProxyListChanged) LogMsg("Task Scheduling Error: SPSProxyListChanged");
+	if (m->LocalRemoveEvents)   LogMsg("Task Scheduling Error: LocalRemoveEvents");
+
+	if (m->timenow - m->NextScheduledEvent    >= 0)
+		LogMsg("Task Scheduling Error: m->NextScheduledEvent %d",    m->timenow - m->NextScheduledEvent);
+
+#ifndef UNICAST_DISABLED
+	if (m->timenow - m->NextuDNSEvent         >= 0)
+		LogMsg("Task Scheduling Error: m->NextuDNSEvent %d",         m->timenow - m->NextuDNSEvent);
+	if (m->timenow - m->NextScheduledNATOp    >= 0)
+		LogMsg("Task Scheduling Error: m->NextScheduledNATOp %d",    m->timenow - m->NextScheduledNATOp);
+	if (m->NextSRVUpdate && m->timenow - m->NextSRVUpdate >= 0)
+		LogMsg("Task Scheduling Error: m->NextSRVUpdate %d",         m->timenow - m->NextSRVUpdate);
+#endif
+
+	if (m->timenow - m->NextCacheCheck        >= 0)
+		LogMsg("Task Scheduling Error: m->NextCacheCheck %d",        m->timenow - m->NextCacheCheck);
+	if (m->timenow - m->NextScheduledSPS      >= 0)
+		LogMsg("Task Scheduling Error: m->NextScheduledSPS %d",      m->timenow - m->NextScheduledSPS);
+	if (!m->DelaySleep && m->SleepLimit && m->timenow - m->NextScheduledSPRetry >= 0)
+		LogMsg("Task Scheduling Error: m->NextScheduledSPRetry %d",  m->timenow - m->NextScheduledSPRetry);
+	if (m->DelaySleep && m->timenow - m->DelaySleep >= 0)
+		LogMsg("Task Scheduling Error: m->DelaySleep %d",            m->timenow - m->DelaySleep);
+
+	if (m->SuppressSending && m->timenow - m->SuppressSending >= 0)
+		LogMsg("Task Scheduling Error: m->SuppressSending %d",       m->timenow - m->SuppressSending);
+	if (m->timenow - m->NextScheduledQuery    >= 0)
+		LogMsg("Task Scheduling Error: m->NextScheduledQuery %d",    m->timenow - m->NextScheduledQuery);
+	if (m->timenow - m->NextScheduledProbe    >= 0)
+		LogMsg("Task Scheduling Error: m->NextScheduledProbe %d",    m->timenow - m->NextScheduledProbe);
+	if (m->timenow - m->NextScheduledResponse >= 0)
+		LogMsg("Task Scheduling Error: m->NextScheduledResponse %d", m->timenow - m->NextScheduledResponse);
+
+	mDNS_Unlock(m);
+	}
+
+mDNSexport void mDNS_Unlock_(mDNS *const m, const char * const functionname)
+	{
+	// Decrement mDNS_busy
+	m->mDNS_busy--;
+	
+	// Check for locking failures
+	if (m->mDNS_busy != m->mDNS_reentrancy)
+		{
+		LogMsg("%s: mDNS_Unlock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", functionname, m->mDNS_busy, m->mDNS_reentrancy);
+#if ForceAlerts
+		*(long*)0 = 0;
+#endif
+		}
+
+	// If this is a final exit from the mDNSCore code, set m->NextScheduledEvent and clear m->timenow
+	if (m->mDNS_busy == 0)
+		{
+		m->NextScheduledEvent = GetNextScheduledEvent(m);
+		if (m->timenow == 0) LogMsg("%s: mDNS_Unlock: ERROR! m->timenow aready zero", functionname);
+		m->timenow = 0;
+		}
+
+	// MUST release the platform lock LAST!
+	mDNSPlatformUnlock(m);
+	}
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - Specialized mDNS version of vsnprintf
+#endif
+
+static const struct mDNSprintf_format
+	{
+	unsigned      leftJustify : 1;
+	unsigned      forceSign : 1;
+	unsigned      zeroPad : 1;
+	unsigned      havePrecision : 1;
+	unsigned      hSize : 1;
+	unsigned      lSize : 1;
+	char          altForm;
+	char          sign;		// +, - or space
+	unsigned int  fieldWidth;
+	unsigned int  precision;
+	} mDNSprintf_format_default = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+mDNSexport mDNSu32 mDNS_vsnprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, va_list arg)
+	{
+	mDNSu32 nwritten = 0;
+	int c;
+	if (buflen == 0) return(0);
+	buflen--;		// Pre-reserve one space in the buffer for the terminating null
+	if (buflen == 0) goto exit;
+
+	for (c = *fmt; c != 0; c = *++fmt)
+		{
+		if (c != '%')
+			{
+			*sbuffer++ = (char)c;
+			if (++nwritten >= buflen) goto exit;
+			}
+		else
+			{
+			unsigned int i=0, j;
+			// The mDNS Vsprintf Argument Conversion Buffer is used as a temporary holding area for
+			// generating decimal numbers, hexdecimal numbers, IP addresses, domain name strings, etc.
+			// The size needs to be enough for a 256-byte domain name plus some error text.
+			#define mDNS_VACB_Size 300
+			char mDNS_VACB[mDNS_VACB_Size];
+			#define mDNS_VACB_Lim (&mDNS_VACB[mDNS_VACB_Size])
+			#define mDNS_VACB_Remain(s) ((mDNSu32)(mDNS_VACB_Lim - s))
+			char *s = mDNS_VACB_Lim, *digits;
+			struct mDNSprintf_format F = mDNSprintf_format_default;
+	
+			while (1)	//  decode flags
+				{
+				c = *++fmt;
+				if      (c == '-') F.leftJustify = 1;
+				else if (c == '+') F.forceSign = 1;
+				else if (c == ' ') F.sign = ' ';
+				else if (c == '#') F.altForm++;
+				else if (c == '0') F.zeroPad = 1;
+				else break;
+				}
+	
+			if (c == '*')	//  decode field width
+				{
+				int f = va_arg(arg, int);
+				if (f < 0) { f = -f; F.leftJustify = 1; }
+				F.fieldWidth = (unsigned int)f;
+				c = *++fmt;
+				}
+			else
+				{
+				for (; c >= '0' && c <= '9'; c = *++fmt)
+					F.fieldWidth = (10 * F.fieldWidth) + (c - '0');
+				}
+	
+			if (c == '.')	//  decode precision
+				{
+				if ((c = *++fmt) == '*')
+					{ F.precision = va_arg(arg, unsigned int); c = *++fmt; }
+				else for (; c >= '0' && c <= '9'; c = *++fmt)
+						F.precision = (10 * F.precision) + (c - '0');
+				F.havePrecision = 1;
+				}
+	
+			if (F.leftJustify) F.zeroPad = 0;
+	
+			conv:
+			switch (c)	//  perform appropriate conversion
+				{
+				unsigned long n;
+				case 'h' :	F.hSize = 1; c = *++fmt; goto conv;
+				case 'l' :	// fall through
+				case 'L' :	F.lSize = 1; c = *++fmt; goto conv;
+				case 'd' :
+				case 'i' :	if (F.lSize) n = (unsigned long)va_arg(arg, long);
+							else n = (unsigned long)va_arg(arg, int);
+							if (F.hSize) n = (short) n;
+							if ((long) n < 0) { n = (unsigned long)-(long)n; F.sign = '-'; }
+							else if (F.forceSign) F.sign = '+';
+							goto decimal;
+				case 'u' :	if (F.lSize) n = va_arg(arg, unsigned long);
+							else n = va_arg(arg, unsigned int);
+							if (F.hSize) n = (unsigned short) n;
+							F.sign = 0;
+							goto decimal;
+				decimal:	if (!F.havePrecision)
+								{
+								if (F.zeroPad)
+									{
+									F.precision = F.fieldWidth;
+									if (F.sign) --F.precision;
+									}
+								if (F.precision < 1) F.precision = 1;
+								}
+							if (F.precision > mDNS_VACB_Size - 1)
+								F.precision = mDNS_VACB_Size - 1;
+							for (i = 0; n; n /= 10, i++) *--s = (char)(n % 10 + '0');
+							for (; i < F.precision; i++) *--s = '0';
+							if (F.sign) { *--s = F.sign; i++; }
+							break;
+	
+				case 'o' :	if (F.lSize) n = va_arg(arg, unsigned long);
+							else n = va_arg(arg, unsigned int);
+							if (F.hSize) n = (unsigned short) n;
+							if (!F.havePrecision)
+								{
+								if (F.zeroPad) F.precision = F.fieldWidth;
+								if (F.precision < 1) F.precision = 1;
+								}
+							if (F.precision > mDNS_VACB_Size - 1)
+								F.precision = mDNS_VACB_Size - 1;
+							for (i = 0; n; n /= 8, i++) *--s = (char)(n % 8 + '0');
+							if (F.altForm && i && *s != '0') { *--s = '0'; i++; }
+							for (; i < F.precision; i++) *--s = '0';
+							break;
+	
+				case 'a' :	{
+							unsigned char *a = va_arg(arg, unsigned char *);
+							if (!a) { static char emsg[] = "<<NULL>>"; s = emsg; i = sizeof(emsg)-1; }
+							else
+								{
+								s = mDNS_VACB;	// Adjust s to point to the start of the buffer, not the end
+								if (F.altForm)
+									{
+									mDNSAddr *ip = (mDNSAddr*)a;
+									switch (ip->type)
+										{
+										case mDNSAddrType_IPv4: F.precision =  4; a = (unsigned char *)&ip->ip.v4; break;
+										case mDNSAddrType_IPv6: F.precision = 16; a = (unsigned char *)&ip->ip.v6; break;
+										default:                F.precision =  0; break;
+										}
+									}
+								if (F.altForm && !F.precision)
+									i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "«ZERO ADDRESS»");
+								else switch (F.precision)
+									{
+									case  4: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%d.%d.%d.%d",
+														a[0], a[1], a[2], a[3]); break;
+									case  6: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%02X:%02X:%02X:%02X:%02X:%02X",
+														a[0], a[1], a[2], a[3], a[4], a[5]); break;
+									case 16: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB),
+														"%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X",
+														a[0x0], a[0x1], a[0x2], a[0x3], a[0x4], a[0x5], a[0x6], a[0x7],
+														a[0x8], a[0x9], a[0xA], a[0xB], a[0xC], a[0xD], a[0xE], a[0xF]); break;
+									default: i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "%s", "<< ERROR: Must specify"
+														" address size (i.e. %.4a=IPv4, %.6a=Ethernet, %.16a=IPv6) >>"); break;
+									}
+								}
+							}
+							break;
+	
+				case 'p' :	F.havePrecision = F.lSize = 1;
+							F.precision = sizeof(void*) * 2;	// 8 characters on 32-bit; 16 characters on 64-bit
+				case 'X' :	digits = "0123456789ABCDEF";
+							goto hexadecimal;
+				case 'x' :	digits = "0123456789abcdef";
+				hexadecimal:if (F.lSize) n = va_arg(arg, unsigned long);
+							else n = va_arg(arg, unsigned int);
+							if (F.hSize) n = (unsigned short) n;
+							if (!F.havePrecision)
+								{
+								if (F.zeroPad)
+									{
+									F.precision = F.fieldWidth;
+									if (F.altForm) F.precision -= 2;
+									}
+								if (F.precision < 1) F.precision = 1;
+								}
+							if (F.precision > mDNS_VACB_Size - 1)
+								F.precision = mDNS_VACB_Size - 1;
+							for (i = 0; n; n /= 16, i++) *--s = digits[n % 16];
+							for (; i < F.precision; i++) *--s = '0';
+							if (F.altForm) { *--s = (char)c; *--s = '0'; i += 2; }
+							break;
+	
+				case 'c' :	*--s = (char)va_arg(arg, int); i = 1; break;
+	
+				case 's' :	s = va_arg(arg, char *);
+							if (!s) { static char emsg[] = "<<NULL>>"; s = emsg; i = sizeof(emsg)-1; }
+							else switch (F.altForm)
+								{
+								case 0: i=0;
+										if (!F.havePrecision)				// C string
+											while (s[i]) i++;
+										else
+											{
+											while ((i < F.precision) && s[i]) i++;
+											// Make sure we don't truncate in the middle of a UTF-8 character
+											// If last character we got was any kind of UTF-8 multi-byte character,
+											// then see if we have to back up.
+											// This is not as easy as the similar checks below, because
+											// here we can't assume it's safe to examine the *next* byte, so we
+											// have to confine ourselves to working only backwards in the string.
+											j = i;		// Record where we got to
+											// Now, back up until we find first non-continuation-char
+											while (i>0 && (s[i-1] & 0xC0) == 0x80) i--;
+											// Now s[i-1] is the first non-continuation-char
+											// and (j-i) is the number of continuation-chars we found
+											if (i>0 && (s[i-1] & 0xC0) == 0xC0)	// If we found a start-char
+												{
+												i--;		// Tentatively eliminate this start-char as well
+												// Now (j-i) is the number of characters we're considering eliminating.
+												// To be legal UTF-8, the start-char must contain (j-i) one-bits,
+												// followed by a zero bit. If we shift it right by (7-(j-i)) bits
+												// (with sign extension) then the result has to be 0xFE.
+												// If this is right, then we reinstate the tentatively eliminated bytes.
+												if (((j-i) < 7) && (((s[i] >> (7-(j-i))) & 0xFF) == 0xFE)) i = j;
+												}
+											}
+										break;
+								case 1: i = (unsigned char) *s++; break;	// Pascal string
+								case 2: {									// DNS label-sequence name
+										unsigned char *a = (unsigned char *)s;
+										s = mDNS_VACB;	// Adjust s to point to the start of the buffer, not the end
+										if (*a == 0) *s++ = '.';	// Special case for root DNS name
+										while (*a)
+											{
+											char buf[63*4+1];
+											if (*a > 63)
+												{ s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "<<INVALID LABEL LENGTH %u>>", *a); break; }
+											if (s + *a >= &mDNS_VACB[254])
+												{ s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "<<NAME TOO LONG>>"); break; }
+											// Need to use ConvertDomainLabelToCString to do proper escaping here,
+											// so it's clear what's a literal dot and what's a label separator
+											ConvertDomainLabelToCString((domainlabel*)a, buf);
+											s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "%s.", buf);
+											a += 1 + *a;
+											}
+										i = (mDNSu32)(s - mDNS_VACB);
+										s = mDNS_VACB;	// Reset s back to the start of the buffer
+										break;
+										}
+								}
+							// Make sure we don't truncate in the middle of a UTF-8 character (see similar comment below)
+							if (F.havePrecision && i > F.precision)
+								{ i = F.precision; while (i>0 && (s[i] & 0xC0) == 0x80) i--; }
+							break;
+	
+				case 'n' :	s = va_arg(arg, char *);
+							if      (F.hSize) * (short *) s = (short)nwritten;
+							else if (F.lSize) * (long  *) s = (long)nwritten;
+							else              * (int   *) s = (int)nwritten;
+							continue;
+	
+				default:	s = mDNS_VACB;
+							i = mDNS_snprintf(mDNS_VACB, sizeof(mDNS_VACB), "<<UNKNOWN FORMAT CONVERSION CODE %%%c>>", c);
+
+				case '%' :	*sbuffer++ = (char)c;
+							if (++nwritten >= buflen) goto exit;
+							break;
+				}
+	
+			if (i < F.fieldWidth && !F.leftJustify)			// Pad on the left
+				do	{
+					*sbuffer++ = ' ';
+					if (++nwritten >= buflen) goto exit;
+					} while (i < --F.fieldWidth);
+	
+			// Make sure we don't truncate in the middle of a UTF-8 character.
+			// Note: s[i] is the first eliminated character; i.e. the next character *after* the last character of the
+			// allowed output. If s[i] is a UTF-8 continuation character, then we've cut a unicode character in half,
+			// so back up 'i' until s[i] is no longer a UTF-8 continuation character. (if the input was proprly
+			// formed, s[i] will now be the UTF-8 start character of the multi-byte character we just eliminated).
+			if (i > buflen - nwritten)
+				{ i = buflen - nwritten; while (i>0 && (s[i] & 0xC0) == 0x80) i--; }
+			for (j=0; j<i; j++) *sbuffer++ = *s++;			// Write the converted result
+			nwritten += i;
+			if (nwritten >= buflen) goto exit;
+	
+			for (; i < F.fieldWidth; i++)					// Pad on the right
+				{
+				*sbuffer++ = ' ';
+				if (++nwritten >= buflen) goto exit;
+				}
+			}
+		}
+	exit:
+	*sbuffer++ = 0;
+	return(nwritten);
+	}
+
+mDNSexport mDNSu32 mDNS_snprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, ...)
+	{
+	mDNSu32 length;
+	
+	va_list ptr;
+	va_start(ptr,fmt);
+	length = mDNS_vsnprintf(sbuffer, buflen, fmt, ptr);
+	va_end(ptr);
+	
+	return(length);
+	}
diff --git a/mdnsresponder/mDNSCore/DNSCommon.h b/mdnsresponder/mDNSCore/DNSCommon.h
new file mode 100644
index 0000000..5df4ce4
--- /dev/null
+++ b/mdnsresponder/mDNSCore/DNSCommon.h
@@ -0,0 +1,292 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+#ifndef __DNSCOMMON_H_
+#define __DNSCOMMON_H_
+
+#include "mDNSEmbeddedAPI.h"
+
+#ifdef	__cplusplus
+	extern "C" {
+#endif
+
+//*************************************************************************************************************
+// Macros
+
+// Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion
+// e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4"
+// To expand "version" to its value before making the string, use STRINGIFY(version) instead
+#define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) #s
+#define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s)
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark - DNS Protocol Constants
+#endif
+
+typedef enum
+	{
+	kDNSFlag0_QR_Mask     = 0x80,		// Query or response?
+	kDNSFlag0_QR_Query    = 0x00,
+	kDNSFlag0_QR_Response = 0x80,
+
+	kDNSFlag0_OP_Mask     = 0x78,		// Operation type
+	kDNSFlag0_OP_StdQuery = 0x00,
+	kDNSFlag0_OP_Iquery   = 0x08,
+	kDNSFlag0_OP_Status   = 0x10,
+	kDNSFlag0_OP_Unused3  = 0x18,
+	kDNSFlag0_OP_Notify   = 0x20,
+	kDNSFlag0_OP_Update   = 0x28,
+
+	kDNSFlag0_QROP_Mask   = kDNSFlag0_QR_Mask | kDNSFlag0_OP_Mask,
+
+	kDNSFlag0_AA          = 0x04,		// Authoritative Answer?
+	kDNSFlag0_TC          = 0x02,		// Truncated?
+	kDNSFlag0_RD          = 0x01,		// Recursion Desired?
+	kDNSFlag1_RA          = 0x80,		// Recursion Available?
+
+	kDNSFlag1_Zero        = 0x40,		// Reserved; must be zero
+	kDNSFlag1_AD          = 0x20,		// Authentic Data [RFC 2535]
+	kDNSFlag1_CD          = 0x10,		// Checking Disabled [RFC 2535]
+
+	kDNSFlag1_RC_Mask     = 0x0F,		// Response code
+	kDNSFlag1_RC_NoErr    = 0x00,
+	kDNSFlag1_RC_FormErr  = 0x01,
+	kDNSFlag1_RC_ServFail = 0x02,
+	kDNSFlag1_RC_NXDomain = 0x03,
+	kDNSFlag1_RC_NotImpl  = 0x04,
+	kDNSFlag1_RC_Refused  = 0x05,
+	kDNSFlag1_RC_YXDomain = 0x06,
+	kDNSFlag1_RC_YXRRSet  = 0x07,
+	kDNSFlag1_RC_NXRRSet  = 0x08,
+	kDNSFlag1_RC_NotAuth  = 0x09,
+	kDNSFlag1_RC_NotZone  = 0x0A
+	} DNS_Flags;
+
+typedef enum
+	{
+	TSIG_ErrBadSig  = 16,
+	TSIG_ErrBadKey  = 17,
+	TSIG_ErrBadTime = 18
+	} TSIG_ErrorCode;
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - General Utility Functions
+#endif
+
+extern NetworkInterfaceInfo *GetFirstActiveInterface(NetworkInterfaceInfo *intf);
+extern mDNSInterfaceID GetNextActiveInterfaceID(const NetworkInterfaceInfo *intf);
+
+extern mDNSu32 mDNSRandom(mDNSu32 max);		// Returns pseudo-random result from zero to max inclusive
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - Domain Name Utility Functions
+#endif
+
+#define mDNSIsDigit(X)     ((X) >= '0' && (X) <= '9')
+#define mDNSIsUpperCase(X) ((X) >= 'A' && (X) <= 'Z')
+#define mDNSIsLowerCase(X) ((X) >= 'a' && (X) <= 'z')
+#define mDNSIsLetter(X)    (mDNSIsUpperCase(X) || mDNSIsLowerCase(X))
+
+#define mDNSValidHostChar(X, notfirst, notlast) (mDNSIsLetter(X) || mDNSIsDigit(X) || ((notfirst) && (notlast) && (X) == '-') )
+
+extern mDNSu16 CompressedDomainNameLength(const domainname *const name, const domainname *parent);
+extern int CountLabels(const domainname *d);
+extern const domainname *SkipLeadingLabels(const domainname *d, int skip);
+
+extern mDNSu32 TruncateUTF8ToLength(mDNSu8 *string, mDNSu32 length, mDNSu32 max);
+extern mDNSBool LabelContainsSuffix(const domainlabel *const name, const mDNSBool RichText);
+extern mDNSu32 RemoveLabelSuffix(domainlabel *name, mDNSBool RichText);
+extern void AppendLabelSuffix(domainlabel *const name, mDNSu32 val, const mDNSBool RichText);
+#define ValidateDomainName(N) (DomainNameLength(N) <= MAX_DOMAIN_NAME)
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - Resource Record Utility Functions
+#endif
+
+// IdenticalResourceRecord returns true if two resources records have
+// the same name, type, class, and identical rdata (InterfaceID and TTL may differ)
+
+// IdenticalSameNameRecord is the same, except it skips the expensive SameDomainName() check,
+// which is at its most expensive and least useful in cases where we know in advance that the names match
+
+// Note: The dominant use of IdenticalResourceRecord is from ProcessQuery(), handling known-answer lists. In this case
+// it's common to have a whole bunch or records with exactly the same name (e.g. "_http._tcp.local") but different RDATA.
+// The SameDomainName() check is expensive when the names match, and in this case *all* the names match, so we
+// used to waste a lot of CPU time verifying that the names match, only then to find that the RDATA is different.
+// We observed mDNSResponder spending 30% of its total CPU time on this single task alone.
+// By swapping the checks so that we check the RDATA first, we can quickly detect when it's different
+// (99% of the time) and then bail out before we waste time on the expensive SameDomainName() check.
+
+#define IdenticalResourceRecord(r1,r2) ( \
+	(r1)->rrtype    == (r2)->rrtype      && \
+	(r1)->rrclass   == (r2)->rrclass     && \
+	(r1)->namehash  == (r2)->namehash    && \
+	(r1)->rdlength  == (r2)->rdlength    && \
+	(r1)->rdatahash == (r2)->rdatahash   && \
+	SameRDataBody((r1), &(r2)->rdata->u, SameDomainName) && \
+	SameDomainName((r1)->name, (r2)->name))
+
+#define IdenticalSameNameRecord(r1,r2) ( \
+	(r1)->rrtype    == (r2)->rrtype      && \
+	(r1)->rrclass   == (r2)->rrclass     && \
+	(r1)->rdlength  == (r2)->rdlength    && \
+	(r1)->rdatahash == (r2)->rdatahash   && \
+	SameRDataBody((r1), &(r2)->rdata->u, SameDomainName))
+
+// A given RRType answers a QuestionType if RRType is CNAME, or types match, or QuestionType is ANY,
+// or the RRType is NSEC and positively asserts the nonexistence of the type being requested
+#define RRTypeAnswersQuestionType(R,Q) ((R)->rrtype == kDNSType_CNAME || (R)->rrtype == (Q) || (Q) == kDNSQType_ANY || RRAssertsNonexistence((R),(Q)))
+#define RRAssertsNonexistence(R,T) ((R)->rrtype == kDNSType_NSEC && (T) < kDNSQType_ANY && !((R)->rdata->u.nsec.bitmap[(T)>>3] & (128 >> ((T)&7))))
+
+extern mDNSu32 RDataHashValue(const ResourceRecord *const rr);
+extern mDNSBool SameRDataBody(const ResourceRecord *const r1, const RDataBody *const r2, DomainNameComparisonFn *samename);
+extern mDNSBool SameNameRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q);
+extern mDNSBool ResourceRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q);
+extern mDNSBool AnyTypeRecordAnswersQuestion (const ResourceRecord *const rr, const DNSQuestion *const q);
+extern mDNSBool ResourceRecordAnswersUnicastResponse(const ResourceRecord *const rr, const DNSQuestion *const q);
+extern mDNSBool LocalOnlyRecordAnswersQuestion(AuthRecord *const rr, const DNSQuestion *const q);
+extern mDNSu16 GetRDLength(const ResourceRecord *const rr, mDNSBool estimate);
+extern mDNSBool ValidateRData(const mDNSu16 rrtype, const mDNSu16 rdlength, const RData *const rd);
+
+#define GetRRDomainNameTarget(RR) (                                                                          \
+	((RR)->rrtype == kDNSType_NS || (RR)->rrtype == kDNSType_CNAME || (RR)->rrtype == kDNSType_PTR || (RR)->rrtype == kDNSType_DNAME) ? &(RR)->rdata->u.name        : \
+	((RR)->rrtype == kDNSType_MX || (RR)->rrtype == kDNSType_AFSDB || (RR)->rrtype == kDNSType_RT  || (RR)->rrtype == kDNSType_KX   ) ? &(RR)->rdata->u.mx.exchange : \
+	((RR)->rrtype == kDNSType_SRV                                  ) ? &(RR)->rdata->u.srv.target : mDNSNULL )
+
+#define LocalRecordReady(X) ((X)->resrec.RecordType != kDNSRecordTypeUnique)
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - DNS Message Creation Functions
+#endif
+
+extern void InitializeDNSMessage(DNSMessageHeader *h, mDNSOpaque16 id, mDNSOpaque16 flags);
+extern const mDNSu8 *FindCompressionPointer(const mDNSu8 *const base, const mDNSu8 *const end, const mDNSu8 *const domname);
+extern mDNSu8 *putDomainNameAsLabels(const DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, const domainname *const name);
+extern mDNSu8 *putRData(const DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, const ResourceRecord *const rr);
+
+// If we have a single large record to put in the packet, then we allow the packet to be up to 9K bytes,
+// but in the normal case we try to keep the packets below 1500 to avoid IP fragmentation on standard Ethernet
+
+#define AllowedRRSpace(msg) (((msg)->h.numAnswers || (msg)->h.numAuthorities || (msg)->h.numAdditionals) ? NormalMaxDNSMessageData : AbsoluteMaxDNSMessageData)
+
+extern mDNSu8 *PutResourceRecordTTLWithLimit(DNSMessage *const msg, mDNSu8 *ptr, mDNSu16 *count, ResourceRecord *rr, mDNSu32 ttl, const mDNSu8 *limit);
+
+#define PutResourceRecordTTL(msg, ptr, count, rr, ttl) \
+	PutResourceRecordTTLWithLimit((msg), (ptr), (count), (rr), (ttl), (msg)->data + AllowedRRSpace(msg))
+
+#define PutResourceRecordTTLJumbo(msg, ptr, count, rr, ttl) \
+	PutResourceRecordTTLWithLimit((msg), (ptr), (count), (rr), (ttl), (msg)->data + AbsoluteMaxDNSMessageData)
+
+#define PutResourceRecord(MSG, P, C, RR) PutResourceRecordTTL((MSG), (P), (C), (RR), (RR)->rroriginalttl)
+
+// The PutRR_OS variants assume a local variable 'm', put build the packet at m->omsg,
+// and assume a local variable 'OwnerRecordSpace' indicating how many bytes (if any) to reserve to add an OWNER option at the end
+#define PutRR_OS_TTL(ptr, count, rr, ttl) \
+	PutResourceRecordTTLWithLimit(&m->omsg, (ptr), (count), (rr), (ttl), m->omsg.data + AllowedRRSpace(&m->omsg) - OwnerRecordSpace)
+
+#define PutRR_OS(P, C, RR) PutRR_OS_TTL((P), (C), (RR), (RR)->rroriginalttl)
+
+extern mDNSu8 *putQuestion(DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, const domainname *const name, mDNSu16 rrtype, mDNSu16 rrclass);
+extern mDNSu8 *putZone(DNSMessage *const msg, mDNSu8 *ptr, mDNSu8 *limit, const domainname *zone, mDNSOpaque16 zoneClass);
+extern mDNSu8 *putPrereqNameNotInUse(const domainname *const name, DNSMessage *const msg, mDNSu8 *const ptr, mDNSu8 *const end);
+extern mDNSu8 *putDeletionRecord(DNSMessage *msg, mDNSu8 *ptr, ResourceRecord *rr);
+extern mDNSu8 *putDeletionRecordWithLimit(DNSMessage *msg, mDNSu8 *ptr, ResourceRecord *rr, mDNSu8 *limit);
+extern mDNSu8 *putDeleteRRSetWithLimit(DNSMessage *msg, mDNSu8 *ptr, const domainname *name, mDNSu16 rrtype, mDNSu8 *limit);
+extern mDNSu8 *putDeleteAllRRSets(DNSMessage *msg, mDNSu8 *ptr, const domainname *name);
+extern mDNSu8 *putUpdateLease(DNSMessage *msg, mDNSu8 *end, mDNSu32 lease);
+extern mDNSu8 *putUpdateLeaseWithLimit(DNSMessage *msg, mDNSu8 *ptr, mDNSu32 lease, mDNSu8 *limit);
+
+extern mDNSu8 *putHINFO(const mDNS *const m, DNSMessage *const msg, mDNSu8 *ptr, DomainAuthInfo *authInfo, mDNSu8 *limit);
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - DNS Message Parsing Functions
+#endif
+
+#define AuthHashSlot(X) (DomainNameHashValue(X) % AUTH_HASH_SLOTS)
+#define HashSlot(X) (DomainNameHashValue(X) % CACHE_HASH_SLOTS)
+extern mDNSu32 DomainNameHashValue(const domainname *const name);
+extern void SetNewRData(ResourceRecord *const rr, RData *NewRData, mDNSu16 rdlength);
+extern const mDNSu8 *skipDomainName(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end);
+extern const mDNSu8 *getDomainName(const DNSMessage *const msg, const mDNSu8 *ptr, const mDNSu8 *const end,
+	domainname *const name);
+extern const mDNSu8 *skipResourceRecord(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end);
+extern const mDNSu8 *GetLargeResourceRecord(mDNS *const m, const DNSMessage * const msg, const mDNSu8 *ptr,
+    const mDNSu8 * end, const mDNSInterfaceID InterfaceID, mDNSu8 RecordType, LargeCacheRecord *const largecr);
+extern const mDNSu8 *skipQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end);
+extern const mDNSu8 *getQuestion(const DNSMessage *msg, const mDNSu8 *ptr, const mDNSu8 *end, const mDNSInterfaceID InterfaceID,
+	DNSQuestion *question);
+extern const mDNSu8 *LocateAnswers(const DNSMessage *const msg, const mDNSu8 *const end);
+extern const mDNSu8 *LocateAuthorities(const DNSMessage *const msg, const mDNSu8 *const end);
+extern const mDNSu8 *LocateAdditionals(const DNSMessage *const msg, const mDNSu8 *const end);
+extern const mDNSu8 *LocateOptRR(const DNSMessage *const msg, const mDNSu8 *const end, int minsize);
+extern const rdataOPT *GetLLQOptData(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end);
+extern mDNSu32 GetPktLease(mDNS *m, DNSMessage *msg, const mDNSu8 *end);
+extern void DumpPacket(mDNS *const m, mStatus status, mDNSBool sent, char *transport,
+	const mDNSAddr *srcaddr, mDNSIPPort srcport,
+	const mDNSAddr *dstaddr, mDNSIPPort dstport, const DNSMessage *const msg, const mDNSu8 *const end);
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - Packet Sending Functions
+#endif
+
+extern mStatus mDNSSendDNSMessage(mDNS *const m, DNSMessage *const msg, mDNSu8 *end,
+	mDNSInterfaceID InterfaceID, UDPSocket *src, const mDNSAddr *dst, mDNSIPPort dstport, TCPSocket *sock, DomainAuthInfo *authInfo);
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - RR List Management & Task Management
+#endif
+
+extern void ShowTaskSchedulingError(mDNS *const m);
+extern void mDNS_Lock_(mDNS *const m, const char * const functionname);
+extern void mDNS_Unlock_(mDNS *const m, const char * const functionname);
+
+#if defined(_WIN32)
+ #define __func__ __FUNCTION__
+#endif
+
+#define mDNS_Lock(X) mDNS_Lock_((X), __func__)
+
+#define mDNS_Unlock(X) mDNS_Unlock_((X), __func__)
+
+#define mDNS_DropLockBeforeCallback() do { m->mDNS_reentrancy++; \
+	if (m->mDNS_busy != m->mDNS_reentrancy) LogMsg("%s: Locking Failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", __func__, m->mDNS_busy, m->mDNS_reentrancy); \
+	} while (0)
+
+#define mDNS_ReclaimLockAfterCallback() do { \
+	if (m->mDNS_busy != m->mDNS_reentrancy) LogMsg("%s: Unlocking Failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", __func__, m->mDNS_busy, m->mDNS_reentrancy); \
+	m->mDNS_reentrancy--; } while (0)
+
+#ifdef	__cplusplus
+	}
+#endif
+
+#endif // __DNSCOMMON_H_
diff --git a/mdnsresponder/mDNSCore/DNSDigest.c b/mdnsresponder/mDNSCore/DNSDigest.c
new file mode 100644
index 0000000..3a0ce25
--- /dev/null
+++ b/mdnsresponder/mDNSCore/DNSDigest.c
@@ -0,0 +1,1579 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#include "mDNSEmbeddedAPI.h"
+#include "DNSCommon.h"
+
+// Disable certain benign warnings with Microsoft compilers
+#if(defined(_MSC_VER))
+	// Disable "conditional expression is constant" warning for debug macros.
+	// Otherwise, this generates warnings for the perfectly natural construct "while(1)"
+	// If someone knows a variant way of writing "while(1)" that doesn't generate warning messages, please let us know
+	#pragma warning(disable:4127)
+#endif
+
+
+ // ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark - Byte Swapping Functions
+#endif
+
+mDNSlocal mDNSu16 NToH16(mDNSu8 * bytes)
+	{
+	return (mDNSu16)((mDNSu16)bytes[0] << 8 | (mDNSu16)bytes[1]);
+	}
+
+mDNSlocal mDNSu32 NToH32(mDNSu8 * bytes)
+	{
+	return (mDNSu32)((mDNSu32) bytes[0] << 24 | (mDNSu32) bytes[1] << 16 | (mDNSu32) bytes[2] << 8 | (mDNSu32)bytes[3]);
+	}
+
+ // ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark - MD5 Hash Functions
+#endif
+
+
+/* The source for the has is derived CommonCrypto files CommonDigest.h, md32_common.h, md5_locl.h, md5_locl.h, and openssl/md5.h.
+ * The following changes have been made to the original sources:
+ *    replaced CC_LONG w/ mDNSu32
+ *    replaced CC_MD5* with MD5*
+ *    replaced CC_LONG w/ mDNSu32, removed conditional #defines from md5.h
+ *    removed extern decls for MD5_Init/Update/Final from CommonDigest.h
+ *    removed APPLE_COMMON_DIGEST specific #defines from md5_locl.h
+ *
+ * Note: machine archetecure specific conditionals from the original sources are turned off, but are left in the code
+ * to aid in platform-specific optimizations and debugging.
+ * Sources originally distributed under the following license headers:
+ * CommonDigest.h - APSL
+ * 
+ * md32_Common.h
+ * ====================================================================
+ * Copyright (c) 1999-2002 The OpenSSL Project.  All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ *
+ * 1. Redistributions of source code must retain the above copyright
+ *    notice, this list of conditions and the following disclaimer. 
+ *
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in
+ *    the documentation and/or other materials provided with the
+ *    distribution.
+ *
+ * 3. All advertising materials mentioning features or use of this
+ *    software must display the following acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit. (http://www.OpenSSL.org/)"
+ *
+ * 4. The names "OpenSSL Toolkit" and "OpenSSL Project" must not be used to
+ *    endorse or promote products derived from this software without
+ *    prior written permission. For written permission, please contact
+ *    licensing@OpenSSL.org.
+ *
+ * 5. Products derived from this software may not be called "OpenSSL"
+ *    nor may "OpenSSL" appear in their names without prior written
+ *    permission of the OpenSSL Project.
+ *
+ * 6. Redistributions of any form whatsoever must retain the following
+ *    acknowledgment:
+ *    "This product includes software developed by the OpenSSL Project
+ *    for use in the OpenSSL Toolkit (http://www.OpenSSL.org/)"
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE OpenSSL PROJECT ``AS IS'' AND ANY
+ * EXPRESSED OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
+ * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OpenSSL PROJECT OR
+ * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
+ * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
+ * STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED
+ * OF THE POSSIBILITY OF SUCH DAMAGE.
+ *
+ *
+ * md5_dgst.c, md5_locl.h
+ * ====================================================================
+ *
+ * This product includes cryptographic software written by Eric Young
+ * (eay@cryptsoft.com).  This product includes software written by Tim
+ * Hudson (tjh@cryptsoft.com).
+ *
+ * Copyright (C) 1995-1998 Eric Young (eay@cryptsoft.com)
+ * All rights reserved.
+ *
+ * This package is an SSL implementation written
+ * by Eric Young (eay@cryptsoft.com).
+ * The implementation was written so as to conform with Netscapes SSL.
+ * 
+ * This library is free for commercial and non-commercial use as long as
+ * the following conditions are aheared to.  The following conditions
+ * apply to all code found in this distribution, be it the RC4, RSA,
+ * lhash, DES, etc., code; not just the SSL code.  The SSL documentation
+ * included with this distribution is covered by the same copyright terms
+ * except that the holder is Tim Hudson (tjh@cryptsoft.com).
+ * 
+ * Copyright remains Eric Young's, and as such any Copyright notices in
+ * the code are not to be removed.
+ * If this package is used in a product, Eric Young should be given attribution
+ * as the author of the parts of the library used.
+ * This can be in the form of a textual message at program startup or
+ * in documentation (online or textual) provided with the package.
+ * 
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the copyright
+ *    notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ *    notice, this list of conditions and the following disclaimer in the
+ *    documentation and/or other materials provided with the distribution.
+ * 3. All advertising materials mentioning features or use of this software
+ *    must display the following acknowledgement:
+ *    "This product includes cryptographic software written by
+ *     Eric Young (eay@cryptsoft.com)"
+ *    The word 'cryptographic' can be left out if the rouines from the library
+ *    being used are not cryptographic related :-).
+ * 4. If you include any Windows specific code (or a derivative thereof) from 
+ *    the apps directory (application code) you must include an acknowledgement:
+ *    "This product includes software written by Tim Hudson (tjh@cryptsoft.com)"
+ * 
+ * THIS SOFTWARE IS PROVIDED BY ERIC YOUNG ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ * 
+ * The licence and distribution terms for any publically available version or
+ * derivative of this code cannot be changed.  i.e. this code cannot simply be
+ * copied and put under another distribution licence
+ * [including the GNU Public Licence.]
+ *
+ */
+
+//from CommonDigest.h
+
+#define MD5_DIGEST_LENGTH	16			/* digest length in bytes */
+#define MD5_BLOCK_BYTES		64			/* block size in bytes */
+#define MD5_BLOCK_LONG       (MD5_BLOCK_BYTES / sizeof(mDNSu32))
+
+typedef struct MD5state_st
+{
+	mDNSu32 A,B,C,D;
+	mDNSu32 Nl,Nh;
+	mDNSu32 data[MD5_BLOCK_LONG];
+	int num;
+} MD5_CTX;
+
+#ifndef HAVE_MD5
+
+// from openssl/md5.h
+
+#define MD5_CBLOCK	64
+#define MD5_LBLOCK	(MD5_CBLOCK/4)
+#define MD5_DIGEST_LENGTH 16
+
+int MD5_Init(MD5_CTX *c);
+int MD5_Update(MD5_CTX *c, const void *data, unsigned long len);
+int MD5_Final(unsigned char *md, MD5_CTX *c);
+void MD5_Transform(MD5_CTX *c, const unsigned char *b);
+
+// From md5_locl.h
+
+#ifndef MD5_LONG_LOG2
+#define MD5_LONG_LOG2 2 /* default to 32 bits */
+#endif
+
+#ifdef MD5_ASM
+# if defined(__i386) || defined(__i386__) || defined(_M_IX86) || defined(__INTEL__)
+#  define md5_block_host_order md5_block_asm_host_order
+# elif defined(__sparc) && defined(OPENSSL_SYS_ULTRASPARC)
+   void md5_block_asm_data_order_aligned (MD5_CTX *c, const mDNSu32 *p,int num);
+#  define HASH_BLOCK_DATA_ORDER_ALIGNED md5_block_asm_data_order_aligned
+# endif
+#endif
+
+void md5_block_host_order (MD5_CTX *c, const void *p,int num);
+void md5_block_data_order (MD5_CTX *c, const void *p,int num);
+
+#if defined(__i386) || defined(__i386__) || defined(_M_IX86) || defined(__INTEL__)
+/*
+ * *_block_host_order is expected to handle aligned data while
+ * *_block_data_order - unaligned. As algorithm and host (x86)
+ * are in this case of the same "endianness" these two are
+ * otherwise indistinguishable. But normally you don't want to
+ * call the same function because unaligned access in places
+ * where alignment is expected is usually a "Bad Thing". Indeed,
+ * on RISCs you get punished with BUS ERROR signal or *severe*
+ * performance degradation. Intel CPUs are in turn perfectly
+ * capable of loading unaligned data without such drastic side
+ * effect. Yes, they say it's slower than aligned load, but no
+ * exception is generated and therefore performance degradation
+ * is *incomparable* with RISCs. What we should weight here is
+ * costs of unaligned access against costs of aligning data.
+ * According to my measurements allowing unaligned access results
+ * in ~9% performance improvement on Pentium II operating at
+ * 266MHz. I won't be surprised if the difference will be higher
+ * on faster systems:-)
+ *
+ *				<appro@fy.chalmers.se>
+ */
+#define md5_block_data_order md5_block_host_order
+#endif
+
+#define DATA_ORDER_IS_LITTLE_ENDIAN
+
+#define HASH_LONG		mDNSu32
+#define HASH_LONG_LOG2	MD5_LONG_LOG2
+#define HASH_CTX		MD5_CTX
+#define HASH_CBLOCK		MD5_CBLOCK
+#define HASH_LBLOCK		MD5_LBLOCK
+
+#define HASH_UPDATE		MD5_Update
+#define HASH_TRANSFORM	MD5_Transform
+#define HASH_FINAL		MD5_Final
+
+#define	HASH_MAKE_STRING(c,s)	do {	\
+	unsigned long ll;		\
+	ll=(c)->A; HOST_l2c(ll,(s));	\
+	ll=(c)->B; HOST_l2c(ll,(s));	\
+	ll=(c)->C; HOST_l2c(ll,(s));	\
+	ll=(c)->D; HOST_l2c(ll,(s));	\
+	} while (0)
+#define HASH_BLOCK_HOST_ORDER	md5_block_host_order
+#if !defined(L_ENDIAN) || defined(md5_block_data_order)
+#define	HASH_BLOCK_DATA_ORDER	md5_block_data_order
+/*
+ * Little-endians (Intel and Alpha) feel better without this.
+ * It looks like memcpy does better job than generic
+ * md5_block_data_order on copying-n-aligning input data.
+ * But frankly speaking I didn't expect such result on Alpha.
+ * On the other hand I've got this with egcs-1.0.2 and if
+ * program is compiled with another (better?) compiler it
+ * might turn out other way around.
+ *
+ *				<appro@fy.chalmers.se>
+ */
+#endif
+
+
+// from md32_common.h
+
+/*
+ * This is a generic 32 bit "collector" for message digest algorithms.
+ * Whenever needed it collects input character stream into chunks of
+ * 32 bit values and invokes a block function that performs actual hash
+ * calculations.
+ *
+ * Porting guide.
+ *
+ * Obligatory macros:
+ *
+ * DATA_ORDER_IS_BIG_ENDIAN or DATA_ORDER_IS_LITTLE_ENDIAN
+ *	this macro defines byte order of input stream.
+ * HASH_CBLOCK
+ *	size of a unit chunk HASH_BLOCK operates on.
+ * HASH_LONG
+ *	has to be at lest 32 bit wide, if it's wider, then
+ *	HASH_LONG_LOG2 *has to* be defined along
+ * HASH_CTX
+ *	context structure that at least contains following
+ *	members:
+ *		typedef struct {
+ *			...
+ *			HASH_LONG	Nl,Nh;
+ *			HASH_LONG	data[HASH_LBLOCK];
+ *			int		num;
+ *			...
+ *			} HASH_CTX;
+ * HASH_UPDATE
+ *	name of "Update" function, implemented here.
+ * HASH_TRANSFORM
+ *	name of "Transform" function, implemented here.
+ * HASH_FINAL
+ *	name of "Final" function, implemented here.
+ * HASH_BLOCK_HOST_ORDER
+ *	name of "block" function treating *aligned* input message
+ *	in host byte order, implemented externally.
+ * HASH_BLOCK_DATA_ORDER
+ *	name of "block" function treating *unaligned* input message
+ *	in original (data) byte order, implemented externally (it
+ *	actually is optional if data and host are of the same
+ *	"endianess").
+ * HASH_MAKE_STRING
+ *	macro convering context variables to an ASCII hash string.
+ *
+ * Optional macros:
+ *
+ * B_ENDIAN or L_ENDIAN
+ *	defines host byte-order.
+ * HASH_LONG_LOG2
+ *	defaults to 2 if not states otherwise.
+ * HASH_LBLOCK
+ *	assumed to be HASH_CBLOCK/4 if not stated otherwise.
+ * HASH_BLOCK_DATA_ORDER_ALIGNED
+ *	alternative "block" function capable of treating
+ *	aligned input message in original (data) order,
+ *	implemented externally.
+ *
+ * MD5 example:
+ *
+ *	#define DATA_ORDER_IS_LITTLE_ENDIAN
+ *
+ *	#define HASH_LONG		mDNSu32
+ *	#define HASH_LONG_LOG2	mDNSu32_LOG2
+ *	#define HASH_CTX		MD5_CTX
+ *	#define HASH_CBLOCK		MD5_CBLOCK
+ *	#define HASH_LBLOCK		MD5_LBLOCK
+ *	#define HASH_UPDATE		MD5_Update
+ *	#define HASH_TRANSFORM		MD5_Transform
+ *	#define HASH_FINAL		MD5_Final
+ *	#define HASH_BLOCK_HOST_ORDER	md5_block_host_order
+ *	#define HASH_BLOCK_DATA_ORDER	md5_block_data_order
+ *
+ *					<appro@fy.chalmers.se>
+ */
+
+#if !defined(DATA_ORDER_IS_BIG_ENDIAN) && !defined(DATA_ORDER_IS_LITTLE_ENDIAN)
+#error "DATA_ORDER must be defined!"
+#endif
+
+#ifndef HASH_CBLOCK
+#error "HASH_CBLOCK must be defined!"
+#endif
+#ifndef HASH_LONG
+#error "HASH_LONG must be defined!"
+#endif
+#ifndef HASH_CTX
+#error "HASH_CTX must be defined!"
+#endif
+
+#ifndef HASH_UPDATE
+#error "HASH_UPDATE must be defined!"
+#endif
+#ifndef HASH_TRANSFORM
+#error "HASH_TRANSFORM must be defined!"
+#endif
+#ifndef HASH_FINAL
+#error "HASH_FINAL must be defined!"
+#endif
+
+#ifndef HASH_BLOCK_HOST_ORDER
+#error "HASH_BLOCK_HOST_ORDER must be defined!"
+#endif
+
+#if 0
+/*
+ * Moved below as it's required only if HASH_BLOCK_DATA_ORDER_ALIGNED
+ * isn't defined.
+ */
+#ifndef HASH_BLOCK_DATA_ORDER
+#error "HASH_BLOCK_DATA_ORDER must be defined!"
+#endif
+#endif
+
+#ifndef HASH_LBLOCK
+#define HASH_LBLOCK	(HASH_CBLOCK/4)
+#endif
+
+#ifndef HASH_LONG_LOG2
+#define HASH_LONG_LOG2	2
+#endif
+
+/*
+ * Engage compiler specific rotate intrinsic function if available.
+ */
+#undef ROTATE
+#ifndef PEDANTIC
+# if 0 /* defined(_MSC_VER) */
+#  define ROTATE(a,n)	_lrotl(a,n)
+# elif defined(__MWERKS__)
+#  if defined(__POWERPC__)
+#   define ROTATE(a,n)	(unsigned MD32_REG_T)__rlwinm((int)a,n,0,31)
+#  elif defined(__MC68K__)
+    /* Motorola specific tweak. <appro@fy.chalmers.se> */
+#   define ROTATE(a,n)	(n<24 ? __rol(a,n) : __ror(a,32-n))
+#  else
+#   define ROTATE(a,n)	__rol(a,n)
+#  endif
+# elif defined(__GNUC__) && __GNUC__>=2 && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM)
+  /*
+   * Some GNU C inline assembler templates. Note that these are
+   * rotates by *constant* number of bits! But that's exactly
+   * what we need here...
+   *
+   * 					<appro@fy.chalmers.se>
+   */
+  /*
+   * LLVM is more strict about compatibility of types between input & output constraints,
+   * but we want these to be rotations of 32 bits, not 64, so we explicitly drop the
+   * most significant bytes by casting to an unsigned int.
+   */
+#  if defined(__i386) || defined(__i386__) || defined(__x86_64) || defined(__x86_64__)
+#   define ROTATE(a,n)	({ register unsigned int ret;	\
+				asm (			\
+				"roll %1,%0"		\
+				: "=r"(ret)		\
+				: "I"(n), "0"((unsigned int)a)	\
+				: "cc");		\
+			   ret;				\
+			})
+#  elif defined(__powerpc) || defined(__ppc)
+#   define ROTATE(a,n)	({ register unsigned int ret;	\
+				asm (			\
+				"rlwinm %0,%1,%2,0,31"	\
+				: "=r"(ret)		\
+				: "r"(a), "I"(n));	\
+			   ret;				\
+			})
+#  endif
+# endif
+
+/*
+ * Engage compiler specific "fetch in reverse byte order"
+ * intrinsic function if available.
+ */
+# if defined(__GNUC__) && __GNUC__>=2 && !defined(OPENSSL_NO_ASM) && !defined(OPENSSL_NO_INLINE_ASM)
+  /* some GNU C inline assembler templates by <appro@fy.chalmers.se> */
+#  if (defined(__i386) || defined(__i386__) || defined(__x86_64) || defined(__x86_64__)) && !defined(I386_ONLY)
+#   define BE_FETCH32(a)	({ register unsigned int l=(a);\
+				asm (			\
+				"bswapl %0"		\
+				: "=r"(l) : "0"(l));	\
+			  l;				\
+			})
+#  elif defined(__powerpc)
+#   define LE_FETCH32(a)	({ register unsigned int l;	\
+				asm (			\
+				"lwbrx %0,0,%1"		\
+				: "=r"(l)		\
+				: "r"(a));		\
+			   l;				\
+			})
+
+#  elif defined(__sparc) && defined(OPENSSL_SYS_ULTRASPARC)
+#  define LE_FETCH32(a)	({ register unsigned int l;		\
+				asm (				\
+				"lda [%1]#ASI_PRIMARY_LITTLE,%0"\
+				: "=r"(l)			\
+				: "r"(a));			\
+			   l;					\
+			})
+#  endif
+# endif
+#endif /* PEDANTIC */
+
+#if HASH_LONG_LOG2==2	/* Engage only if sizeof(HASH_LONG)== 4 */
+/* A nice byte order reversal from Wei Dai <weidai@eskimo.com> */
+#ifdef ROTATE
+/* 5 instructions with rotate instruction, else 9 */
+#define REVERSE_FETCH32(a,l)	(					\
+		l=*(const HASH_LONG *)(a),				\
+		((ROTATE(l,8)&0x00FF00FF)|(ROTATE((l&0x00FF00FF),24)))	\
+				)
+#else
+/* 6 instructions with rotate instruction, else 8 */
+#define REVERSE_FETCH32(a,l)	(				\
+		l=*(const HASH_LONG *)(a),			\
+		l=(((l>>8)&0x00FF00FF)|((l&0x00FF00FF)<<8)),	\
+		ROTATE(l,16)					\
+				)
+/*
+ * Originally the middle line started with l=(((l&0xFF00FF00)>>8)|...
+ * It's rewritten as above for two reasons:
+ *	- RISCs aren't good at long constants and have to explicitely
+ *	  compose 'em with several (well, usually 2) instructions in a
+ *	  register before performing the actual operation and (as you
+ *	  already realized:-) having same constant should inspire the
+ *	  compiler to permanently allocate the only register for it;
+ *	- most modern CPUs have two ALUs, but usually only one has
+ *	  circuitry for shifts:-( this minor tweak inspires compiler
+ *	  to schedule shift instructions in a better way...
+ *
+ *				<appro@fy.chalmers.se>
+ */
+#endif
+#endif
+
+#ifndef ROTATE
+#define ROTATE(a,n)     (((a)<<(n))|(((a)&0xffffffff)>>(32-(n))))
+#endif
+
+/*
+ * Make some obvious choices. E.g., HASH_BLOCK_DATA_ORDER_ALIGNED
+ * and HASH_BLOCK_HOST_ORDER ought to be the same if input data
+ * and host are of the same "endianess". It's possible to mask
+ * this with blank #define HASH_BLOCK_DATA_ORDER though...
+ *
+ *				<appro@fy.chalmers.se>
+ */
+#if defined(B_ENDIAN)
+#  if defined(DATA_ORDER_IS_BIG_ENDIAN)
+#    if !defined(HASH_BLOCK_DATA_ORDER_ALIGNED) && HASH_LONG_LOG2==2
+#      define HASH_BLOCK_DATA_ORDER_ALIGNED	HASH_BLOCK_HOST_ORDER
+#    endif
+#  elif defined(DATA_ORDER_IS_LITTLE_ENDIAN)
+#    ifndef HOST_FETCH32
+#      ifdef LE_FETCH32
+#        define HOST_FETCH32(p,l)	LE_FETCH32(p)
+#      elif defined(REVERSE_FETCH32)
+#        define HOST_FETCH32(p,l)	REVERSE_FETCH32(p,l)
+#      endif
+#    endif
+#  endif
+#elif defined(L_ENDIAN)
+#  if defined(DATA_ORDER_IS_LITTLE_ENDIAN)
+#    if !defined(HASH_BLOCK_DATA_ORDER_ALIGNED) && HASH_LONG_LOG2==2
+#      define HASH_BLOCK_DATA_ORDER_ALIGNED	HASH_BLOCK_HOST_ORDER
+#    endif
+#  elif defined(DATA_ORDER_IS_BIG_ENDIAN)
+#    ifndef HOST_FETCH32
+#      ifdef BE_FETCH32
+#        define HOST_FETCH32(p,l)	BE_FETCH32(p)
+#      elif defined(REVERSE_FETCH32)
+#        define HOST_FETCH32(p,l)	REVERSE_FETCH32(p,l)
+#      endif
+#    endif
+#  endif
+#endif
+
+#if !defined(HASH_BLOCK_DATA_ORDER_ALIGNED)
+#ifndef HASH_BLOCK_DATA_ORDER
+#error "HASH_BLOCK_DATA_ORDER must be defined!"
+#endif
+#endif
+
+#if defined(DATA_ORDER_IS_BIG_ENDIAN)
+
+#define HOST_c2l(c,l)	(l =(((unsigned long)(*((c)++)))<<24),		\
+			 l|=(((unsigned long)(*((c)++)))<<16),		\
+			 l|=(((unsigned long)(*((c)++)))<< 8),		\
+			 l|=(((unsigned long)(*((c)++)))    ),		\
+			 l)
+#define HOST_p_c2l(c,l,n)	{					\
+			switch (n) {					\
+			case 0: l =((unsigned long)(*((c)++)))<<24;	\
+			case 1: l|=((unsigned long)(*((c)++)))<<16;	\
+			case 2: l|=((unsigned long)(*((c)++)))<< 8;	\
+			case 3: l|=((unsigned long)(*((c)++)));		\
+				} }
+#define HOST_p_c2l_p(c,l,sc,len) {					\
+			switch (sc) {					\
+			case 0: l =((unsigned long)(*((c)++)))<<24;	\
+				if (--len == 0) break;			\
+			case 1: l|=((unsigned long)(*((c)++)))<<16;	\
+				if (--len == 0) break;			\
+			case 2: l|=((unsigned long)(*((c)++)))<< 8;	\
+				} }
+/* NOTE the pointer is not incremented at the end of this */
+#define HOST_c2l_p(c,l,n)	{					\
+			l=0; (c)+=n;					\
+			switch (n) {					\
+			case 3: l =((unsigned long)(*(--(c))))<< 8;	\
+			case 2: l|=((unsigned long)(*(--(c))))<<16;	\
+			case 1: l|=((unsigned long)(*(--(c))))<<24;	\
+				} }
+#define HOST_l2c(l,c)	(*((c)++)=(unsigned char)(((l)>>24)&0xff),	\
+			 *((c)++)=(unsigned char)(((l)>>16)&0xff),	\
+			 *((c)++)=(unsigned char)(((l)>> 8)&0xff),	\
+			 *((c)++)=(unsigned char)(((l)    )&0xff),	\
+			 l)
+
+#elif defined(DATA_ORDER_IS_LITTLE_ENDIAN)
+
+#define HOST_c2l(c,l)	(l =(((unsigned long)(*((c)++)))    ),		\
+			 l|=(((unsigned long)(*((c)++)))<< 8),		\
+			 l|=(((unsigned long)(*((c)++)))<<16),		\
+			 l|=(((unsigned long)(*((c)++)))<<24),		\
+			 l)
+#define HOST_p_c2l(c,l,n)	{					\
+			switch (n) {					\
+			case 0: l =((unsigned long)(*((c)++)));		\
+			case 1: l|=((unsigned long)(*((c)++)))<< 8;	\
+			case 2: l|=((unsigned long)(*((c)++)))<<16;	\
+			case 3: l|=((unsigned long)(*((c)++)))<<24;	\
+				} }
+#define HOST_p_c2l_p(c,l,sc,len) {					\
+			switch (sc) {					\
+			case 0: l =((unsigned long)(*((c)++)));		\
+				if (--len == 0) break;			\
+			case 1: l|=((unsigned long)(*((c)++)))<< 8;	\
+				if (--len == 0) break;			\
+			case 2: l|=((unsigned long)(*((c)++)))<<16;	\
+				} }
+/* NOTE the pointer is not incremented at the end of this */
+#define HOST_c2l_p(c,l,n)	{					\
+			l=0; (c)+=n;					\
+			switch (n) {					\
+			case 3: l =((unsigned long)(*(--(c))))<<16;	\
+			case 2: l|=((unsigned long)(*(--(c))))<< 8;	\
+			case 1: l|=((unsigned long)(*(--(c))));		\
+				} }
+#define HOST_l2c(l,c)	(*((c)++)=(unsigned char)(((l)    )&0xff),	\
+			 *((c)++)=(unsigned char)(((l)>> 8)&0xff),	\
+			 *((c)++)=(unsigned char)(((l)>>16)&0xff),	\
+			 *((c)++)=(unsigned char)(((l)>>24)&0xff),	\
+			 l)
+
+#endif
+
+/*
+ * Time for some action:-)
+ */
+
+int HASH_UPDATE (HASH_CTX *c, const void *data_, unsigned long len)
+	{
+	const unsigned char *data=(const unsigned char *)data_;
+	register HASH_LONG * p;
+	register unsigned long l;
+	int sw,sc,ew,ec;
+
+	if (len==0) return 1;
+
+	l=(c->Nl+(len<<3))&0xffffffffL;
+	/* 95-05-24 eay Fixed a bug with the overflow handling, thanks to
+	 * Wei Dai <weidai@eskimo.com> for pointing it out. */
+	if (l < c->Nl) /* overflow */
+		c->Nh++;
+	c->Nh+=(len>>29);
+	c->Nl=l;
+
+	if (c->num != 0)
+		{
+		p=c->data;
+		sw=c->num>>2;
+		sc=c->num&0x03;
+
+		if ((c->num+len) >= HASH_CBLOCK)
+			{
+			l=p[sw]; HOST_p_c2l(data,l,sc); p[sw++]=l;
+			for (; sw<HASH_LBLOCK; sw++)
+				{
+				HOST_c2l(data,l); p[sw]=l;
+				}
+			HASH_BLOCK_HOST_ORDER (c,p,1);
+			len-=(HASH_CBLOCK-c->num);
+			c->num=0;
+			/* drop through and do the rest */
+			}
+		else
+			{
+			c->num+=len;
+			if ((sc+len) < 4) /* ugly, add char's to a word */
+				{
+				l=p[sw]; HOST_p_c2l_p(data,l,sc,len); p[sw]=l;
+				}
+			else
+				{
+				ew=(c->num>>2);
+				ec=(c->num&0x03);
+				if (sc)
+					l=p[sw];
+				HOST_p_c2l(data,l,sc);
+				p[sw++]=l;
+				for (; sw < ew; sw++)
+					{
+					HOST_c2l(data,l); p[sw]=l;
+					}
+				if (ec)
+					{
+					HOST_c2l_p(data,l,ec); p[sw]=l;
+					}
+				}
+			return 1;
+			}
+		}
+
+	sw=(int)(len/HASH_CBLOCK);
+	if (sw > 0)
+		{
+#if defined(HASH_BLOCK_DATA_ORDER_ALIGNED)
+		/*
+		 * Note that HASH_BLOCK_DATA_ORDER_ALIGNED gets defined
+		 * only if sizeof(HASH_LONG)==4.
+		 */
+		if ((((unsigned long)data)%4) == 0)
+			{
+			/* data is properly aligned so that we can cast it: */
+			HASH_BLOCK_DATA_ORDER_ALIGNED (c,(HASH_LONG *)data,sw);
+			sw*=HASH_CBLOCK;
+			data+=sw;
+			len-=sw;
+			}
+		else
+#if !defined(HASH_BLOCK_DATA_ORDER)
+			while (sw--)
+				{
+				mDNSPlatformMemCopy(p=c->data,data,HASH_CBLOCK);
+				HASH_BLOCK_DATA_ORDER_ALIGNED(c,p,1);
+				data+=HASH_CBLOCK;
+				len-=HASH_CBLOCK;
+				}
+#endif
+#endif
+#if defined(HASH_BLOCK_DATA_ORDER)
+			{
+			HASH_BLOCK_DATA_ORDER(c,data,sw);
+			sw*=HASH_CBLOCK;
+			data+=sw;
+			len-=sw;
+			}
+#endif
+		}
+
+	if (len!=0)
+		{
+		p = c->data;
+		c->num = (int)len;
+		ew=(int)(len>>2);	/* words to copy */
+		ec=(int)(len&0x03);
+		for (; ew; ew--,p++)
+			{
+			HOST_c2l(data,l); *p=l;
+			}
+		HOST_c2l_p(data,l,ec);
+		*p=l;
+		}
+	return 1;
+	}
+
+
+void HASH_TRANSFORM (HASH_CTX *c, const unsigned char *data)
+	{
+#if defined(HASH_BLOCK_DATA_ORDER_ALIGNED)
+	if ((((unsigned long)data)%4) == 0)
+		/* data is properly aligned so that we can cast it: */
+		HASH_BLOCK_DATA_ORDER_ALIGNED (c,(HASH_LONG *)data,1);
+	else
+#if !defined(HASH_BLOCK_DATA_ORDER)
+		{
+		mDNSPlatformMemCopy(c->data,data,HASH_CBLOCK);
+		HASH_BLOCK_DATA_ORDER_ALIGNED (c,c->data,1);
+		}
+#endif
+#endif
+#if defined(HASH_BLOCK_DATA_ORDER)
+	HASH_BLOCK_DATA_ORDER (c,data,1);
+#endif
+	}
+
+
+int HASH_FINAL (unsigned char *md, HASH_CTX *c)
+	{
+	register HASH_LONG *p;
+	register unsigned long l;
+	register int i,j;
+	static const unsigned char end[4]={0x80,0x00,0x00,0x00};
+	const unsigned char *cp=end;
+
+	/* c->num should definitly have room for at least one more byte. */
+	p=c->data;
+	i=c->num>>2;
+	j=c->num&0x03;
+
+#if 0
+	/* purify often complains about the following line as an
+	 * Uninitialized Memory Read.  While this can be true, the
+	 * following p_c2l macro will reset l when that case is true.
+	 * This is because j&0x03 contains the number of 'valid' bytes
+	 * already in p[i].  If and only if j&0x03 == 0, the UMR will
+	 * occur but this is also the only time p_c2l will do
+	 * l= *(cp++) instead of l|= *(cp++)
+	 * Many thanks to Alex Tang <altitude@cic.net> for pickup this
+	 * 'potential bug' */
+#ifdef PURIFY
+	if (j==0) p[i]=0; /* Yeah, but that's not the way to fix it:-) */
+#endif
+	l=p[i];
+#else
+	l = (j==0) ? 0 : p[i];
+#endif
+	HOST_p_c2l(cp,l,j); p[i++]=l; /* i is the next 'undefined word' */
+
+	if (i>(HASH_LBLOCK-2)) /* save room for Nl and Nh */
+		{
+		if (i<HASH_LBLOCK) p[i]=0;
+		HASH_BLOCK_HOST_ORDER (c,p,1);
+		i=0;
+		}
+	for (; i<(HASH_LBLOCK-2); i++)
+		p[i]=0;
+
+#if   defined(DATA_ORDER_IS_BIG_ENDIAN)
+	p[HASH_LBLOCK-2]=c->Nh;
+	p[HASH_LBLOCK-1]=c->Nl;
+#elif defined(DATA_ORDER_IS_LITTLE_ENDIAN)
+	p[HASH_LBLOCK-2]=c->Nl;
+	p[HASH_LBLOCK-1]=c->Nh;
+#endif
+	HASH_BLOCK_HOST_ORDER (c,p,1);
+
+#ifndef HASH_MAKE_STRING
+#error "HASH_MAKE_STRING must be defined!"
+#else
+	HASH_MAKE_STRING(c,md);
+#endif
+
+	c->num=0;
+	/* clear stuff, HASH_BLOCK may be leaving some stuff on the stack
+	 * but I'm not worried :-)
+	OPENSSL_cleanse((void *)c,sizeof(HASH_CTX));
+	 */
+	return 1;
+	}
+
+#ifndef MD32_REG_T
+#define MD32_REG_T long
+/*
+ * This comment was originaly written for MD5, which is why it
+ * discusses A-D. But it basically applies to all 32-bit digests,
+ * which is why it was moved to common header file.
+ *
+ * In case you wonder why A-D are declared as long and not
+ * as mDNSu32. Doing so results in slight performance
+ * boost on LP64 architectures. The catch is we don't
+ * really care if 32 MSBs of a 64-bit register get polluted
+ * with eventual overflows as we *save* only 32 LSBs in
+ * *either* case. Now declaring 'em long excuses the compiler
+ * from keeping 32 MSBs zeroed resulting in 13% performance
+ * improvement under SPARC Solaris7/64 and 5% under AlphaLinux.
+ * Well, to be honest it should say that this *prevents* 
+ * performance degradation.
+ *				<appro@fy.chalmers.se>
+ * Apparently there're LP64 compilers that generate better
+ * code if A-D are declared int. Most notably GCC-x86_64
+ * generates better code.
+ *				<appro@fy.chalmers.se>
+ */
+#endif
+
+
+// from md5_locl.h (continued)
+
+/*
+#define	F(x,y,z)	(((x) & (y))  |  ((~(x)) & (z)))
+#define	G(x,y,z)	(((x) & (z))  |  ((y) & (~(z))))
+*/
+
+/* As pointed out by Wei Dai <weidai@eskimo.com>, the above can be
+ * simplified to the code below.  Wei attributes these optimizations
+ * to Peter Gutmann's SHS code, and he attributes it to Rich Schroeppel.
+ */
+#define	F(b,c,d)	((((c) ^ (d)) & (b)) ^ (d))
+#define	G(b,c,d)	((((b) ^ (c)) & (d)) ^ (c))
+#define	H(b,c,d)	((b) ^ (c) ^ (d))
+#define	I(b,c,d)	(((~(d)) | (b)) ^ (c))
+
+#define R0(a,b,c,d,k,s,t) { \
+	a+=((k)+(t)+F((b),(c),(d))); \
+	a=ROTATE(a,s); \
+	a+=b; };\
+
+#define R1(a,b,c,d,k,s,t) { \
+	a+=((k)+(t)+G((b),(c),(d))); \
+	a=ROTATE(a,s); \
+	a+=b; };
+
+#define R2(a,b,c,d,k,s,t) { \
+	a+=((k)+(t)+H((b),(c),(d))); \
+	a=ROTATE(a,s); \
+	a+=b; };
+
+#define R3(a,b,c,d,k,s,t) { \
+	a+=((k)+(t)+I((b),(c),(d))); \
+	a=ROTATE(a,s); \
+	a+=b; };
+	
+// from md5_dgst.c
+
+
+/* Implemented from RFC1321 The MD5 Message-Digest Algorithm
+ */
+
+#define INIT_DATA_A (unsigned long)0x67452301L
+#define INIT_DATA_B (unsigned long)0xefcdab89L
+#define INIT_DATA_C (unsigned long)0x98badcfeL
+#define INIT_DATA_D (unsigned long)0x10325476L
+
+int MD5_Init(MD5_CTX *c)
+	{
+	c->A=INIT_DATA_A;
+	c->B=INIT_DATA_B;
+	c->C=INIT_DATA_C;
+	c->D=INIT_DATA_D;
+	c->Nl=0;
+	c->Nh=0;
+	c->num=0;
+	return 1;
+	}
+
+#ifndef md5_block_host_order
+void md5_block_host_order (MD5_CTX *c, const void *data, int num)
+	{
+	const mDNSu32 *X=(const mDNSu32 *)data;
+	register unsigned MD32_REG_T A,B,C,D;
+
+	A=c->A;
+	B=c->B;
+	C=c->C;
+	D=c->D;
+
+	for (;num--;X+=HASH_LBLOCK)
+		{
+	/* Round 0 */
+	R0(A,B,C,D,X[ 0], 7,0xd76aa478L);
+	R0(D,A,B,C,X[ 1],12,0xe8c7b756L);
+	R0(C,D,A,B,X[ 2],17,0x242070dbL);
+	R0(B,C,D,A,X[ 3],22,0xc1bdceeeL);
+	R0(A,B,C,D,X[ 4], 7,0xf57c0fafL);
+	R0(D,A,B,C,X[ 5],12,0x4787c62aL);
+	R0(C,D,A,B,X[ 6],17,0xa8304613L);
+	R0(B,C,D,A,X[ 7],22,0xfd469501L);
+	R0(A,B,C,D,X[ 8], 7,0x698098d8L);
+	R0(D,A,B,C,X[ 9],12,0x8b44f7afL);
+	R0(C,D,A,B,X[10],17,0xffff5bb1L);
+	R0(B,C,D,A,X[11],22,0x895cd7beL);
+	R0(A,B,C,D,X[12], 7,0x6b901122L);
+	R0(D,A,B,C,X[13],12,0xfd987193L);
+	R0(C,D,A,B,X[14],17,0xa679438eL);
+	R0(B,C,D,A,X[15],22,0x49b40821L);
+	/* Round 1 */
+	R1(A,B,C,D,X[ 1], 5,0xf61e2562L);
+	R1(D,A,B,C,X[ 6], 9,0xc040b340L);
+	R1(C,D,A,B,X[11],14,0x265e5a51L);
+	R1(B,C,D,A,X[ 0],20,0xe9b6c7aaL);
+	R1(A,B,C,D,X[ 5], 5,0xd62f105dL);
+	R1(D,A,B,C,X[10], 9,0x02441453L);
+	R1(C,D,A,B,X[15],14,0xd8a1e681L);
+	R1(B,C,D,A,X[ 4],20,0xe7d3fbc8L);
+	R1(A,B,C,D,X[ 9], 5,0x21e1cde6L);
+	R1(D,A,B,C,X[14], 9,0xc33707d6L);
+	R1(C,D,A,B,X[ 3],14,0xf4d50d87L);
+	R1(B,C,D,A,X[ 8],20,0x455a14edL);
+	R1(A,B,C,D,X[13], 5,0xa9e3e905L);
+	R1(D,A,B,C,X[ 2], 9,0xfcefa3f8L);
+	R1(C,D,A,B,X[ 7],14,0x676f02d9L);
+	R1(B,C,D,A,X[12],20,0x8d2a4c8aL);
+	/* Round 2 */
+	R2(A,B,C,D,X[ 5], 4,0xfffa3942L);
+	R2(D,A,B,C,X[ 8],11,0x8771f681L);
+	R2(C,D,A,B,X[11],16,0x6d9d6122L);
+	R2(B,C,D,A,X[14],23,0xfde5380cL);
+	R2(A,B,C,D,X[ 1], 4,0xa4beea44L);
+	R2(D,A,B,C,X[ 4],11,0x4bdecfa9L);
+	R2(C,D,A,B,X[ 7],16,0xf6bb4b60L);
+	R2(B,C,D,A,X[10],23,0xbebfbc70L);
+	R2(A,B,C,D,X[13], 4,0x289b7ec6L);
+	R2(D,A,B,C,X[ 0],11,0xeaa127faL);
+	R2(C,D,A,B,X[ 3],16,0xd4ef3085L);
+	R2(B,C,D,A,X[ 6],23,0x04881d05L);
+	R2(A,B,C,D,X[ 9], 4,0xd9d4d039L);
+	R2(D,A,B,C,X[12],11,0xe6db99e5L);
+	R2(C,D,A,B,X[15],16,0x1fa27cf8L);
+	R2(B,C,D,A,X[ 2],23,0xc4ac5665L);
+	/* Round 3 */
+	R3(A,B,C,D,X[ 0], 6,0xf4292244L);
+	R3(D,A,B,C,X[ 7],10,0x432aff97L);
+	R3(C,D,A,B,X[14],15,0xab9423a7L);
+	R3(B,C,D,A,X[ 5],21,0xfc93a039L);
+	R3(A,B,C,D,X[12], 6,0x655b59c3L);
+	R3(D,A,B,C,X[ 3],10,0x8f0ccc92L);
+	R3(C,D,A,B,X[10],15,0xffeff47dL);
+	R3(B,C,D,A,X[ 1],21,0x85845dd1L);
+	R3(A,B,C,D,X[ 8], 6,0x6fa87e4fL);
+	R3(D,A,B,C,X[15],10,0xfe2ce6e0L);
+	R3(C,D,A,B,X[ 6],15,0xa3014314L);
+	R3(B,C,D,A,X[13],21,0x4e0811a1L);
+	R3(A,B,C,D,X[ 4], 6,0xf7537e82L);
+	R3(D,A,B,C,X[11],10,0xbd3af235L);
+	R3(C,D,A,B,X[ 2],15,0x2ad7d2bbL);
+	R3(B,C,D,A,X[ 9],21,0xeb86d391L);
+
+	A = c->A += A;
+	B = c->B += B;
+	C = c->C += C;
+	D = c->D += D;
+		}
+	}
+#endif
+
+#ifndef md5_block_data_order
+#ifdef X
+#undef X
+#endif
+void md5_block_data_order (MD5_CTX *c, const void *data_, int num)
+	{
+	const unsigned char *data=data_;
+	register unsigned MD32_REG_T A,B,C,D,l;
+#ifndef MD32_XARRAY
+	/* See comment in crypto/sha/sha_locl.h for details. */
+	unsigned MD32_REG_T	XX0, XX1, XX2, XX3, XX4, XX5, XX6, XX7,
+				XX8, XX9,XX10,XX11,XX12,XX13,XX14,XX15;
+# define X(i)	XX##i
+#else
+	mDNSu32 XX[MD5_LBLOCK];
+# define X(i)	XX[i]
+#endif
+
+	A=c->A;
+	B=c->B;
+	C=c->C;
+	D=c->D;
+
+	for (;num--;)
+		{
+	HOST_c2l(data,l); X( 0)=l;		HOST_c2l(data,l); X( 1)=l;
+	/* Round 0 */
+	R0(A,B,C,D,X( 0), 7,0xd76aa478L);	HOST_c2l(data,l); X( 2)=l;
+	R0(D,A,B,C,X( 1),12,0xe8c7b756L);	HOST_c2l(data,l); X( 3)=l;
+	R0(C,D,A,B,X( 2),17,0x242070dbL);	HOST_c2l(data,l); X( 4)=l;
+	R0(B,C,D,A,X( 3),22,0xc1bdceeeL);	HOST_c2l(data,l); X( 5)=l;
+	R0(A,B,C,D,X( 4), 7,0xf57c0fafL);	HOST_c2l(data,l); X( 6)=l;
+	R0(D,A,B,C,X( 5),12,0x4787c62aL);	HOST_c2l(data,l); X( 7)=l;
+	R0(C,D,A,B,X( 6),17,0xa8304613L);	HOST_c2l(data,l); X( 8)=l;
+	R0(B,C,D,A,X( 7),22,0xfd469501L);	HOST_c2l(data,l); X( 9)=l;
+	R0(A,B,C,D,X( 8), 7,0x698098d8L);	HOST_c2l(data,l); X(10)=l;
+	R0(D,A,B,C,X( 9),12,0x8b44f7afL);	HOST_c2l(data,l); X(11)=l;
+	R0(C,D,A,B,X(10),17,0xffff5bb1L);	HOST_c2l(data,l); X(12)=l;
+	R0(B,C,D,A,X(11),22,0x895cd7beL);	HOST_c2l(data,l); X(13)=l;
+	R0(A,B,C,D,X(12), 7,0x6b901122L);	HOST_c2l(data,l); X(14)=l;
+	R0(D,A,B,C,X(13),12,0xfd987193L);	HOST_c2l(data,l); X(15)=l;
+	R0(C,D,A,B,X(14),17,0xa679438eL);
+	R0(B,C,D,A,X(15),22,0x49b40821L);
+	/* Round 1 */
+	R1(A,B,C,D,X( 1), 5,0xf61e2562L);
+	R1(D,A,B,C,X( 6), 9,0xc040b340L);
+	R1(C,D,A,B,X(11),14,0x265e5a51L);
+	R1(B,C,D,A,X( 0),20,0xe9b6c7aaL);
+	R1(A,B,C,D,X( 5), 5,0xd62f105dL);
+	R1(D,A,B,C,X(10), 9,0x02441453L);
+	R1(C,D,A,B,X(15),14,0xd8a1e681L);
+	R1(B,C,D,A,X( 4),20,0xe7d3fbc8L);
+	R1(A,B,C,D,X( 9), 5,0x21e1cde6L);
+	R1(D,A,B,C,X(14), 9,0xc33707d6L);
+	R1(C,D,A,B,X( 3),14,0xf4d50d87L);
+	R1(B,C,D,A,X( 8),20,0x455a14edL);
+	R1(A,B,C,D,X(13), 5,0xa9e3e905L);
+	R1(D,A,B,C,X( 2), 9,0xfcefa3f8L);
+	R1(C,D,A,B,X( 7),14,0x676f02d9L);
+	R1(B,C,D,A,X(12),20,0x8d2a4c8aL);
+	/* Round 2 */
+	R2(A,B,C,D,X( 5), 4,0xfffa3942L);
+	R2(D,A,B,C,X( 8),11,0x8771f681L);
+	R2(C,D,A,B,X(11),16,0x6d9d6122L);
+	R2(B,C,D,A,X(14),23,0xfde5380cL);
+	R2(A,B,C,D,X( 1), 4,0xa4beea44L);
+	R2(D,A,B,C,X( 4),11,0x4bdecfa9L);
+	R2(C,D,A,B,X( 7),16,0xf6bb4b60L);
+	R2(B,C,D,A,X(10),23,0xbebfbc70L);
+	R2(A,B,C,D,X(13), 4,0x289b7ec6L);
+	R2(D,A,B,C,X( 0),11,0xeaa127faL);
+	R2(C,D,A,B,X( 3),16,0xd4ef3085L);
+	R2(B,C,D,A,X( 6),23,0x04881d05L);
+	R2(A,B,C,D,X( 9), 4,0xd9d4d039L);
+	R2(D,A,B,C,X(12),11,0xe6db99e5L);
+	R2(C,D,A,B,X(15),16,0x1fa27cf8L);
+	R2(B,C,D,A,X( 2),23,0xc4ac5665L);
+	/* Round 3 */
+	R3(A,B,C,D,X( 0), 6,0xf4292244L);
+	R3(D,A,B,C,X( 7),10,0x432aff97L);
+	R3(C,D,A,B,X(14),15,0xab9423a7L);
+	R3(B,C,D,A,X( 5),21,0xfc93a039L);
+	R3(A,B,C,D,X(12), 6,0x655b59c3L);
+	R3(D,A,B,C,X( 3),10,0x8f0ccc92L);
+	R3(C,D,A,B,X(10),15,0xffeff47dL);
+	R3(B,C,D,A,X( 1),21,0x85845dd1L);
+	R3(A,B,C,D,X( 8), 6,0x6fa87e4fL);
+	R3(D,A,B,C,X(15),10,0xfe2ce6e0L);
+	R3(C,D,A,B,X( 6),15,0xa3014314L);
+	R3(B,C,D,A,X(13),21,0x4e0811a1L);
+	R3(A,B,C,D,X( 4), 6,0xf7537e82L);
+	R3(D,A,B,C,X(11),10,0xbd3af235L);
+	R3(C,D,A,B,X( 2),15,0x2ad7d2bbL);
+	R3(B,C,D,A,X( 9),21,0xeb86d391L);
+
+	A = c->A += A;
+	B = c->B += B;
+	C = c->C += C;
+	D = c->D += D;
+		}
+	}
+#endif
+
+#endif  // !HAVE_MD5
+
+ // ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark - base64 -> binary conversion
+#endif
+
+static const char Base64[] = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/";
+static const char Pad64 = '=';
+
+
+#define mDNSisspace(x) (x == '\t' || x == '\n' || x == '\v' || x == '\f' || x == '\r' || x == ' ')
+
+mDNSlocal const char *mDNSstrchr(const char *s, int c)
+	{
+	while (1)
+		{
+		if (c == *s) return s;
+		if (!*s) return mDNSNULL;
+		s++;
+		}
+	}
+
+// skips all whitespace anywhere.
+// converts characters, four at a time, starting at (or after)
+// src from base - 64 numbers into three 8 bit bytes in the target area.
+// it returns the number of data bytes stored at the target, or -1 on error.
+// adapted from BIND sources
+
+mDNSlocal mDNSs32 DNSDigest_Base64ToBin(const char *src, mDNSu8 *target, mDNSu32 targsize)
+	{
+	int tarindex, state, ch;
+	const char *pos;
+
+	state = 0;
+	tarindex = 0;
+
+	while ((ch = *src++) != '\0') {
+		if (mDNSisspace(ch))	/* Skip whitespace anywhere. */
+			continue;
+
+		if (ch == Pad64)
+			break;
+
+		pos = mDNSstrchr(Base64, ch);
+		if (pos == 0) 		/* A non-base64 character. */
+			return (-1);
+
+		switch (state) {
+		case 0:
+			if (target) {
+				if ((mDNSu32)tarindex >= targsize)
+					return (-1);
+				target[tarindex] = (mDNSu8)((pos - Base64) << 2);
+			}
+			state = 1;
+			break;
+		case 1:
+			if (target) {
+				if ((mDNSu32)tarindex + 1 >= targsize)
+					return (-1);
+				target[tarindex]   |=  (pos - Base64) >> 4;
+				target[tarindex+1]  = (mDNSu8)(((pos - Base64) & 0x0f) << 4);
+			}
+			tarindex++;
+			state = 2;
+			break;
+		case 2:
+			if (target) {
+				if ((mDNSu32)tarindex + 1 >= targsize)
+					return (-1);
+				target[tarindex]   |=  (pos - Base64) >> 2;
+				target[tarindex+1]  = (mDNSu8)(((pos - Base64) & 0x03) << 6);
+			}
+			tarindex++;
+			state = 3;
+			break;
+		case 3:
+			if (target) {
+				if ((mDNSu32)tarindex >= targsize)
+					return (-1);
+				target[tarindex] |= (pos - Base64);
+			}
+			tarindex++;
+			state = 0;
+			break;
+		default:
+			return -1;
+		}
+	}
+
+	/*
+	 * We are done decoding Base-64 chars.  Let's see if we ended
+	 * on a byte boundary, and/or with erroneous trailing characters.
+	 */
+
+	if (ch == Pad64) {		/* We got a pad char. */
+		ch = *src++;		/* Skip it, get next. */
+		switch (state) {
+		case 0:		/* Invalid = in first position */
+		case 1:		/* Invalid = in second position */
+			return (-1);
+
+		case 2:		/* Valid, means one byte of info */
+			/* Skip any number of spaces. */
+			for ((void)mDNSNULL; ch != '\0'; ch = *src++)
+				if (!mDNSisspace(ch))
+					break;
+			/* Make sure there is another trailing = sign. */
+			if (ch != Pad64)
+				return (-1);
+			ch = *src++;		/* Skip the = */
+			/* Fall through to "single trailing =" case. */
+			/* FALLTHROUGH */
+
+		case 3:		/* Valid, means two bytes of info */
+			/*
+			 * We know this char is an =.  Is there anything but
+			 * whitespace after it?
+			 */
+			for ((void)mDNSNULL; ch != '\0'; ch = *src++)
+				if (!mDNSisspace(ch))
+					return (-1);
+
+			/*
+			 * Now make sure for cases 2 and 3 that the "extra"
+			 * bits that slopped past the last full byte were
+			 * zeros.  If we don't check them, they become a
+			 * subliminal channel.
+			 */
+			if (target && target[tarindex] != 0)
+				return (-1);
+		}
+	} else {
+		/*
+		 * We ended by seeing the end of the string.  Make sure we
+		 * have no partial bytes lying around.
+		 */
+		if (state != 0)
+			return (-1);
+		}
+
+	return (tarindex);
+	}
+
+
+ // ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark - API exported to mDNS Core
+#endif
+
+// Constants
+#define HMAC_IPAD   0x36
+#define HMAC_OPAD   0x5c
+#define MD5_LEN     16
+
+#define HMAC_MD5_AlgName (*(const domainname*) "\010" "hmac-md5" "\007" "sig-alg" "\003" "reg" "\003" "int")
+
+// Adapted from Appendix, RFC 2104
+mDNSlocal void DNSDigest_ConstructHMACKey(DomainAuthInfo *info, const mDNSu8 *key, mDNSu32 len)		
+	{
+	MD5_CTX k;
+	mDNSu8 buf[MD5_LEN];
+	int i;
+	
+	// If key is longer than HMAC_LEN reset it to MD5(key)
+	if (len > HMAC_LEN)
+		{
+		MD5_Init(&k);
+		MD5_Update(&k, key, len);
+		MD5_Final(buf, &k);
+		key = buf;
+		len = MD5_LEN;
+		}
+
+	// store key in pads
+	mDNSPlatformMemZero(info->keydata_ipad, HMAC_LEN);
+	mDNSPlatformMemZero(info->keydata_opad, HMAC_LEN);
+	mDNSPlatformMemCopy(info->keydata_ipad, key, len);
+	mDNSPlatformMemCopy(info->keydata_opad, key, len);
+
+	// XOR key with ipad and opad values
+	for (i = 0; i < HMAC_LEN; i++)
+		{
+		info->keydata_ipad[i] ^= HMAC_IPAD;
+		info->keydata_opad[i] ^= HMAC_OPAD;
+		}
+
+	}
+
+mDNSexport mDNSs32 DNSDigest_ConstructHMACKeyfromBase64(DomainAuthInfo *info, const char *b64key)
+	{
+	mDNSu8 keybuf[1024];
+	mDNSs32 keylen = DNSDigest_Base64ToBin(b64key, keybuf, sizeof(keybuf));
+	if (keylen < 0) return(keylen);
+	DNSDigest_ConstructHMACKey(info, keybuf, (mDNSu32)keylen);
+	return(keylen);
+	}
+
+mDNSexport void DNSDigest_SignMessage(DNSMessage *msg, mDNSu8 **end, DomainAuthInfo *info, mDNSu16 tcode)
+	{
+	AuthRecord tsig;
+	mDNSu8  *rdata, *const countPtr = (mDNSu8 *)&msg->h.numAdditionals;	// Get existing numAdditionals value
+	mDNSu32 utc32;
+	mDNSu8 utc48[6];
+	mDNSu8 digest[MD5_LEN];
+	mDNSu8 *ptr = *end;
+	mDNSu32 len;
+	mDNSOpaque16 buf;
+	MD5_CTX c;
+	mDNSu16 numAdditionals = (mDNSu16)((mDNSu16)countPtr[0] << 8 | countPtr[1]);
+	
+	// Init MD5 context, digest inner key pad and message
+    MD5_Init(&c);
+    MD5_Update(&c, info->keydata_ipad, HMAC_LEN);
+	MD5_Update(&c, (mDNSu8 *)msg, (unsigned long)(*end - (mDNSu8 *)msg));
+	   
+	// Construct TSIG RR, digesting variables as apporpriate
+	mDNS_SetupResourceRecord(&tsig, mDNSNULL, 0, kDNSType_TSIG, 0, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
+
+	// key name
+	AssignDomainName(&tsig.namestorage, &info->keyname);
+	MD5_Update(&c, info->keyname.c, DomainNameLength(&info->keyname));
+
+	// class
+	tsig.resrec.rrclass = kDNSQClass_ANY;
+	buf = mDNSOpaque16fromIntVal(kDNSQClass_ANY);
+	MD5_Update(&c, buf.b, sizeof(mDNSOpaque16));
+
+	// ttl
+	tsig.resrec.rroriginalttl = 0;
+	MD5_Update(&c, (mDNSu8 *)&tsig.resrec.rroriginalttl, sizeof(tsig.resrec.rroriginalttl));
+	
+	// alg name
+	AssignDomainName(&tsig.resrec.rdata->u.name, &HMAC_MD5_AlgName);
+	len = DomainNameLength(&HMAC_MD5_AlgName);
+	rdata = tsig.resrec.rdata->u.data + len;
+	MD5_Update(&c, HMAC_MD5_AlgName.c, len);
+
+	// time
+	// get UTC (universal time), convert to 48-bit unsigned in network byte order
+	utc32 = (mDNSu32)mDNSPlatformUTC();
+	if (utc32 == (unsigned)-1) { LogMsg("ERROR: DNSDigest_SignMessage - mDNSPlatformUTC returned bad time -1"); *end = mDNSNULL; }
+	utc48[0] = 0;
+	utc48[1] = 0;
+	utc48[2] = (mDNSu8)((utc32 >> 24) & 0xff);
+	utc48[3] = (mDNSu8)((utc32 >> 16) & 0xff);
+	utc48[4] = (mDNSu8)((utc32 >>  8) & 0xff);
+	utc48[5] = (mDNSu8)( utc32        & 0xff);
+
+	mDNSPlatformMemCopy(rdata, utc48, 6);
+	rdata += 6;              	
+	MD5_Update(&c, utc48, 6);
+
+	// 300 sec is fudge recommended in RFC 2485
+	rdata[0] = (mDNSu8)((300 >> 8)  & 0xff);
+	rdata[1] = (mDNSu8)( 300        & 0xff);
+	MD5_Update(&c, rdata, sizeof(mDNSOpaque16));
+	rdata += sizeof(mDNSOpaque16);
+
+	// digest error (tcode) and other data len (zero) - we'll add them to the rdata later
+	buf.b[0] = (mDNSu8)((tcode >> 8) & 0xff);
+	buf.b[1] = (mDNSu8)( tcode       & 0xff);
+	MD5_Update(&c, buf.b, sizeof(mDNSOpaque16));  // error
+	buf.NotAnInteger = 0;
+	MD5_Update(&c, buf.b, sizeof(mDNSOpaque16));  // other data len
+
+	// finish the message & tsig var hash
+    MD5_Final(digest, &c);
+	
+	// perform outer MD5 (outer key pad, inner digest)
+	MD5_Init(&c);
+	MD5_Update(&c, info->keydata_opad, HMAC_LEN);
+	MD5_Update(&c, digest, MD5_LEN);
+	MD5_Final(digest, &c);
+
+	// set remaining rdata fields
+	rdata[0] = (mDNSu8)((MD5_LEN >> 8)  & 0xff);
+	rdata[1] = (mDNSu8)( MD5_LEN        & 0xff);
+	rdata += sizeof(mDNSOpaque16);
+	mDNSPlatformMemCopy(rdata, digest, MD5_LEN);                          // MAC
+	rdata += MD5_LEN;
+	rdata[0] = msg->h.id.b[0];                                            // original ID
+	rdata[1] = msg->h.id.b[1];
+	rdata[2] = (mDNSu8)((tcode >> 8) & 0xff);
+	rdata[3] = (mDNSu8)( tcode       & 0xff);
+	rdata[4] = 0;                                                         // other data len
+	rdata[5] = 0;
+	rdata += 6;
+	
+	tsig.resrec.rdlength = (mDNSu16)(rdata - tsig.resrec.rdata->u.data);
+	*end = PutResourceRecordTTLJumbo(msg, ptr, &numAdditionals, &tsig.resrec, 0);
+	if (!*end) { LogMsg("ERROR: DNSDigest_SignMessage - could not put TSIG"); *end = mDNSNULL; return; }
+
+	// Write back updated numAdditionals value
+	countPtr[0] = (mDNSu8)(numAdditionals >> 8);
+	countPtr[1] = (mDNSu8)(numAdditionals &  0xFF);
+	}
+
+mDNSexport mDNSBool DNSDigest_VerifyMessage(DNSMessage *msg, mDNSu8 *end, LargeCacheRecord * lcr, DomainAuthInfo *info, mDNSu16 * rcode, mDNSu16 * tcode)
+	{
+	mDNSu8			*	ptr = (mDNSu8*) &lcr->r.resrec.rdata->u.data;
+	mDNSs32				now;
+	mDNSs32				then;
+	mDNSu8				thisDigest[MD5_LEN];
+	mDNSu8				thatDigest[MD5_LEN];
+	mDNSu32				macsize;
+	mDNSOpaque16 		buf;
+	mDNSu8				utc48[6];
+	mDNSs32				delta;
+	mDNSu16				fudge;
+	domainname		*	algo;
+	MD5_CTX				c;
+	mDNSBool			ok = mDNSfalse;
+
+	// We only support HMAC-MD5 for now
+
+	algo = (domainname*) ptr;
+
+	if (!SameDomainName(algo, &HMAC_MD5_AlgName))
+		{
+		LogMsg("ERROR: DNSDigest_VerifyMessage - TSIG algorithm not supported: %##s", algo->c);
+		*rcode = kDNSFlag1_RC_NotAuth;
+		*tcode = TSIG_ErrBadKey;
+		ok = mDNSfalse;
+		goto exit;
+		}
+
+	ptr += DomainNameLength(algo);
+
+	// Check the times
+
+	now = mDNSPlatformUTC();
+	if (now == -1)
+		{
+		LogMsg("ERROR: DNSDigest_VerifyMessage - mDNSPlatformUTC returned bad time -1");
+		*rcode = kDNSFlag1_RC_NotAuth;
+		*tcode = TSIG_ErrBadTime;
+		ok = mDNSfalse;
+		goto exit;
+		}
+
+	// Get the 48 bit time field, skipping over the first word
+
+	utc48[0] = *ptr++;
+	utc48[1] = *ptr++;
+	utc48[2] = *ptr++;
+	utc48[3] = *ptr++;
+	utc48[4] = *ptr++;
+	utc48[5] = *ptr++;
+
+	then  = (mDNSs32)NToH32(utc48 + sizeof(mDNSu16));
+
+	fudge = NToH16(ptr);
+
+	ptr += sizeof(mDNSu16);
+
+	delta = (now > then) ? now - then : then - now;
+
+	if (delta > fudge)
+		{
+		LogMsg("ERROR: DNSDigest_VerifyMessage - time skew > %d", fudge);
+		*rcode = kDNSFlag1_RC_NotAuth;
+		*tcode = TSIG_ErrBadTime;
+		ok = mDNSfalse;
+		goto exit;
+		}
+
+	// MAC size
+
+	macsize = (mDNSu32) NToH16(ptr);
+	
+	ptr += sizeof(mDNSu16);
+
+	// MAC
+
+	mDNSPlatformMemCopy(thatDigest, ptr, MD5_LEN);
+
+	// Init MD5 context, digest inner key pad and message
+
+	MD5_Init(&c);
+	MD5_Update(&c, info->keydata_ipad, HMAC_LEN);
+	MD5_Update(&c, (mDNSu8*) msg, (unsigned long)(end - (mDNSu8*) msg));
+	   
+	// Key name
+
+	MD5_Update(&c, lcr->r.resrec.name->c, DomainNameLength(lcr->r.resrec.name));
+
+	// Class name
+
+	buf = mDNSOpaque16fromIntVal(lcr->r.resrec.rrclass);
+	MD5_Update(&c, buf.b, sizeof(mDNSOpaque16));
+
+	// TTL
+
+	MD5_Update(&c, (mDNSu8*) &lcr->r.resrec.rroriginalttl, sizeof(lcr->r.resrec.rroriginalttl));
+	
+	// Algorithm
+ 
+	MD5_Update(&c, algo->c, DomainNameLength(algo));
+
+	// Time
+
+	MD5_Update(&c, utc48, 6);
+
+	// Fudge
+
+	buf = mDNSOpaque16fromIntVal(fudge);
+	MD5_Update(&c, buf.b, sizeof(mDNSOpaque16));
+
+	// Digest error and other data len (both zero) - we'll add them to the rdata later
+
+	buf.NotAnInteger = 0;
+	MD5_Update(&c, buf.b, sizeof(mDNSOpaque16));  // error
+	MD5_Update(&c, buf.b, sizeof(mDNSOpaque16));  // other data len
+
+	// Finish the message & tsig var hash
+
+    MD5_Final(thisDigest, &c);
+	
+	// perform outer MD5 (outer key pad, inner digest)
+
+	MD5_Init(&c);
+	MD5_Update(&c, info->keydata_opad, HMAC_LEN);
+	MD5_Update(&c, thisDigest, MD5_LEN);
+	MD5_Final(thisDigest, &c);
+
+	if (!mDNSPlatformMemSame(thisDigest, thatDigest, MD5_LEN))
+		{
+		LogMsg("ERROR: DNSDigest_VerifyMessage - bad signature");
+		*rcode = kDNSFlag1_RC_NotAuth;
+		*tcode = TSIG_ErrBadSig;
+		ok = mDNSfalse;
+		goto exit;
+		}
+
+	// set remaining rdata fields
+	ok = mDNStrue;
+
+exit:
+
+	return ok;
+	}
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/mdnsresponder/mDNSCore/Implementer Notes.txt b/mdnsresponder/mDNSCore/Implementer Notes.txt
new file mode 100644
index 0000000..60c9b3a
--- /dev/null
+++ b/mdnsresponder/mDNSCore/Implementer Notes.txt
@@ -0,0 +1,66 @@
+February 2002:
+
+The mDNSResponder code has a slight architectural change to improve
+efficiency.
+
+The mDNSResponder code previously called ScheduleNextTask() after every
+operation, to calculate the time at which it needed to be called back to
+perform its next timed operation. When the workload is light, and
+protocol operations are rare and far apart, this makes sense.
+
+However, on networks where there is a lot of mDNS traffic (or the CPU is
+slow), this leads to the following anomolous behaviour: mDNSResponder
+spends a lot of CPU time working out what to do next, when what it needs
+to do next should be obvious: Finish processing the big backlog of
+packets that have been received.
+
+To remedy this, mDNSResponder now only executes ScheduleNextTask() when
+there is no other obvious work waiting to be done. However, the
+mDNSResponder code does not have direct access to this knowledge. Only
+the platform layer below knows whether there are packets waiting to be
+processed. Only the client layer above knows whether it is in the
+process of performing a long sequence of back-to-back mDNS API calls.
+
+This means that the new architecture places an additional responsibility
+on the client layer and/or platform support layer. As long as they have
+immediate work to do, they should call the appropriate mDNSCore routines
+to accomplish that work. With each call, mDNSCore will do only what it
+immediately has to do to satisfy the call. Any optional work will be
+deferred. As soon as there is no more immediate work to do, the calling
+layer MUST call mDNS_Execute(). Failure to call mDNS_Execute() will lead
+to unreliable or incorrect operation.
+
+The value returned from mDNS_Execute() is the next time (in absolute
+platform time units) at which mDNS_Execute() MUST be called again to
+perform its next necessary operation (e.g. transmitting its next
+scheduled query packet, etc.) Note that the time returned is an absolute
+time, not the time *interval* between now and the next required call.
+For OS APIs that work in terms of intervals instead of absolute times,
+mDNSPlatformTimeNow() must be subtracted from the absolute time to get
+the interval between now and the next event.
+
+In a single-threaded application using a blocking select() call as its
+main synchronization point, this means that you should call
+mDNS_Execute() before calling select(), and the timeout value you pass
+to select() MUST NOT be larger than that indicated by the result
+returned from mDNS_Execute(). After the blocking select() call returns,
+you should do whatever work you have to do, and then, if mDNS packets
+were received, or mDNS API calls were made, be sure to call
+mDNS_Execute() again, and if necessary adjust your timeout value
+accordingly, before going back into the select() call.
+
+In an asynchronous or interrupt-driven application, there are three
+places that should call mDNS_Execute():
+
+1. After delivering received packets, the platform support layer should
+call mDNS_Execute(), and use the value returned to set the platform
+callback timer to fire at the indicated time.
+
+2. After making any mDNS API call or series of calls, the client layer
+should call mDNS_Execute(), and use the value returned to set the
+platform callback timer to fire at the indicated time.
+
+3. When the platform callback timer fires, it should call mDNS_Execute()
+(to allow mDNSCore to perform its necessary work) and then the timer
+routine use the result returned to reset itself to fire at the right
+time for the next scheduled event.
diff --git a/mdnsresponder/mDNSCore/mDNS.c b/mdnsresponder/mDNSCore/mDNS.c
new file mode 100755
index 0000000..e126754
--- /dev/null
+++ b/mdnsresponder/mDNSCore/mDNS.c
@@ -0,0 +1,11469 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2006 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ *
+ * This code is completely 100% portable C. It does not depend on any external header files
+ * from outside the mDNS project -- all the types it expects to find are defined right here.
+ * 
+ * The previous point is very important: This file does not depend on any external
+ * header files. It should compile on *any* platform that has a C compiler, without
+ * making *any* assumptions about availability of so-called "standard" C functions,
+ * routines, or types (which may or may not be present on any given platform).
+
+ * Formatting notes:
+ * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion
+ * on C indentation can be found on the web, such as <http://www.kafejo.com/komp/1tbs.htm>,
+ * but for the sake of brevity here I will say just this: Curly braces are not syntactially
+ * part of an "if" statement; they are the beginning and ending markers of a compound statement;
+ * therefore common sense dictates that if they are part of a compound statement then they
+ * should be indented to the same level as everything else in that compound statement.
+ * Indenting curly braces at the same level as the "if" implies that curly braces are
+ * part of the "if", which is false. (This is as misleading as people who write "char* x,y;"
+ * thinking that variables x and y are both of type "char*" -- and anyone who doesn't
+ * understand why variable y is not of type "char*" just proves the point that poor code
+ * layout leads people to unfortunate misunderstandings about how the C language really works.)
+ */
+
+#include "DNSCommon.h"                  // Defines general DNS untility routines
+#include "uDNS.h"						// Defines entry points into unicast-specific routines
+
+// Disable certain benign warnings with Microsoft compilers
+#if(defined(_MSC_VER))
+	// Disable "conditional expression is constant" warning for debug macros.
+	// Otherwise, this generates warnings for the perfectly natural construct "while(1)"
+	// If someone knows a variant way of writing "while(1)" that doesn't generate warning messages, please let us know
+	#pragma warning(disable:4127)
+	
+	// Disable "assignment within conditional expression".
+	// Other compilers understand the convention that if you place the assignment expression within an extra pair
+	// of parentheses, this signals to the compiler that you really intended an assignment and no warning is necessary.
+	// The Microsoft compiler doesn't understand this convention, so in the absense of any other way to signal
+	// to the compiler that the assignment is intentional, we have to just turn this warning off completely.
+	#pragma warning(disable:4706)
+#endif
+
+#if APPLE_OSX_mDNSResponder
+
+#include <WebFilterDNS/WebFilterDNS.h>
+
+#if ! NO_WCF
+WCFConnection *WCFConnectionNew(void) __attribute__((weak_import));
+void WCFConnectionDealloc(WCFConnection* c) __attribute__((weak_import));
+
+// Do we really need to define a macro for "if"?
+#define CHECK_WCF_FUNCTION(X) if (X)
+#endif // ! NO_WCF
+
+#else
+
+#define NO_WCF 1
+#endif // APPLE_OSX_mDNSResponder
+
+// Forward declarations
+mDNSlocal void BeginSleepProcessing(mDNS *const m);
+mDNSlocal void RetrySPSRegistrations(mDNS *const m);
+mDNSlocal void SendWakeup(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSEthAddr *EthAddr, mDNSOpaque48 *password);
+mDNSlocal mDNSBool CacheRecordRmvEventsForQuestion(mDNS *const m, DNSQuestion *q);
+mDNSlocal mDNSBool LocalRecordRmvEventsForQuestion(mDNS *const m, DNSQuestion *q);
+mDNSlocal void mDNS_PurgeBeforeResolve(mDNS *const m, DNSQuestion *q);
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark - Program Constants
+#endif
+
+#define NO_HINFO 1
+
+
+// Any records bigger than this are considered 'large' records
+#define SmallRecordLimit 1024
+
+#define kMaxUpdateCredits 10
+#define kUpdateCreditRefreshInterval (mDNSPlatformOneSecond * 6)
+
+mDNSexport const char *const mDNS_DomainTypeNames[] =
+	{
+	 "b._dns-sd._udp.",		// Browse
+	"db._dns-sd._udp.",		// Default Browse
+	"lb._dns-sd._udp.",		// Automatic Browse
+	 "r._dns-sd._udp.",		// Registration
+	"dr._dns-sd._udp."		// Default Registration
+	};
+
+#ifdef UNICAST_DISABLED
+#define uDNS_IsActiveQuery(q, u) mDNSfalse
+#endif
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - General Utility Functions
+#endif
+
+// If there is a authoritative LocalOnly record that answers questions of type A, AAAA and CNAME
+// this returns true. Main use is to handle /etc/hosts records. 
+#define LORecordAnswersAddressType(rr) ((rr)->ARType == AuthRecordLocalOnly && \
+									(rr)->resrec.RecordType & kDNSRecordTypeUniqueMask && \
+									((rr)->resrec.rrtype == kDNSType_A || (rr)->resrec.rrtype == kDNSType_AAAA || \
+									(rr)->resrec.rrtype == kDNSType_CNAME))
+
+#define FollowCNAME(q, rr, AddRecord)	(AddRecord && (q)->qtype != kDNSType_CNAME && \
+										(rr)->RecordType != kDNSRecordTypePacketNegative && \
+										(rr)->rrtype == kDNSType_CNAME)
+
+mDNSlocal void SetNextQueryStopTime(mDNS *const m, const DNSQuestion *const q)
+	{
+	if (m->mDNS_busy != m->mDNS_reentrancy+1)
+		LogMsg("SetNextQueryTime: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
+
+#if ForceAlerts
+	if (m->mDNS_busy != m->mDNS_reentrancy+1) *(long*)0 = 0;
+#endif
+
+	if (m->NextScheduledStopTime - q->StopTime > 0)
+		m->NextScheduledStopTime = q->StopTime;
+	}
+
+mDNSexport void SetNextQueryTime(mDNS *const m, const DNSQuestion *const q)
+	{
+	if (m->mDNS_busy != m->mDNS_reentrancy+1)
+		LogMsg("SetNextQueryTime: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
+
+#if ForceAlerts
+	if (m->mDNS_busy != m->mDNS_reentrancy+1) *(long*)0 = 0;
+#endif
+
+	if (ActiveQuestion(q))
+		{
+		// Depending on whether this is a multicast or unicast question we want to set either:
+		// m->NextScheduledQuery = NextQSendTime(q) or
+		// m->NextuDNSEvent      = NextQSendTime(q)
+		mDNSs32 *const timer = mDNSOpaque16IsZero(q->TargetQID) ? &m->NextScheduledQuery : &m->NextuDNSEvent;
+		if (*timer - NextQSendTime(q) > 0)
+			*timer = NextQSendTime(q);
+		}
+	}
+
+mDNSlocal void ReleaseAuthEntity(AuthHash *r, AuthEntity *e)
+	{
+#if APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING >= 1
+	unsigned int i;
+	for (i=0; i<sizeof(*e); i++) ((char*)e)[i] = 0xFF;
+#endif
+	e->next = r->rrauth_free;
+	r->rrauth_free = e;
+	r->rrauth_totalused--;
+	}
+
+mDNSlocal void ReleaseAuthGroup(AuthHash *r, AuthGroup **cp)
+	{
+	AuthEntity *e = (AuthEntity *)(*cp);
+	LogMsg("ReleaseAuthGroup:  Releasing AuthGroup %##s", (*cp)->name->c);
+	if ((*cp)->rrauth_tail != &(*cp)->members)
+		LogMsg("ERROR: (*cp)->members == mDNSNULL but (*cp)->rrauth_tail != &(*cp)->members)");
+	if ((*cp)->name != (domainname*)((*cp)->namestorage)) mDNSPlatformMemFree((*cp)->name);
+	(*cp)->name = mDNSNULL;
+	*cp = (*cp)->next;			// Cut record from list
+	ReleaseAuthEntity(r, e);
+	}
+
+mDNSlocal AuthEntity *GetAuthEntity(AuthHash *r, const AuthGroup *const PreserveAG)
+	{
+	AuthEntity *e = mDNSNULL;
+
+	if (r->rrauth_lock) { LogMsg("GetFreeCacheRR ERROR! Cache already locked!"); return(mDNSNULL); }
+	r->rrauth_lock = 1;
+	
+	if (!r->rrauth_free)
+		{
+		// We allocate just one AuthEntity at a time because we need to be able
+		// free them all individually which normally happens when we parse /etc/hosts into
+		// AuthHash where we add the "new" entries and discard (free) the already added
+		// entries. If we allocate as chunks, we can't free them individually.
+		AuthEntity *storage = mDNSPlatformMemAllocate(sizeof(AuthEntity));
+		storage->next = mDNSNULL;
+		r->rrauth_free = storage;
+		}
+	
+	// If we still have no free records, recycle all the records we can.
+	// Enumerating the entire auth is moderately expensive, so when we do it, we reclaim all the records we can in one pass.
+	if (!r->rrauth_free)
+		{
+		mDNSu32 oldtotalused = r->rrauth_totalused;
+		mDNSu32 slot;
+		for (slot = 0; slot < AUTH_HASH_SLOTS; slot++)
+			{
+			AuthGroup **cp = &r->rrauth_hash[slot];
+			while (*cp)
+				{
+				if ((*cp)->members || (*cp)==PreserveAG) cp=&(*cp)->next;
+				else ReleaseAuthGroup(r, cp);
+				}
+			}
+		LogInfo("GetAuthEntity: Recycled %d records to reduce auth cache from %d to %d",
+			oldtotalused - r->rrauth_totalused, oldtotalused, r->rrauth_totalused);
+		}
+
+	if (r->rrauth_free)	// If there are records in the free list, take one
+		{
+		e = r->rrauth_free;
+		r->rrauth_free = e->next;
+		if (++r->rrauth_totalused >= r->rrauth_report)
+			{
+			LogInfo("RR Auth now using %ld objects", r->rrauth_totalused);
+			if      (r->rrauth_report <  100) r->rrauth_report += 10;
+			else if (r->rrauth_report < 1000) r->rrauth_report += 100;
+			else                               r->rrauth_report += 1000;
+			}
+		mDNSPlatformMemZero(e, sizeof(*e));
+		}
+
+	r->rrauth_lock = 0;
+
+	return(e);
+	}
+
+mDNSexport AuthGroup *AuthGroupForName(AuthHash *r, const mDNSu32 slot, const mDNSu32 namehash, const domainname *const name)
+	{
+	AuthGroup *ag;
+	for (ag = r->rrauth_hash[slot]; ag; ag=ag->next)
+		if (ag->namehash == namehash && SameDomainName(ag->name, name))
+			break;
+	return(ag);
+	}
+
+mDNSexport AuthGroup *AuthGroupForRecord(AuthHash *r, const mDNSu32 slot, const ResourceRecord *const rr)
+	{
+	return(AuthGroupForName(r, slot, rr->namehash, rr->name));
+	}
+
+mDNSlocal AuthGroup *GetAuthGroup(AuthHash *r, const mDNSu32 slot, const ResourceRecord *const rr)
+	{
+	mDNSu16 namelen = DomainNameLength(rr->name);
+	AuthGroup *ag = (AuthGroup*)GetAuthEntity(r, mDNSNULL);
+	if (!ag) { LogMsg("GetAuthGroup: Failed to allocate memory for %##s", rr->name->c); return(mDNSNULL); }
+	ag->next         = r->rrauth_hash[slot];
+	ag->namehash     = rr->namehash;
+	ag->members      = mDNSNULL;
+	ag->rrauth_tail  = &ag->members;
+	ag->name         = (domainname*)ag->namestorage;
+	ag->NewLocalOnlyRecords = mDNSNULL;
+	if (namelen > InlineCacheGroupNameSize) ag->name = mDNSPlatformMemAllocate(namelen);
+	if (!ag->name)
+		{
+		LogMsg("GetAuthGroup: Failed to allocate name storage for %##s", rr->name->c);
+		ReleaseAuthEntity(r, (AuthEntity*)ag);
+		return(mDNSNULL);
+		}
+	AssignDomainName(ag->name, rr->name);
+
+	if (AuthGroupForRecord(r, slot, rr)) LogMsg("GetAuthGroup: Already have AuthGroup for %##s", rr->name->c);
+	r->rrauth_hash[slot] = ag;
+	if (AuthGroupForRecord(r, slot, rr) != ag) LogMsg("GetAuthGroup: Not finding AuthGroup for %##s", rr->name->c);
+	
+	return(ag);
+	}
+
+// Returns the AuthGroup in which the AuthRecord was inserted
+mDNSexport AuthGroup *InsertAuthRecord(mDNS *const m, AuthHash *r, AuthRecord *rr)
+	{
+	AuthGroup *ag;
+	const mDNSu32 slot = AuthHashSlot(rr->resrec.name);
+	ag = AuthGroupForRecord(r, slot, &rr->resrec);
+	if (!ag) ag = GetAuthGroup(r, slot, &rr->resrec);	// If we don't have a AuthGroup for this name, make one now
+	if (ag)
+		{
+		LogInfo("InsertAuthRecord: inserting auth record %s from table", ARDisplayString(m, rr));
+		*(ag->rrauth_tail) = rr;				// Append this record to tail of cache slot list
+		ag->rrauth_tail = &(rr->next);			// Advance tail pointer
+		}
+	return ag;
+	}
+
+mDNSexport AuthGroup *RemoveAuthRecord(mDNS *const m, AuthHash *r, AuthRecord *rr)
+	{
+	AuthGroup *a;
+	AuthGroup **ag = &a;
+	AuthRecord **rp;
+	const mDNSu32 slot = AuthHashSlot(rr->resrec.name);
+
+	a = AuthGroupForRecord(r, slot, &rr->resrec);
+	if (!a) { LogMsg("RemoveAuthRecord: ERROR!! AuthGroup not found for %s", ARDisplayString(m, rr)); return mDNSNULL; }
+	rp = &(*ag)->members;
+	while (*rp)
+		{
+		if (*rp != rr)
+			rp=&(*rp)->next;
+		else
+			{
+			// We don't break here, so that we can set the tail below without tracking "prev" pointers
+
+			LogInfo("RemoveAuthRecord: removing auth record %s from table", ARDisplayString(m, rr));
+			*rp = (*rp)->next;			// Cut record from list
+			}
+		}
+	// TBD: If there are no more members, release authgroup ?
+	(*ag)->rrauth_tail = rp;
+	return a;
+	}
+
+mDNSexport CacheGroup *CacheGroupForName(const mDNS *const m, const mDNSu32 slot, const mDNSu32 namehash, const domainname *const name)
+	{
+	CacheGroup *cg;
+	for (cg = m->rrcache_hash[slot]; cg; cg=cg->next)
+		if (cg->namehash == namehash && SameDomainName(cg->name, name))
+			break;
+	return(cg);
+	}
+
+mDNSlocal CacheGroup *CacheGroupForRecord(const mDNS *const m, const mDNSu32 slot, const ResourceRecord *const rr)
+	{
+	return(CacheGroupForName(m, slot, rr->namehash, rr->name));
+	}
+
+mDNSexport mDNSBool mDNS_AddressIsLocalSubnet(mDNS *const m, const mDNSInterfaceID InterfaceID, const mDNSAddr *addr)
+	{
+	NetworkInterfaceInfo *intf;
+
+	if (addr->type == mDNSAddrType_IPv4)
+		{
+		// Normally we resist touching the NotAnInteger fields, but here we're doing tricky bitwise masking so we make an exception
+		if (mDNSv4AddressIsLinkLocal(&addr->ip.v4)) return(mDNStrue);
+		for (intf = m->HostInterfaces; intf; intf = intf->next)
+			if (intf->ip.type == addr->type && intf->InterfaceID == InterfaceID && intf->McastTxRx)
+				if (((intf->ip.ip.v4.NotAnInteger ^ addr->ip.v4.NotAnInteger) & intf->mask.ip.v4.NotAnInteger) == 0)
+					return(mDNStrue);
+		}
+
+	if (addr->type == mDNSAddrType_IPv6)
+		{
+		if (mDNSv6AddressIsLinkLocal(&addr->ip.v6)) return(mDNStrue);
+		for (intf = m->HostInterfaces; intf; intf = intf->next)
+			if (intf->ip.type == addr->type && intf->InterfaceID == InterfaceID && intf->McastTxRx)
+				if ((((intf->ip.ip.v6.l[0] ^ addr->ip.v6.l[0]) & intf->mask.ip.v6.l[0]) == 0) &&
+					(((intf->ip.ip.v6.l[1] ^ addr->ip.v6.l[1]) & intf->mask.ip.v6.l[1]) == 0) &&
+					(((intf->ip.ip.v6.l[2] ^ addr->ip.v6.l[2]) & intf->mask.ip.v6.l[2]) == 0) &&
+					(((intf->ip.ip.v6.l[3] ^ addr->ip.v6.l[3]) & intf->mask.ip.v6.l[3]) == 0))
+						return(mDNStrue);
+		}
+
+	return(mDNSfalse);
+	}
+
+mDNSlocal NetworkInterfaceInfo *FirstInterfaceForID(mDNS *const m, const mDNSInterfaceID InterfaceID)
+	{
+	NetworkInterfaceInfo *intf = m->HostInterfaces;
+	while (intf && intf->InterfaceID != InterfaceID) intf = intf->next;
+	return(intf);
+	}
+
+mDNSexport char *InterfaceNameForID(mDNS *const m, const mDNSInterfaceID InterfaceID)
+	{
+	NetworkInterfaceInfo *intf = FirstInterfaceForID(m, InterfaceID);
+	return(intf ? intf->ifname : mDNSNULL);
+	}
+
+// Caller should hold the lock
+mDNSlocal void GenerateNegativeResponse(mDNS *const m)
+	{
+	DNSQuestion *q;
+	if (!m->CurrentQuestion) { LogMsg("GenerateNegativeResponse: ERROR!! CurrentQuestion not set"); return; }
+	q = m->CurrentQuestion;
+	LogInfo("GenerateNegativeResponse: Generating negative response for question %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+
+	MakeNegativeCacheRecord(m, &m->rec.r, &q->qname, q->qnamehash, q->qtype, q->qclass, 60, mDNSInterface_Any, mDNSNULL);
+	AnswerCurrentQuestionWithResourceRecord(m, &m->rec.r, QC_addnocache);
+	if (m->CurrentQuestion == q) { q->ThisQInterval = 0; }				// Deactivate this question
+	// Don't touch the question after this
+	m->rec.r.resrec.RecordType = 0;		// Clear RecordType to show we're not still using it
+	}
+
+mDNSlocal void AnswerQuestionByFollowingCNAME(mDNS *const m, DNSQuestion *q, ResourceRecord *rr)
+	{
+	const mDNSBool selfref = SameDomainName(&q->qname, &rr->rdata->u.name);
+	if (q->CNAMEReferrals >= 10 || selfref)
+		LogMsg("AnswerQuestionByFollowingCNAME: %p %##s (%s) NOT following CNAME referral %d%s for %s",
+			q, q->qname.c, DNSTypeName(q->qtype), q->CNAMEReferrals, selfref ? " (Self-Referential)" : "", RRDisplayString(m, rr));
+	else
+		{
+		const mDNSu32 c = q->CNAMEReferrals + 1;		// Stash a copy of the new q->CNAMEReferrals value
+
+		// The SameDomainName check above is to ignore bogus CNAME records that point right back at
+		// themselves. Without that check we can get into a case where we have two duplicate questions,
+		// A and B, and when we stop question A, UpdateQuestionDuplicates copies the value of CNAMEReferrals
+		// from A to B, and then A is re-appended to the end of the list as a duplicate of B (because
+		// the target name is still the same), and then when we stop question B, UpdateQuestionDuplicates
+		// copies the B's value of CNAMEReferrals back to A, and we end up not incrementing CNAMEReferrals
+		// for either of them. This is not a problem for CNAME loops of two or more records because in
+		// those cases the newly re-appended question A has a different target name and therefore cannot be
+		// a duplicate of any other question ('B') which was itself a duplicate of the previous question A.
+
+		// Right now we just stop and re-use the existing query. If we really wanted to be 100% perfect,
+		// and track CNAMEs coming and going, we should really create a subordinate query here,
+		// which we would subsequently cancel and retract if the CNAME referral record were removed.
+		// In reality this is such a corner case we'll ignore it until someone actually needs it.
+
+		LogInfo("AnswerQuestionByFollowingCNAME: %p %##s (%s) following CNAME referral %d for %s",
+			q, q->qname.c, DNSTypeName(q->qtype), q->CNAMEReferrals, RRDisplayString(m, rr));
+
+		mDNS_StopQuery_internal(m, q);								// Stop old query
+		AssignDomainName(&q->qname, &rr->rdata->u.name);			// Update qname
+		q->qnamehash = DomainNameHashValue(&q->qname);				// and namehash
+		// If a unicast query results in a CNAME that points to a .local, we need to re-try
+		// this as unicast. Setting the mDNSInterface_Unicast tells mDNS_StartQuery_internal
+		// to try this as unicast query even though it is a .local name
+		if (!mDNSOpaque16IsZero(q->TargetQID) && IsLocalDomain(&q->qname))
+			{
+			LogInfo("AnswerQuestionByFollowingCNAME: Resolving a .local CNAME %p %##s (%s) Record %s",
+				q, q->qname.c, DNSTypeName(q->qtype), RRDisplayString(m, rr));
+			q->InterfaceID = mDNSInterface_Unicast;
+			}
+		mDNS_StartQuery_internal(m, q);								// start new query
+		// Record how many times we've done this. We need to do this *after* mDNS_StartQuery_internal,
+		// because mDNS_StartQuery_internal re-initializes CNAMEReferrals to zero
+		q->CNAMEReferrals = c;
+		}
+	}
+
+// For a single given DNSQuestion pointed to by CurrentQuestion, deliver an add/remove result for the single given AuthRecord
+// Note: All the callers should use the m->CurrentQuestion to see if the question is still valid or not
+mDNSlocal void AnswerLocalQuestionWithLocalAuthRecord(mDNS *const m, AuthRecord *rr, QC_result AddRecord)
+	{
+	DNSQuestion *q = m->CurrentQuestion;
+	mDNSBool followcname;
+
+	if (!q)
+		{
+		LogMsg("AnswerLocalQuestionWithLocalAuthRecord: ERROR!! CurrentQuestion NULL while answering with %s", ARDisplayString(m, rr));
+		return;
+		}
+		
+	followcname = FollowCNAME(q, &rr->resrec, AddRecord);
+
+	// We should not be delivering results for record types Unregistered, Deregistering, and (unverified) Unique
+	if (!(rr->resrec.RecordType & kDNSRecordTypeActiveMask))
+		{
+		LogMsg("AnswerLocalQuestionWithLocalAuthRecord: *NOT* delivering %s event for local record type %X %s",
+			AddRecord ? "Add" : "Rmv", rr->resrec.RecordType, ARDisplayString(m, rr));
+		return;
+		}
+
+	// Indicate that we've given at least one positive answer for this record, so we should be prepared to send a goodbye for it
+	if (AddRecord) rr->AnsweredLocalQ = mDNStrue;
+	mDNS_DropLockBeforeCallback();		// Allow client to legally make mDNS API calls from the callback
+	if (q->QuestionCallback && !q->NoAnswer)
+		{
+		q->CurrentAnswers += AddRecord ? 1 : -1;
+ 		if (LORecordAnswersAddressType(rr))
+			{
+			if (!followcname || q->ReturnIntermed)
+				{
+				// Don't send this packet on the wire as we answered from /etc/hosts
+				q->ThisQInterval = 0;
+				q->LOAddressAnswers += AddRecord ? 1 : -1;
+				q->QuestionCallback(m, q, &rr->resrec, AddRecord);
+				}
+			mDNS_ReclaimLockAfterCallback();	// Decrement mDNS_reentrancy to block mDNS API calls again
+			// The callback above could have caused the question to stop. Detect that
+			// using m->CurrentQuestion
+			if (followcname && m->CurrentQuestion == q)
+				AnswerQuestionByFollowingCNAME(m, q, &rr->resrec);
+			return;
+			}
+		else
+			q->QuestionCallback(m, q, &rr->resrec, AddRecord);
+		}
+	mDNS_ReclaimLockAfterCallback();	// Decrement mDNS_reentrancy to block mDNS API calls again
+	}
+
+mDNSlocal void AnswerInterfaceAnyQuestionsWithLocalAuthRecord(mDNS *const m, AuthRecord *rr, QC_result AddRecord)
+ 	{
+ 	if (m->CurrentQuestion)
+ 		LogMsg("AnswerInterfaceAnyQuestionsWithLocalAuthRecord: ERROR m->CurrentQuestion already set: %##s (%s)",
+ 			m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype));
+ 	m->CurrentQuestion = m->Questions;
+ 	while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions)
+ 		{
+		mDNSBool answered;
+ 		DNSQuestion *q = m->CurrentQuestion;
+		if (RRAny(rr))
+			answered = ResourceRecordAnswersQuestion(&rr->resrec, q);
+		else
+			answered = LocalOnlyRecordAnswersQuestion(rr, q);
+ 		if (answered)
+ 			AnswerLocalQuestionWithLocalAuthRecord(m, rr, AddRecord);		// MUST NOT dereference q again
+		if (m->CurrentQuestion == q)	// If m->CurrentQuestion was not auto-advanced, do it ourselves now
+			m->CurrentQuestion = q->next;
+ 		}
+ 	m->CurrentQuestion = mDNSNULL;
+ 	}
+
+// When a new local AuthRecord is created or deleted, AnswerAllLocalQuestionsWithLocalAuthRecord()
+// delivers the appropriate add/remove events to listening questions:
+// 1. It runs though all our LocalOnlyQuestions delivering answers as appropriate,
+//    stopping if it reaches a NewLocalOnlyQuestion -- brand-new questions are handled by AnswerNewLocalOnlyQuestion().
+// 2. If the AuthRecord is marked mDNSInterface_LocalOnly or mDNSInterface_P2P, then it also runs though
+//    our main question list, delivering answers to mDNSInterface_Any questions as appropriate,
+//    stopping if it reaches a NewQuestion -- brand-new questions are handled by AnswerNewQuestion().
+//
+// AnswerAllLocalQuestionsWithLocalAuthRecord is used by the m->NewLocalRecords loop in mDNS_Execute(),
+// and by mDNS_Deregister_internal()
+
+mDNSlocal void AnswerAllLocalQuestionsWithLocalAuthRecord(mDNS *const m, AuthRecord *rr, QC_result AddRecord)
+	{
+	if (m->CurrentQuestion)
+		LogMsg("AnswerAllLocalQuestionsWithLocalAuthRecord ERROR m->CurrentQuestion already set: %##s (%s)",
+			m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype));
+
+	m->CurrentQuestion = m->LocalOnlyQuestions;
+	while (m->CurrentQuestion && m->CurrentQuestion != m->NewLocalOnlyQuestions)
+		{
+		mDNSBool answered;
+		DNSQuestion *q = m->CurrentQuestion;
+		// We are called with both LocalOnly/P2P record or a regular AuthRecord
+		if (RRAny(rr))
+			answered = ResourceRecordAnswersQuestion(&rr->resrec, q);
+		else
+			answered = LocalOnlyRecordAnswersQuestion(rr, q);
+		if (answered)
+			AnswerLocalQuestionWithLocalAuthRecord(m, rr, AddRecord);			// MUST NOT dereference q again
+		if (m->CurrentQuestion == q)	// If m->CurrentQuestion was not auto-advanced, do it ourselves now
+			m->CurrentQuestion = q->next;
+		}
+
+	m->CurrentQuestion = mDNSNULL;
+
+	// If this AuthRecord is marked LocalOnly or P2P, then we want to deliver it to all local 'mDNSInterface_Any' questions
+	if (rr->ARType == AuthRecordLocalOnly || rr->ARType == AuthRecordP2P)
+		AnswerInterfaceAnyQuestionsWithLocalAuthRecord(m, rr, AddRecord);
+
+	}
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - Resource Record Utility Functions
+#endif
+
+#define RRTypeIsAddressType(T) ((T) == kDNSType_A || (T) == kDNSType_AAAA)
+
+#define ResourceRecordIsValidAnswer(RR) ( ((RR)->             resrec.RecordType & kDNSRecordTypeActiveMask)  && \
+		((RR)->Additional1 == mDNSNULL || ((RR)->Additional1->resrec.RecordType & kDNSRecordTypeActiveMask)) && \
+		((RR)->Additional2 == mDNSNULL || ((RR)->Additional2->resrec.RecordType & kDNSRecordTypeActiveMask)) && \
+		((RR)->DependentOn == mDNSNULL || ((RR)->DependentOn->resrec.RecordType & kDNSRecordTypeActiveMask))  )
+
+#define ResourceRecordIsValidInterfaceAnswer(RR, INTID) \
+	(ResourceRecordIsValidAnswer(RR) && \
+	((RR)->resrec.InterfaceID == mDNSInterface_Any || (RR)->resrec.InterfaceID == (INTID)))
+
+#define DefaultProbeCountForTypeUnique ((mDNSu8)3)
+#define DefaultProbeCountForRecordType(X)      ((X) == kDNSRecordTypeUnique ? DefaultProbeCountForTypeUnique : (mDNSu8)0)
+
+#define InitialAnnounceCount ((mDNSu8)8)
+
+// For goodbye packets we set the count to 3, and for wakeups we set it to 18
+// (which will be up to 15 wakeup attempts over the course of 30 seconds,
+// and then if the machine fails to wake, 3 goodbye packets).
+#define GoodbyeCount ((mDNSu8)3)
+#define WakeupCount ((mDNSu8)18)
+
+// Number of wakeups we send if WakeOnResolve is set in the question
+#define InitialWakeOnResolveCount ((mDNSu8)3)
+
+// Note that the announce intervals use exponential backoff, doubling each time. The probe intervals do not.
+// This means that because the announce interval is doubled after sending the first packet, the first
+// observed on-the-wire inter-packet interval between announcements is actually one second.
+// The half-second value here may be thought of as a conceptual (non-existent) half-second delay *before* the first packet is sent.
+#define DefaultProbeIntervalForTypeUnique (mDNSPlatformOneSecond/4)
+#define DefaultAnnounceIntervalForTypeShared (mDNSPlatformOneSecond/2)
+#define DefaultAnnounceIntervalForTypeUnique (mDNSPlatformOneSecond/2)
+
+#define DefaultAPIntervalForRecordType(X)  ((X) & kDNSRecordTypeActiveSharedMask ? DefaultAnnounceIntervalForTypeShared : \
+											(X) & kDNSRecordTypeUnique           ? DefaultProbeIntervalForTypeUnique    : \
+											(X) & kDNSRecordTypeActiveUniqueMask ? DefaultAnnounceIntervalForTypeUnique : 0)
+
+#define TimeToAnnounceThisRecord(RR,time) ((RR)->AnnounceCount && (time) - ((RR)->LastAPTime + (RR)->ThisAPInterval) >= 0)
+#define TimeToSendThisRecord(RR,time) ((TimeToAnnounceThisRecord(RR,time) || (RR)->ImmedAnswer) && ResourceRecordIsValidAnswer(RR))
+#define TicksTTL(RR) ((mDNSs32)(RR)->resrec.rroriginalttl * mDNSPlatformOneSecond)
+#define RRExpireTime(RR) ((RR)->TimeRcvd + TicksTTL(RR))
+
+#define MaxUnansweredQueries 4
+
+// SameResourceRecordSignature returns true if two resources records have the same name, type, and class, and may be sent
+// (or were received) on the same interface (i.e. if *both* records specify an interface, then it has to match).
+// TTL and rdata may differ.
+// This is used for cache flush management:
+// When sending a unique record, all other records matching "SameResourceRecordSignature" must also be sent
+// When receiving a unique record, all old cache records matching "SameResourceRecordSignature" are flushed
+
+// SameResourceRecordNameClassInterface is functionally the same as SameResourceRecordSignature, except rrtype does not have to match
+
+#define SameResourceRecordSignature(A,B) (A)->resrec.rrtype == (B)->resrec.rrtype && SameResourceRecordNameClassInterface((A),(B))
+
+mDNSlocal mDNSBool SameResourceRecordNameClassInterface(const AuthRecord *const r1, const AuthRecord *const r2)
+	{
+	if (!r1) { LogMsg("SameResourceRecordSignature ERROR: r1 is NULL"); return(mDNSfalse); }
+	if (!r2) { LogMsg("SameResourceRecordSignature ERROR: r2 is NULL"); return(mDNSfalse); }
+	if (r1->resrec.InterfaceID &&
+		r2->resrec.InterfaceID &&
+		r1->resrec.InterfaceID != r2->resrec.InterfaceID) return(mDNSfalse);
+	return(mDNSBool)(
+		r1->resrec.rrclass  == r2->resrec.rrclass &&
+		r1->resrec.namehash == r2->resrec.namehash &&
+		SameDomainName(r1->resrec.name, r2->resrec.name));
+	}
+
+// PacketRRMatchesSignature behaves as SameResourceRecordSignature, except that types may differ if our
+// authoratative record is unique (as opposed to shared). For unique records, we are supposed to have
+// complete ownership of *all* types for this name, so *any* record type with the same name is a conflict.
+// In addition, when probing we send our questions with the wildcard type kDNSQType_ANY,
+// so a response of any type should match, even if it is not actually the type the client plans to use.
+
+// For now, to make it easier to avoid false conflicts, we treat SPS Proxy records like shared records,
+// and require the rrtypes to match for the rdata to be considered potentially conflicting
+mDNSlocal mDNSBool PacketRRMatchesSignature(const CacheRecord *const pktrr, const AuthRecord *const authrr)
+	{
+	if (!pktrr)  { LogMsg("PacketRRMatchesSignature ERROR: pktrr is NULL"); return(mDNSfalse); }
+	if (!authrr) { LogMsg("PacketRRMatchesSignature ERROR: authrr is NULL"); return(mDNSfalse); }
+	if (pktrr->resrec.InterfaceID &&
+		authrr->resrec.InterfaceID &&
+		pktrr->resrec.InterfaceID != authrr->resrec.InterfaceID) return(mDNSfalse);
+	if (!(authrr->resrec.RecordType & kDNSRecordTypeUniqueMask) || authrr->WakeUp.HMAC.l[0])
+		if (pktrr->resrec.rrtype != authrr->resrec.rrtype) return(mDNSfalse);
+	return(mDNSBool)(
+		pktrr->resrec.rrclass == authrr->resrec.rrclass &&
+		pktrr->resrec.namehash == authrr->resrec.namehash &&
+		SameDomainName(pktrr->resrec.name, authrr->resrec.name));
+	}
+
+// CacheRecord *ka is the CacheRecord from the known answer list in the query.
+// This is the information that the requester believes to be correct.
+// AuthRecord *rr is the answer we are proposing to give, if not suppressed.
+// This is the information that we believe to be correct.
+// We've already determined that we plan to give this answer on this interface
+// (either the record is non-specific, or it is specific to this interface)
+// so now we just need to check the name, type, class, rdata and TTL.
+mDNSlocal mDNSBool ShouldSuppressKnownAnswer(const CacheRecord *const ka, const AuthRecord *const rr)
+	{
+	// If RR signature is different, or data is different, then don't suppress our answer
+	if (!IdenticalResourceRecord(&ka->resrec, &rr->resrec)) return(mDNSfalse);
+	
+	// If the requester's indicated TTL is less than half the real TTL,
+	// we need to give our answer before the requester's copy expires.
+	// If the requester's indicated TTL is at least half the real TTL,
+	// then we can suppress our answer this time.
+	// If the requester's indicated TTL is greater than the TTL we believe,
+	// then that's okay, and we don't need to do anything about it.
+	// (If two responders on the network are offering the same information,
+	// that's okay, and if they are offering the information with different TTLs,
+	// the one offering the lower TTL should defer to the one offering the higher TTL.)
+	return(mDNSBool)(ka->resrec.rroriginalttl >= rr->resrec.rroriginalttl / 2);
+	}
+
+mDNSlocal void SetNextAnnounceProbeTime(mDNS *const m, const AuthRecord *const rr)
+	{
+	if (rr->resrec.RecordType == kDNSRecordTypeUnique)
+		{
+		if ((rr->LastAPTime + rr->ThisAPInterval) - m->timenow > mDNSPlatformOneSecond * 10)
+			{
+			LogMsg("SetNextAnnounceProbeTime: ProbeCount %d Next in %d %s", rr->ProbeCount, (rr->LastAPTime + rr->ThisAPInterval) - m->timenow, ARDisplayString(m, rr));
+			LogMsg("SetNextAnnounceProbeTime: m->SuppressProbes %d m->timenow %d diff %d", m->SuppressProbes, m->timenow, m->SuppressProbes - m->timenow);
+			}
+		if (m->NextScheduledProbe - (rr->LastAPTime + rr->ThisAPInterval) >= 0)
+			m->NextScheduledProbe = (rr->LastAPTime + rr->ThisAPInterval);
+		// Some defensive code:
+		// If (rr->LastAPTime + rr->ThisAPInterval) happens to be far in the past, we don't want to allow
+		// NextScheduledProbe to be set excessively in the past, because that can cause bad things to happen.
+		// See: <rdar://problem/7795434> mDNS: Sometimes advertising stops working and record interval is set to zero
+		if (m->NextScheduledProbe - m->timenow < 0)
+			m->NextScheduledProbe = m->timenow;
+		}
+	else if (rr->AnnounceCount && (ResourceRecordIsValidAnswer(rr) || rr->resrec.RecordType == kDNSRecordTypeDeregistering))
+		{
+		if (m->NextScheduledResponse - (rr->LastAPTime + rr->ThisAPInterval) >= 0)
+			m->NextScheduledResponse = (rr->LastAPTime + rr->ThisAPInterval);
+		}
+	}
+
+mDNSlocal void InitializeLastAPTime(mDNS *const m, AuthRecord *const rr)
+	{
+	// For reverse-mapping Sleep Proxy PTR records, probe interval is one second
+	rr->ThisAPInterval = rr->AddressProxy.type ? mDNSPlatformOneSecond : DefaultAPIntervalForRecordType(rr->resrec.RecordType);
+
+	// * If this is a record type that's going to probe, then we use the m->SuppressProbes time.
+	// * Otherwise, if it's not going to probe, but m->SuppressProbes is set because we have other
+	//   records that are going to probe, then we delay its first announcement so that it will
+	//   go out synchronized with the first announcement for the other records that *are* probing.
+	//   This is a minor performance tweak that helps keep groups of related records synchronized together.
+	//   The addition of "interval / 2" is to make sure that, in the event that any of the probes are
+	//   delayed by a few milliseconds, this announcement does not inadvertently go out *before* the probing is complete.
+	//   When the probing is complete and those records begin to announce, these records will also be picked up and accelerated,
+	//   because they will meet the criterion of being at least half-way to their scheduled announcement time.
+	// * If it's not going to probe and m->SuppressProbes is not already set then we should announce immediately.
+
+	if (rr->ProbeCount)
+		{
+		// If we have no probe suppression time set, or it is in the past, set it now
+		if (m->SuppressProbes == 0 || m->SuppressProbes - m->timenow < 0)
+			{
+			// To allow us to aggregate probes when a group of services are registered together,
+			// the first probe is delayed 1/4 second. This means the common-case behaviour is:
+			// 1/4 second wait; probe
+			// 1/4 second wait; probe
+			// 1/4 second wait; probe
+			// 1/4 second wait; announce (i.e. service is normally announced exactly one second after being registered)
+			m->SuppressProbes = NonZeroTime(m->timenow + DefaultProbeIntervalForTypeUnique/2 + mDNSRandom(DefaultProbeIntervalForTypeUnique/2));
+
+			// If we already have a *probe* scheduled to go out sooner, then use that time to get better aggregation
+			if (m->SuppressProbes - m->NextScheduledProbe >= 0)
+				m->SuppressProbes = NonZeroTime(m->NextScheduledProbe);
+			if (m->SuppressProbes - m->timenow < 0)		// Make sure we don't set m->SuppressProbes excessively in the past
+				m->SuppressProbes = m->timenow;
+
+			// If we already have a *query* scheduled to go out sooner, then use that time to get better aggregation
+			if (m->SuppressProbes - m->NextScheduledQuery >= 0)
+				m->SuppressProbes = NonZeroTime(m->NextScheduledQuery);
+			if (m->SuppressProbes - m->timenow < 0)		// Make sure we don't set m->SuppressProbes excessively in the past
+				m->SuppressProbes = m->timenow;
+
+			// except... don't expect to be able to send before the m->SuppressSending timer fires
+			if (m->SuppressSending && m->SuppressProbes - m->SuppressSending < 0)
+				m->SuppressProbes = NonZeroTime(m->SuppressSending);
+
+			if (m->SuppressProbes - m->timenow > mDNSPlatformOneSecond * 8)
+				{
+				LogMsg("InitializeLastAPTime ERROR m->SuppressProbes %d m->NextScheduledProbe %d m->NextScheduledQuery %d m->SuppressSending %d %d",
+					m->SuppressProbes     - m->timenow,
+					m->NextScheduledProbe - m->timenow,
+					m->NextScheduledQuery - m->timenow,
+					m->SuppressSending,
+					m->SuppressSending    - m->timenow);
+				m->SuppressProbes = NonZeroTime(m->timenow + DefaultProbeIntervalForTypeUnique/2 + mDNSRandom(DefaultProbeIntervalForTypeUnique/2));
+				}
+			}
+		rr->LastAPTime = m->SuppressProbes - rr->ThisAPInterval;
+		}
+	else if (m->SuppressProbes && m->SuppressProbes - m->timenow >= 0)
+		rr->LastAPTime = m->SuppressProbes - rr->ThisAPInterval + DefaultProbeIntervalForTypeUnique * DefaultProbeCountForTypeUnique + rr->ThisAPInterval / 2;
+	else
+		rr->LastAPTime = m->timenow - rr->ThisAPInterval;
+
+	// For reverse-mapping Sleep Proxy PTR records we don't want to start probing instantly -- we
+	// wait one second to give the client a chance to go to sleep, and then start our ARP/NDP probing.
+	// After three probes one second apart with no answer, we conclude the client is now sleeping
+	// and we can begin broadcasting our announcements to take over ownership of that IP address.
+	// If we don't wait for the client to go to sleep, then when the client sees our ARP Announcements there's a risk
+	// (depending on the OS and networking stack it's using) that it might interpret it as a conflict and change its IP address.
+	if (rr->AddressProxy.type) rr->LastAPTime = m->timenow;
+
+	// Unsolicited Neighbor Advertisements (RFC 2461 Section 7.2.6) give us fast address cache updating,
+	// but some older IPv6 clients get confused by them, so for now we don't send them. Without Unsolicited
+	// Neighbor Advertisements we have to rely on Neighbor Unreachability Detection instead, which is slower.
+	// Given this, we'll do our best to wake for existing IPv6 connections, but we don't want to encourage
+	// new ones for sleeping clients, so we'll we send deletions for our SPS clients' AAAA records.
+	if (m->KnownBugs & mDNS_KnownBug_LimitedIPv6)
+		if (rr->WakeUp.HMAC.l[0] && rr->resrec.rrtype == kDNSType_AAAA)
+			rr->LastAPTime = m->timenow - rr->ThisAPInterval + mDNSPlatformOneSecond * 10;
+	
+	// Set LastMCTime to now, to inhibit multicast responses
+	// (no need to send additional multicast responses when we're announcing anyway)
+	rr->LastMCTime      = m->timenow;
+	rr->LastMCInterface = mDNSInterfaceMark;
+	
+	SetNextAnnounceProbeTime(m, rr);
+	}
+
+mDNSlocal const domainname *SetUnicastTargetToHostName(mDNS *const m, AuthRecord *rr)
+	{
+	const domainname *target;
+	if (rr->AutoTarget)
+		{
+		// For autotunnel services pointing at our IPv6 ULA we don't need or want a NAT mapping, but for all other
+		// advertised services referencing our uDNS hostname, we want NAT mappings automatically created as appropriate,
+		// with the port number in our advertised SRV record automatically tracking the external mapped port.
+		DomainAuthInfo *AuthInfo = GetAuthInfoForName_internal(m, rr->resrec.name);
+		if (!AuthInfo || !AuthInfo->AutoTunnel) rr->AutoTarget = Target_AutoHostAndNATMAP;
+		}
+
+	target = GetServiceTarget(m, rr);
+	if (!target || target->c[0] == 0)
+		{
+		// defer registration until we've got a target
+		LogInfo("SetUnicastTargetToHostName No target for %s", ARDisplayString(m, rr));
+		rr->state = regState_NoTarget;
+		return mDNSNULL;
+		}
+	else
+		{
+		LogInfo("SetUnicastTargetToHostName target %##s for resource record %s", target->c, ARDisplayString(m,rr));
+		return target;
+		}
+	}
+
+// Right now this only applies to mDNS (.local) services where the target host is always m->MulticastHostname
+// Eventually we should unify this with GetServiceTarget() in uDNS.c
+mDNSlocal void SetTargetToHostName(mDNS *const m, AuthRecord *const rr)
+	{
+	domainname *const target = GetRRDomainNameTarget(&rr->resrec);
+	const domainname *newname = &m->MulticastHostname;
+
+	if (!target) LogInfo("SetTargetToHostName: Don't know how to set the target of rrtype %s", DNSTypeName(rr->resrec.rrtype));
+
+	if (!(rr->ForceMCast || rr->ARType == AuthRecordLocalOnly || rr->ARType == AuthRecordP2P || IsLocalDomain(&rr->namestorage)))
+		{
+		const domainname *const n = SetUnicastTargetToHostName(m, rr);
+		if (n) newname = n;
+		else { target->c[0] = 0; SetNewRData(&rr->resrec, mDNSNULL, 0); return; }
+		}
+
+	if (target && SameDomainName(target, newname))
+		debugf("SetTargetToHostName: Target of %##s is already %##s", rr->resrec.name->c, target->c);
+	
+	if (target && !SameDomainName(target, newname))
+		{
+		AssignDomainName(target, newname);
+		SetNewRData(&rr->resrec, mDNSNULL, 0);		// Update rdlength, rdestimate, rdatahash
+		
+		// If we're in the middle of probing this record, we need to start again,
+		// because changing its rdata may change the outcome of the tie-breaker.
+		// (If the record type is kDNSRecordTypeUnique (unconfirmed unique) then DefaultProbeCountForRecordType is non-zero.)
+		rr->ProbeCount     = DefaultProbeCountForRecordType(rr->resrec.RecordType);
+
+		// If we've announced this record, we really should send a goodbye packet for the old rdata before
+		// changing to the new rdata. However, in practice, we only do SetTargetToHostName for unique records,
+		// so when we announce them we'll set the kDNSClass_UniqueRRSet and clear any stale data that way.
+		if (rr->RequireGoodbye && rr->resrec.RecordType == kDNSRecordTypeShared)
+			debugf("Have announced shared record %##s (%s) at least once: should have sent a goodbye packet before updating",
+				rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
+
+		rr->AnnounceCount  = InitialAnnounceCount;
+		rr->RequireGoodbye = mDNSfalse;
+		InitializeLastAPTime(m, rr);
+		}
+	}
+
+mDNSlocal void AcknowledgeRecord(mDNS *const m, AuthRecord *const rr)
+	{
+	if (rr->RecordCallback)
+		{
+		// CAUTION: MUST NOT do anything more with rr after calling rr->Callback(), because the client's callback function
+		// is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc.
+		rr->Acknowledged = mDNStrue;
+		mDNS_DropLockBeforeCallback();		// Allow client to legally make mDNS API calls from the callback
+		rr->RecordCallback(m, rr, mStatus_NoError);
+		mDNS_ReclaimLockAfterCallback();	// Decrement mDNS_reentrancy to block mDNS API calls again
+		}
+	}
+
+mDNSexport void ActivateUnicastRegistration(mDNS *const m, AuthRecord *const rr)
+	{
+	// Make sure that we don't activate the SRV record and associated service records, if it is in
+	// NoTarget state. First time when a service is being instantiated, SRV record may be in NoTarget state.
+	// We should not activate any of the other reords (PTR, TXT) that are part of the service. When
+	// the target becomes available, the records will be reregistered.
+	if (rr->resrec.rrtype != kDNSType_SRV)
+		{
+		AuthRecord *srvRR = mDNSNULL;
+		if (rr->resrec.rrtype == kDNSType_PTR)
+			srvRR = rr->Additional1;
+		else if (rr->resrec.rrtype == kDNSType_TXT)
+			srvRR = rr->DependentOn;
+		if (srvRR)
+			{
+			if (srvRR->resrec.rrtype != kDNSType_SRV)
+				{
+				LogMsg("ActivateUnicastRegistration: ERROR!! Resource record %s wrong, expecting SRV type", ARDisplayString(m, srvRR));
+				}
+			else
+				{
+				LogInfo("ActivateUnicastRegistration: Found Service Record %s in state %d for %##s (%s)",
+					ARDisplayString(m, srvRR), srvRR->state, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
+				rr->state = srvRR->state;
+				}
+			}
+		}
+
+	if (rr->state == regState_NoTarget)
+		{
+		LogInfo("ActivateUnicastRegistration record %s in regState_NoTarget, not activating", ARDisplayString(m, rr));
+		return;
+		}
+	// When we wake up from sleep, we call ActivateUnicastRegistration. It is possible that just before we went to sleep,
+	// the service/record was being deregistered. In that case, we should not try to register again. For the cases where
+	// the records are deregistered due to e.g., no target for the SRV record, we would have returned from above if it
+	// was already in NoTarget state. If it was in the process of deregistration but did not complete fully before we went
+	// to sleep, then it is okay to start in Pending state as we will go back to NoTarget state if we don't have a target.
+	if (rr->resrec.RecordType == kDNSRecordTypeDeregistering)
+		{
+		LogInfo("ActivateUnicastRegistration: Resource record %s, current state %d, moving to DeregPending", ARDisplayString(m, rr), rr->state);
+		rr->state = regState_DeregPending;
+		}
+	else
+		{
+		LogInfo("ActivateUnicastRegistration: Resource record %s, current state %d, moving to Pending", ARDisplayString(m, rr), rr->state);
+		rr->state = regState_Pending;
+		}
+	rr->ProbeCount     = 0;
+	rr->AnnounceCount  = 0;
+	rr->ThisAPInterval = INIT_RECORD_REG_INTERVAL;
+	rr->LastAPTime     = m->timenow - rr->ThisAPInterval;
+	rr->expire         = 0;	// Forget about all the leases, start fresh
+	rr->uselease       = mDNStrue;
+	rr->updateid       = zeroID;
+	rr->SRVChanged     = mDNSfalse;
+	rr->updateError    = mStatus_NoError;
+	// RestartRecordGetZoneData calls this function whenever a new interface gets registered with core.
+	// The records might already be registered with the server and hence could have NAT state.
+	if (rr->NATinfo.clientContext)
+		{
+		mDNS_StopNATOperation_internal(m, &rr->NATinfo);
+		rr->NATinfo.clientContext = mDNSNULL;
+		}
+	if (rr->nta) { CancelGetZoneData(m, rr->nta); rr->nta = mDNSNULL; }
+	if (rr->tcp) { DisposeTCPConn(rr->tcp);       rr->tcp = mDNSNULL; }
+	if (m->NextuDNSEvent - (rr->LastAPTime + rr->ThisAPInterval) >= 0)
+		m->NextuDNSEvent = (rr->LastAPTime + rr->ThisAPInterval);
+	}
+
+// Two records qualify to be local duplicates if:
+// (a) the RecordTypes are the same, or
+// (b) one is Unique and the other Verified
+// (c) either is in the process of deregistering
+#define RecordLDT(A,B) ((A)->resrec.RecordType == (B)->resrec.RecordType || \
+	((A)->resrec.RecordType | (B)->resrec.RecordType) == (kDNSRecordTypeUnique | kDNSRecordTypeVerified) || \
+	((A)->resrec.RecordType == kDNSRecordTypeDeregistering || (B)->resrec.RecordType == kDNSRecordTypeDeregistering))
+
+#define RecordIsLocalDuplicate(A,B) \
+	((A)->resrec.InterfaceID == (B)->resrec.InterfaceID && RecordLDT((A),(B)) && IdenticalResourceRecord(&(A)->resrec, &(B)->resrec))
+
+mDNSlocal AuthRecord *CheckAuthIdenticalRecord(AuthHash *r, AuthRecord *rr)
+	{
+	AuthGroup *a;
+	AuthGroup **ag = &a;
+	AuthRecord **rp;
+	const mDNSu32 slot = AuthHashSlot(rr->resrec.name);
+
+	a = AuthGroupForRecord(r, slot, &rr->resrec);
+	if (!a) return mDNSNULL;
+	rp = &(*ag)->members;
+	while (*rp)
+		{
+		if (!RecordIsLocalDuplicate(*rp, rr))
+			rp=&(*rp)->next;
+		else
+			{
+			if ((*rp)->resrec.RecordType == kDNSRecordTypeDeregistering)
+				{
+				(*rp)->AnnounceCount = 0;
+				rp=&(*rp)->next;
+				}
+			else return *rp;
+			}
+		}
+	return (mDNSNULL);
+	}
+
+mDNSlocal mDNSBool CheckAuthRecordConflict(AuthHash *r, AuthRecord *rr)
+	{
+	AuthGroup *a;
+	AuthGroup **ag = &a;
+	AuthRecord **rp;
+	const mDNSu32 slot = AuthHashSlot(rr->resrec.name);
+
+	a = AuthGroupForRecord(r, slot, &rr->resrec);
+	if (!a) return mDNSfalse;
+	rp = &(*ag)->members;
+	while (*rp)
+		{
+		const AuthRecord *s1 = rr->RRSet ? rr->RRSet : rr;
+		const AuthRecord *s2 = (*rp)->RRSet ? (*rp)->RRSet : *rp;
+		if (s1 != s2 && SameResourceRecordSignature((*rp), rr) && !IdenticalSameNameRecord(&(*rp)->resrec, &rr->resrec))
+			return mDNStrue;
+		else
+			rp=&(*rp)->next;
+		}
+	return (mDNSfalse);
+	}
+
+// checks to see if "rr" is already present
+mDNSlocal AuthRecord *CheckAuthSameRecord(AuthHash *r, AuthRecord *rr)
+	{
+	AuthGroup *a;
+	AuthGroup **ag = &a;
+	AuthRecord **rp;
+	const mDNSu32 slot = AuthHashSlot(rr->resrec.name);
+
+	a = AuthGroupForRecord(r, slot, &rr->resrec);
+	if (!a) return mDNSNULL;
+	rp = &(*ag)->members;
+	while (*rp)
+		{
+		if (*rp != rr)
+			rp=&(*rp)->next;
+		else
+			{
+			return *rp;
+			}
+		}
+	return (mDNSNULL);
+	}
+
+// Exported so uDNS.c can call this
+mDNSexport mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr)
+	{
+	domainname *target = GetRRDomainNameTarget(&rr->resrec);
+	AuthRecord *r;
+	AuthRecord **p = &m->ResourceRecords;
+	AuthRecord **d = &m->DuplicateRecords;
+
+	if ((mDNSs32)rr->resrec.rroriginalttl <= 0)
+		{ LogMsg("mDNS_Register_internal: TTL %X should be 1 - 0x7FFFFFFF %s", rr->resrec.rroriginalttl, ARDisplayString(m, rr)); return(mStatus_BadParamErr); }
+
+	if (!rr->resrec.RecordType)
+		{ LogMsg("mDNS_Register_internal: RecordType must be non-zero %s", ARDisplayString(m, rr)); return(mStatus_BadParamErr); }
+
+	if (m->ShutdownTime)
+		{ LogMsg("mDNS_Register_internal: Shutting down, can't register %s", ARDisplayString(m, rr)); return(mStatus_ServiceNotRunning); }
+	
+	if (m->DivertMulticastAdvertisements && !AuthRecord_uDNS(rr))
+		{
+		mDNSInterfaceID previousID = rr->resrec.InterfaceID;
+		if (rr->resrec.InterfaceID == mDNSInterface_Any || rr->resrec.InterfaceID == mDNSInterface_P2P)
+			{
+			rr->resrec.InterfaceID = mDNSInterface_LocalOnly;
+			rr->ARType = AuthRecordLocalOnly;
+			}
+		if (rr->resrec.InterfaceID != mDNSInterface_LocalOnly)
+			{
+			NetworkInterfaceInfo *intf = FirstInterfaceForID(m, rr->resrec.InterfaceID);
+			if (intf && !intf->Advertise){ rr->resrec.InterfaceID = mDNSInterface_LocalOnly; rr->ARType = AuthRecordLocalOnly; }
+			}
+		if (rr->resrec.InterfaceID != previousID)
+			LogInfo("mDNS_Register_internal: Diverting record to local-only %s", ARDisplayString(m, rr));
+		}
+
+	if (RRLocalOnly(rr))
+		{
+		if (CheckAuthSameRecord(&m->rrauth, rr))
+			{
+			LogMsg("mDNS_Register_internal: ERROR!! Tried to register LocalOnly AuthRecord %p %##s (%s) that's already in the list",
+				rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
+			return(mStatus_AlreadyRegistered);
+			}
+		}
+	else
+		{
+		while (*p && *p != rr) p=&(*p)->next;
+		if (*p)
+			{
+			LogMsg("mDNS_Register_internal: ERROR!! Tried to register AuthRecord %p %##s (%s) that's already in the list",
+				rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
+			return(mStatus_AlreadyRegistered);
+			}
+		}
+
+	while (*d && *d != rr) d=&(*d)->next;
+	if (*d)
+		{
+		LogMsg("mDNS_Register_internal: ERROR!! Tried to register AuthRecord %p %##s (%s) that's already in the Duplicate list",
+				rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
+		return(mStatus_AlreadyRegistered);
+		}
+
+	if (rr->DependentOn)
+		{
+		if (rr->resrec.RecordType == kDNSRecordTypeUnique)
+			rr->resrec.RecordType =  kDNSRecordTypeVerified;
+		else
+			{
+			LogMsg("mDNS_Register_internal: ERROR! %##s (%s): rr->DependentOn && RecordType != kDNSRecordTypeUnique",
+				rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
+			return(mStatus_Invalid);
+			}
+		if (!(rr->DependentOn->resrec.RecordType & (kDNSRecordTypeUnique | kDNSRecordTypeVerified | kDNSRecordTypeKnownUnique)))
+			{
+			LogMsg("mDNS_Register_internal: ERROR! %##s (%s): rr->DependentOn->RecordType bad type %X",
+				rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), rr->DependentOn->resrec.RecordType);
+			return(mStatus_Invalid);
+			}
+		}
+
+	// If this resource record is referencing a specific interface, make sure it exists.
+	// Skip checks for LocalOnly and P2P as they are not valid InterfaceIDs. Also, for scoped
+	// entries in /etc/hosts skip that check as that interface may not be valid at this time.
+	if (rr->resrec.InterfaceID && rr->ARType != AuthRecordLocalOnly && rr->ARType != AuthRecordP2P)
+		{
+		NetworkInterfaceInfo *intf = FirstInterfaceForID(m, rr->resrec.InterfaceID);
+		if (!intf)
+			{
+			debugf("mDNS_Register_internal: Bogus InterfaceID %p in resource record", rr->resrec.InterfaceID);
+			return(mStatus_BadReferenceErr);
+			}
+		}
+
+	rr->next = mDNSNULL;
+
+	// Field Group 1: The actual information pertaining to this resource record
+	// Set up by client prior to call
+
+	// Field Group 2: Persistent metadata for Authoritative Records
+//	rr->Additional1       = set to mDNSNULL  in mDNS_SetupResourceRecord; may be overridden by client
+//	rr->Additional2       = set to mDNSNULL  in mDNS_SetupResourceRecord; may be overridden by client
+//	rr->DependentOn       = set to mDNSNULL  in mDNS_SetupResourceRecord; may be overridden by client
+//	rr->RRSet             = set to mDNSNULL  in mDNS_SetupResourceRecord; may be overridden by client
+//	rr->Callback          = already set      in mDNS_SetupResourceRecord
+//	rr->Context           = already set      in mDNS_SetupResourceRecord
+//	rr->RecordType        = already set      in mDNS_SetupResourceRecord
+//	rr->HostTarget        = set to mDNSfalse in mDNS_SetupResourceRecord; may be overridden by client
+//	rr->AllowRemoteQuery  = set to mDNSfalse in mDNS_SetupResourceRecord; may be overridden by client
+	// Make sure target is not uninitialized data, or we may crash writing debugging log messages
+	if (rr->AutoTarget && target) target->c[0] = 0;
+
+	// Field Group 3: Transient state for Authoritative Records
+	rr->Acknowledged      = mDNSfalse;
+	rr->ProbeCount        = DefaultProbeCountForRecordType(rr->resrec.RecordType);
+	rr->AnnounceCount     = InitialAnnounceCount;
+	rr->RequireGoodbye    = mDNSfalse;
+	rr->AnsweredLocalQ    = mDNSfalse;
+	rr->IncludeInProbe    = mDNSfalse;
+	rr->ImmedUnicast      = mDNSfalse;
+	rr->SendNSECNow       = mDNSNULL;
+	rr->ImmedAnswer       = mDNSNULL;
+	rr->ImmedAdditional   = mDNSNULL;
+	rr->SendRNow          = mDNSNULL;
+	rr->v4Requester       = zerov4Addr;
+	rr->v6Requester       = zerov6Addr;
+	rr->NextResponse      = mDNSNULL;
+	rr->NR_AnswerTo       = mDNSNULL;
+	rr->NR_AdditionalTo   = mDNSNULL;
+	if (!rr->AutoTarget) InitializeLastAPTime(m, rr);
+//	rr->LastAPTime        = Set for us in InitializeLastAPTime()
+//	rr->LastMCTime        = Set for us in InitializeLastAPTime()
+//	rr->LastMCInterface   = Set for us in InitializeLastAPTime()
+	rr->NewRData          = mDNSNULL;
+	rr->newrdlength       = 0;
+	rr->UpdateCallback    = mDNSNULL;
+	rr->UpdateCredits     = kMaxUpdateCredits;
+	rr->NextUpdateCredit  = 0;
+	rr->UpdateBlocked     = 0;
+
+	// For records we're holding as proxy (except reverse-mapping PTR records) two announcements is sufficient
+	if (rr->WakeUp.HMAC.l[0] && !rr->AddressProxy.type) rr->AnnounceCount = 2;
+
+	// Field Group 4: Transient uDNS state for Authoritative Records
+	rr->state             = regState_Zero;
+	rr->uselease          = 0;
+	rr->expire            = 0;
+	rr->Private           = 0;
+	rr->updateid          = zeroID;
+	rr->zone              = rr->resrec.name;
+	rr->nta               = mDNSNULL;
+	rr->tcp               = mDNSNULL;
+	rr->OrigRData         = 0;
+	rr->OrigRDLen         = 0;
+	rr->InFlightRData     = 0;
+	rr->InFlightRDLen     = 0;
+	rr->QueuedRData       = 0;
+	rr->QueuedRDLen       = 0;
+	//mDNSPlatformMemZero(&rr->NATinfo, sizeof(rr->NATinfo));
+	// We should be recording the actual internal port for this service record here. Once we initiate our NAT mapping
+	// request we'll subsequently overwrite srv.port with the allocated external NAT port -- potentially multiple
+	// times with different values if the external NAT port changes during the lifetime of the service registration.
+	//if (rr->resrec.rrtype == kDNSType_SRV) rr->NATinfo.IntPort = rr->resrec.rdata->u.srv.port;
+
+//	rr->resrec.interface         = already set in mDNS_SetupResourceRecord
+//	rr->resrec.name->c           = MUST be set by client
+//	rr->resrec.rrtype            = already set in mDNS_SetupResourceRecord
+//	rr->resrec.rrclass           = already set in mDNS_SetupResourceRecord
+//	rr->resrec.rroriginalttl     = already set in mDNS_SetupResourceRecord
+//	rr->resrec.rdata             = MUST be set by client, unless record type is CNAME or PTR and rr->HostTarget is set
+
+	// BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct,
+	// since RFC 1035 specifies a TXT record as "One or more <character-string>s", not "Zero or more <character-string>s".
+	// Since some legacy apps try to create zero-length TXT records, we'll silently correct it here.
+	if (rr->resrec.rrtype == kDNSType_TXT && rr->resrec.rdlength == 0) { rr->resrec.rdlength = 1; rr->resrec.rdata->u.txt.c[0] = 0; }
+
+	if (rr->AutoTarget)
+		{
+		SetTargetToHostName(m, rr);	// Also sets rdlength and rdestimate for us, and calls InitializeLastAPTime();
+#ifndef UNICAST_DISABLED
+		// If we have no target record yet, SetTargetToHostName will set rr->state == regState_NoTarget
+		// In this case we leave the record half-formed in the list, and later we'll remove it from the list and re-add it properly.
+		if (rr->state == regState_NoTarget)
+			{
+			// Initialize the target so that we don't crash while logging etc.
+			domainname *tar = GetRRDomainNameTarget(&rr->resrec);
+			if (tar) tar->c[0] = 0;
+			LogInfo("mDNS_Register_internal: record %s in NoTarget state", ARDisplayString(m, rr));
+			}
+#endif
+		}
+	else
+		{
+		rr->resrec.rdlength   = GetRDLength(&rr->resrec, mDNSfalse);
+		rr->resrec.rdestimate = GetRDLength(&rr->resrec, mDNStrue);
+		}
+
+	if (!ValidateDomainName(rr->resrec.name))
+		{ LogMsg("Attempt to register record with invalid name: %s", ARDisplayString(m, rr)); return(mStatus_Invalid); }
+
+	// Don't do this until *after* we've set rr->resrec.rdlength
+	if (!ValidateRData(rr->resrec.rrtype, rr->resrec.rdlength, rr->resrec.rdata))
+		{ LogMsg("Attempt to register record with invalid rdata: %s", ARDisplayString(m, rr)); return(mStatus_Invalid); }
+
+	rr->resrec.namehash   = DomainNameHashValue(rr->resrec.name);
+	rr->resrec.rdatahash  = target ? DomainNameHashValue(target) : RDataHashValue(&rr->resrec);
+	
+	if (RRLocalOnly(rr))
+		{
+		// If this is supposed to be unique, make sure we don't have any name conflicts.
+		// If we found a conflict, we may still want to insert the record in the list but mark it appropriately
+		// (kDNSRecordTypeDeregistering) so that we deliver RMV events to the application. But this causes more
+		// complications and not clear whether there are any benefits. See rdar:9304275 for details.
+		// Hence, just bail out.
+		if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask)
+			{
+			if (CheckAuthRecordConflict(&m->rrauth, rr))
+				{
+				LogInfo("mDNS_Register_internal: Name conflict %s (%p), InterfaceID %p", ARDisplayString(m, rr), rr, rr->resrec.InterfaceID);
+				return mStatus_NameConflict;
+				}
+			}
+		}
+
+	// For uDNS records, we don't support duplicate checks at this time.
+#ifndef UNICAST_DISABLED
+	if (AuthRecord_uDNS(rr))
+		{
+		if (!m->NewLocalRecords) m->NewLocalRecords = rr;
+		// When we called SetTargetToHostName, it may have caused mDNS_Register_internal to be re-entered, appending new
+		// records to the list, so we now need to update p to advance to the new end to the list before appending our new record.
+		// Note that for AutoTunnel this should never happen, but this check makes the code future-proof.
+		while (*p) p=&(*p)->next;
+		*p = rr;
+		if (rr->resrec.RecordType == kDNSRecordTypeUnique) rr->resrec.RecordType = kDNSRecordTypeVerified;
+		rr->ProbeCount    = 0;
+		rr->AnnounceCount = 0;
+		if (rr->state != regState_NoTarget) ActivateUnicastRegistration(m, rr);
+		return(mStatus_NoError);			// <--- Note: For unicast records, code currently bails out at this point
+		}
+#endif
+
+	// Now that we've finished building our new record, make sure it's not identical to one we already have
+	if (RRLocalOnly(rr))
+		{
+		rr->ProbeCount    = 0;
+		rr->AnnounceCount = 0;
+		r = CheckAuthIdenticalRecord(&m->rrauth, rr);
+		}
+	else
+		{
+		for (r = m->ResourceRecords; r; r=r->next)
+			if (RecordIsLocalDuplicate(r, rr))
+				{
+				if (r->resrec.RecordType == kDNSRecordTypeDeregistering) r->AnnounceCount = 0;
+				else break;
+				}
+		}
+
+	if (r)
+		{
+		debugf("mDNS_Register_internal:Adding to duplicate list %s", ARDisplayString(m,rr));
+		*d = rr;
+		// If the previous copy of this record is already verified unique,
+		// then indicate that we should move this record promptly to kDNSRecordTypeUnique state.
+		// Setting ProbeCount to zero will cause SendQueries() to advance this record to
+		// kDNSRecordTypeVerified state and call the client callback at the next appropriate time.
+		if (rr->resrec.RecordType == kDNSRecordTypeUnique && r->resrec.RecordType == kDNSRecordTypeVerified)
+			rr->ProbeCount = 0;
+		}
+	else
+		{
+		debugf("mDNS_Register_internal: Adding to active record list %s", ARDisplayString(m,rr));
+		if (RRLocalOnly(rr))
+			{
+			AuthGroup *ag;
+			ag = InsertAuthRecord(m, &m->rrauth, rr);
+			if (ag && !ag->NewLocalOnlyRecords) {
+				m->NewLocalOnlyRecords = mDNStrue;
+				ag->NewLocalOnlyRecords = rr;
+			}
+			// No probing for LocalOnly records, Acknowledge them right away
+			if (rr->resrec.RecordType == kDNSRecordTypeUnique) rr->resrec.RecordType = kDNSRecordTypeVerified;
+			AcknowledgeRecord(m, rr);
+			return(mStatus_NoError);
+			}
+		else
+			{
+			if (!m->NewLocalRecords) m->NewLocalRecords = rr;
+			*p = rr;
+			}
+		}
+
+	if (!AuthRecord_uDNS(rr))	// This check is superfluous, given that for unicast records we (currently) bail out above
+		{
+		// For records that are not going to probe, acknowledge them right away
+		if (rr->resrec.RecordType != kDNSRecordTypeUnique && rr->resrec.RecordType != kDNSRecordTypeDeregistering)
+			AcknowledgeRecord(m, rr);
+
+		// Adding a record may affect whether or not we should sleep
+		mDNS_UpdateAllowSleep(m);
+		}
+
+	return(mStatus_NoError);
+	}
+
+mDNSlocal void RecordProbeFailure(mDNS *const m, const AuthRecord *const rr)
+	{
+	m->ProbeFailTime = m->timenow;
+	m->NumFailedProbes++;
+	// If we've had fifteen or more probe failures, rate-limit to one every five seconds.
+	// If a bunch of hosts have all been configured with the same name, then they'll all
+	// conflict and run through the same series of names: name-2, name-3, name-4, etc.,
+	// up to name-10. After that they'll start adding random increments in the range 1-100,
+	// so they're more likely to branch out in the available namespace and settle on a set of
+	// unique names quickly. If after five more tries the host is still conflicting, then we
+	// may have a serious problem, so we start rate-limiting so we don't melt down the network.
+	if (m->NumFailedProbes >= 15)
+		{
+		m->SuppressProbes = NonZeroTime(m->timenow + mDNSPlatformOneSecond * 5);
+		LogMsg("Excessive name conflicts (%lu) for %##s (%s); rate limiting in effect",
+			m->NumFailedProbes, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
+		}
+	}
+
+mDNSlocal void CompleteRDataUpdate(mDNS *const m, AuthRecord *const rr)
+	{
+	RData *OldRData = rr->resrec.rdata;
+	mDNSu16 OldRDLen = rr->resrec.rdlength;
+	SetNewRData(&rr->resrec, rr->NewRData, rr->newrdlength);	// Update our rdata
+	rr->NewRData = mDNSNULL;									// Clear the NewRData pointer ...
+	if (rr->UpdateCallback)
+		rr->UpdateCallback(m, rr, OldRData, OldRDLen);			// ... and let the client know
+	}
+
+// Note: mDNS_Deregister_internal can call a user callback, which may change the record list and/or question list.
+// Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
+// Exported so uDNS.c can call this
+mDNSexport mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, mDNS_Dereg_type drt)
+	{
+	AuthRecord *r2;
+	mDNSu8 RecordType = rr->resrec.RecordType;
+	AuthRecord **p = &m->ResourceRecords;	// Find this record in our list of active records
+	mDNSBool dupList = mDNSfalse;
+
+	if (RRLocalOnly(rr))
+		{
+		AuthGroup *a;
+		AuthGroup **ag = &a;
+		AuthRecord **rp;
+		const mDNSu32 slot = AuthHashSlot(rr->resrec.name);
+	
+		a = AuthGroupForRecord(&m->rrauth, slot, &rr->resrec);
+		if (!a) return mDNSfalse;
+		rp = &(*ag)->members;
+		while (*rp && *rp != rr) rp=&(*rp)->next;
+		p = rp;
+		}
+	else
+		{
+		while (*p && *p != rr) p=&(*p)->next;
+		}
+
+	if (*p)
+		{
+		// We found our record on the main list. See if there are any duplicates that need special handling.
+		if (drt == mDNS_Dereg_conflict)		// If this was a conflict, see that all duplicates get the same treatment
+			{
+			// Scan for duplicates of rr, and mark them for deregistration at the end of this routine, after we've finished
+			// deregistering rr. We need to do this scan *before* we give the client the chance to free and reuse the rr memory.
+			for (r2 = m->DuplicateRecords; r2; r2=r2->next) if (RecordIsLocalDuplicate(r2, rr)) r2->ProbeCount = 0xFF;
+			}
+		else
+			{
+			// Before we delete the record (and potentially send a goodbye packet)
+			// first see if we have a record on the duplicate list ready to take over from it.
+			AuthRecord **d = &m->DuplicateRecords;
+			while (*d && !RecordIsLocalDuplicate(*d, rr)) d=&(*d)->next;
+			if (*d)
+				{
+				AuthRecord *dup = *d;
+				debugf("mDNS_Register_internal: Duplicate record %p taking over from %p %##s (%s)",
+					dup, rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
+				*d        = dup->next;		// Cut replacement record from DuplicateRecords list
+				if (RRLocalOnly(rr))
+					{
+					dup->next = mDNSNULL;
+					if (!InsertAuthRecord(m, &m->rrauth, dup)) LogMsg("mDNS_Deregister_internal: ERROR!! cannot insert %s", ARDisplayString(m, dup));
+					}
+				else
+					{
+					dup->next = rr->next;		// And then...
+					rr->next  = dup;			// ... splice it in right after the record we're about to delete
+					}
+				dup->resrec.RecordType        = rr->resrec.RecordType;
+				dup->ProbeCount      = rr->ProbeCount;
+				dup->AnnounceCount   = rr->AnnounceCount;
+				dup->RequireGoodbye  = rr->RequireGoodbye;
+				dup->AnsweredLocalQ  = rr->AnsweredLocalQ;
+				dup->ImmedAnswer     = rr->ImmedAnswer;
+				dup->ImmedUnicast    = rr->ImmedUnicast;
+				dup->ImmedAdditional = rr->ImmedAdditional;
+				dup->v4Requester     = rr->v4Requester;
+				dup->v6Requester     = rr->v6Requester;
+				dup->ThisAPInterval  = rr->ThisAPInterval;
+				dup->LastAPTime      = rr->LastAPTime;
+				dup->LastMCTime      = rr->LastMCTime;
+				dup->LastMCInterface = rr->LastMCInterface;
+				dup->Private         = rr->Private;
+				dup->state           = rr->state;
+				rr->RequireGoodbye = mDNSfalse;
+				rr->AnsweredLocalQ = mDNSfalse;
+				}
+			}
+		}
+	else
+		{
+		// We didn't find our record on the main list; try the DuplicateRecords list instead.
+		p = &m->DuplicateRecords;
+		while (*p && *p != rr) p=&(*p)->next;
+		// If we found our record on the duplicate list, then make sure we don't send a goodbye for it
+		if (*p) { rr->RequireGoodbye = mDNSfalse; dupList = mDNStrue; }
+		if (*p) debugf("mDNS_Deregister_internal: Deleting DuplicateRecord %p %##s (%s)",
+			rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
+		}
+
+	if (!*p)
+		{
+		// No need to log an error message if we already know this is a potentially repeated deregistration
+		if (drt != mDNS_Dereg_repeat)
+			LogMsg("mDNS_Deregister_internal: Record %p not found in list %s", rr, ARDisplayString(m,rr));
+		return(mStatus_BadReferenceErr);
+		}
+
+	// If this is a shared record and we've announced it at least once,
+	// we need to retract that announcement before we delete the record
+
+	// If this is a record (including mDNSInterface_LocalOnly records) for which we've given local-only answers then
+	// it's tempting to just do "AnswerAllLocalQuestionsWithLocalAuthRecord(m, rr, mDNSfalse)" here, but that would not not be safe.
+	// The AnswerAllLocalQuestionsWithLocalAuthRecord routine walks the question list invoking client callbacks, using the "m->CurrentQuestion"
+	// mechanism to cope with the client callback modifying the question list while that's happening.
+	// However, mDNS_Deregister could have been called from a client callback (e.g. from the domain enumeration callback FoundDomain)
+	// which means that the "m->CurrentQuestion" mechanism is already in use to protect that list, so we can't use it twice.
+	// More generally, if we invoke callbacks from within a client callback, then those callbacks could deregister other
+	// records, thereby invoking yet more callbacks, without limit.
+	// The solution is to defer delivering the "Remove" events until mDNS_Execute time, just like we do for sending
+	// actual goodbye packets.
+	
+#ifndef UNICAST_DISABLED
+	if (AuthRecord_uDNS(rr))
+		{
+		if (rr->RequireGoodbye)
+			{
+			if (rr->tcp) { DisposeTCPConn(rr->tcp); rr->tcp = mDNSNULL; }
+			rr->resrec.RecordType    = kDNSRecordTypeDeregistering;
+			m->LocalRemoveEvents     = mDNStrue;
+			uDNS_DeregisterRecord(m, rr);
+			// At this point unconditionally we bail out
+			// Either uDNS_DeregisterRecord will have completed synchronously, and called CompleteDeregistration,
+			// which calls us back here with RequireGoodbye set to false, or it will have initiated the deregistration
+			// process and will complete asynchronously. Either way we don't need to do anything more here.
+			return(mStatus_NoError);
+			}
+		// Sometimes the records don't complete proper deregistration i.e., don't wait for a response
+		// from the server. In that case, if the records have been part of a group update, clear the
+		// state here. Some recors e.g., AutoTunnel gets reused without ever being completely initialized
+		rr->updateid = zeroID;
+
+		// We defer cleaning up NAT state only after sending goodbyes. This is important because
+		// RecordRegistrationGotZoneData guards against creating NAT state if clientContext is non-NULL.
+		// This happens today when we turn on/off interface where we get multiple network transitions
+		// and RestartRecordGetZoneData triggers re-registration of the resource records even though
+		// they may be in Registered state which causes NAT information to be setup multiple times. Defering
+		// the cleanup here keeps clientContext non-NULL and hence prevents that. Note that cleaning up
+		// NAT state here takes care of the case where we did not send goodbyes at all.
+		if (rr->NATinfo.clientContext)
+			{
+			mDNS_StopNATOperation_internal(m, &rr->NATinfo);
+			rr->NATinfo.clientContext = mDNSNULL;
+			}
+		if (rr->nta) { CancelGetZoneData(m, rr->nta); rr->nta = mDNSNULL; }
+		if (rr->tcp) { DisposeTCPConn(rr->tcp);       rr->tcp = mDNSNULL; }
+		}
+#endif // UNICAST_DISABLED
+
+	if      (RecordType == kDNSRecordTypeUnregistered)
+		LogMsg("mDNS_Deregister_internal: %s already marked kDNSRecordTypeUnregistered", ARDisplayString(m, rr));
+	else if (RecordType == kDNSRecordTypeDeregistering)
+		{
+		LogMsg("mDNS_Deregister_internal: %s already marked kDNSRecordTypeDeregistering", ARDisplayString(m, rr));
+		return(mStatus_BadReferenceErr);
+		}
+
+	// <rdar://problem/7457925> Local-only questions don't get remove events for unique records
+	// We may want to consider changing this code so that we generate local-only question "rmv"
+	// events (and maybe goodbye packets too) for unique records as well as for shared records
+	// Note: If we change the logic for this "if" statement, need to ensure that the code in
+	// CompleteDeregistration() sets the appropriate state variables to gaurantee that "else"
+	// clause will execute here and the record will be cut from the list.
+	if (rr->WakeUp.HMAC.l[0] ||
+		(RecordType == kDNSRecordTypeShared && (rr->RequireGoodbye || rr->AnsweredLocalQ)))
+		{
+		verbosedebugf("mDNS_Deregister_internal: Starting deregistration for %s", ARDisplayString(m, rr));
+		rr->resrec.RecordType    = kDNSRecordTypeDeregistering;
+		rr->resrec.rroriginalttl = 0;
+		rr->AnnounceCount        = rr->WakeUp.HMAC.l[0] ? WakeupCount : (drt == mDNS_Dereg_rapid) ? 1 : GoodbyeCount;
+		rr->ThisAPInterval       = mDNSPlatformOneSecond * 2;
+		rr->LastAPTime           = m->timenow - rr->ThisAPInterval;
+		m->LocalRemoveEvents     = mDNStrue;
+		if (m->NextScheduledResponse - (m->timenow + mDNSPlatformOneSecond/10) >= 0)
+			m->NextScheduledResponse = (m->timenow + mDNSPlatformOneSecond/10);
+		}
+	else
+		{
+		if (!dupList && RRLocalOnly(rr))
+			{
+			AuthGroup *ag = RemoveAuthRecord(m, &m->rrauth, rr);
+			if (ag->NewLocalOnlyRecords == rr) ag->NewLocalOnlyRecords = rr->next;
+			}
+		else
+			{
+			*p = rr->next;					// Cut this record from the list
+			if (m->NewLocalRecords == rr) m->NewLocalRecords = rr->next;
+			}
+		// If someone is about to look at this, bump the pointer forward
+		if (m->CurrentRecord   == rr) m->CurrentRecord   = rr->next;
+		rr->next = mDNSNULL;
+
+		// Should we generate local remove events here?
+		// i.e. something like:
+		// if (rr->AnsweredLocalQ) { AnswerAllLocalQuestionsWithLocalAuthRecord(m, rr, mDNSfalse); rr->AnsweredLocalQ = mDNSfalse; }
+
+		verbosedebugf("mDNS_Deregister_internal: Deleting record for %s", ARDisplayString(m, rr));
+		rr->resrec.RecordType = kDNSRecordTypeUnregistered;
+
+		if ((drt == mDNS_Dereg_conflict || drt == mDNS_Dereg_repeat) && RecordType == kDNSRecordTypeShared)
+			debugf("mDNS_Deregister_internal: Cannot have a conflict on a shared record! %##s (%s)",
+				rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
+
+		// If we have an update queued up which never executed, give the client a chance to free that memory
+		if (rr->NewRData) CompleteRDataUpdate(m, rr);	// Update our rdata, clear the NewRData pointer, and return memory to the client
+
+
+		// CAUTION: MUST NOT do anything more with rr after calling rr->Callback(), because the client's callback function
+		// is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc.
+		// In this case the likely client action to the mStatus_MemFree message is to free the memory,
+		// so any attempt to touch rr after this is likely to lead to a crash.
+		if (drt != mDNS_Dereg_conflict)
+			{
+			mDNS_DropLockBeforeCallback();		// Allow client to legally make mDNS API calls from the callback
+			LogInfo("mDNS_Deregister_internal: mStatus_MemFree for %s", ARDisplayString(m, rr));
+			if (rr->RecordCallback)
+				rr->RecordCallback(m, rr, mStatus_MemFree);			// MUST NOT touch rr after this
+			mDNS_ReclaimLockAfterCallback();	// Decrement mDNS_reentrancy to block mDNS API calls again
+			}
+		else
+			{
+			RecordProbeFailure(m, rr);
+			mDNS_DropLockBeforeCallback();		// Allow client to legally make mDNS API calls from the callback
+			if (rr->RecordCallback)
+				rr->RecordCallback(m, rr, mStatus_NameConflict);	// MUST NOT touch rr after this
+			mDNS_ReclaimLockAfterCallback();	// Decrement mDNS_reentrancy to block mDNS API calls again
+			// Now that we've finished deregistering rr, check our DuplicateRecords list for any that we marked previously.
+			// Note that with all the client callbacks going on, by the time we get here all the
+			// records we marked may have been explicitly deregistered by the client anyway.
+			r2 = m->DuplicateRecords;
+			while (r2)
+				{
+				if (r2->ProbeCount != 0xFF) r2 = r2->next;
+				else { mDNS_Deregister_internal(m, r2, mDNS_Dereg_conflict); r2 = m->DuplicateRecords; }
+				}
+			}
+		}
+	mDNS_UpdateAllowSleep(m);
+	return(mStatus_NoError);
+	}
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - Packet Sending Functions
+#endif
+
+mDNSlocal void AddRecordToResponseList(AuthRecord ***nrpp, AuthRecord *rr, AuthRecord *add)
+	{
+	if (rr->NextResponse == mDNSNULL && *nrpp != &rr->NextResponse)
+		{
+		**nrpp = rr;
+		// NR_AdditionalTo must point to a record with NR_AnswerTo set (and not NR_AdditionalTo)
+		// If 'add' does not meet this requirement, then follow its NR_AdditionalTo pointer to a record that does
+		// The referenced record will definitely be acceptable (by recursive application of this rule)
+		if (add && add->NR_AdditionalTo) add = add->NR_AdditionalTo;
+		rr->NR_AdditionalTo = add;
+		*nrpp = &rr->NextResponse;
+		}
+	debugf("AddRecordToResponseList: %##s (%s) already in list", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
+	}
+
+mDNSlocal void AddAdditionalsToResponseList(mDNS *const m, AuthRecord *ResponseRecords, AuthRecord ***nrpp, const mDNSInterfaceID InterfaceID)
+	{
+	AuthRecord  *rr, *rr2;
+	for (rr=ResponseRecords; rr; rr=rr->NextResponse)			// For each record we plan to put
+		{
+		// (Note: This is an "if", not a "while". If we add a record, we'll find it again
+		// later in the "for" loop, and we will follow further "additional" links then.)
+		if (rr->Additional1 && ResourceRecordIsValidInterfaceAnswer(rr->Additional1, InterfaceID))
+			AddRecordToResponseList(nrpp, rr->Additional1, rr);
+
+		if (rr->Additional2 && ResourceRecordIsValidInterfaceAnswer(rr->Additional2, InterfaceID))
+			AddRecordToResponseList(nrpp, rr->Additional2, rr);
+
+		// For SRV records, automatically add the Address record(s) for the target host
+		if (rr->resrec.rrtype == kDNSType_SRV)
+			{
+			for (rr2=m->ResourceRecords; rr2; rr2=rr2->next)					// Scan list of resource records
+				if (RRTypeIsAddressType(rr2->resrec.rrtype) &&					// For all address records (A/AAAA) ...
+					ResourceRecordIsValidInterfaceAnswer(rr2, InterfaceID) &&	// ... which are valid for answer ...
+					rr->resrec.rdatahash == rr2->resrec.namehash &&			// ... whose name is the name of the SRV target
+					SameDomainName(&rr->resrec.rdata->u.srv.target, rr2->resrec.name))
+					AddRecordToResponseList(nrpp, rr2, rr);
+			}
+		else if (RRTypeIsAddressType(rr->resrec.rrtype))	// For A or AAAA, put counterpart as additional
+			{
+			for (rr2=m->ResourceRecords; rr2; rr2=rr2->next)					// Scan list of resource records
+				if (RRTypeIsAddressType(rr2->resrec.rrtype) &&					// For all address records (A/AAAA) ...
+					ResourceRecordIsValidInterfaceAnswer(rr2, InterfaceID) &&	// ... which are valid for answer ...
+					rr->resrec.namehash == rr2->resrec.namehash &&				// ... and have the same name
+					SameDomainName(rr->resrec.name, rr2->resrec.name))
+					AddRecordToResponseList(nrpp, rr2, rr);
+			}
+		else if (rr->resrec.rrtype == kDNSType_PTR)			// For service PTR, see if we want to add DeviceInfo record
+			{
+			if (ResourceRecordIsValidInterfaceAnswer(&m->DeviceInfo, InterfaceID) &&
+				SameDomainLabel(rr->resrec.rdata->u.name.c, m->DeviceInfo.resrec.name->c))
+				AddRecordToResponseList(nrpp, &m->DeviceInfo, rr);
+			}
+		}
+	}
+
+mDNSlocal void SendDelayedUnicastResponse(mDNS *const m, const mDNSAddr *const dest, const mDNSInterfaceID InterfaceID)
+	{
+	AuthRecord *rr;
+	AuthRecord  *ResponseRecords = mDNSNULL;
+	AuthRecord **nrp             = &ResponseRecords;
+	NetworkInterfaceInfo *intf = FirstInterfaceForID(m, InterfaceID);
+
+	// Make a list of all our records that need to be unicast to this destination
+	for (rr = m->ResourceRecords; rr; rr=rr->next)
+		{
+		// If we find we can no longer unicast this answer, clear ImmedUnicast
+		if (rr->ImmedAnswer == mDNSInterfaceMark               ||
+			mDNSSameIPv4Address(rr->v4Requester, onesIPv4Addr) ||
+			mDNSSameIPv6Address(rr->v6Requester, onesIPv6Addr)  )
+			rr->ImmedUnicast = mDNSfalse;
+
+		if (rr->ImmedUnicast && rr->ImmedAnswer == InterfaceID)
+			{
+			if ((dest->type == mDNSAddrType_IPv4 && mDNSSameIPv4Address(rr->v4Requester, dest->ip.v4)) ||
+				(dest->type == mDNSAddrType_IPv6 && mDNSSameIPv6Address(rr->v6Requester, dest->ip.v6)))
+				{
+				rr->ImmedAnswer  = mDNSNULL;				// Clear the state fields
+				rr->ImmedUnicast = mDNSfalse;
+				rr->v4Requester  = zerov4Addr;
+				rr->v6Requester  = zerov6Addr;
+
+				// Only sent records registered for P2P over P2P interfaces
+				if (intf && !mDNSPlatformValidRecordForInterface(rr, intf))
+					{
+					LogInfo("SendDelayedUnicastResponse: Not sending %s, on %s", ARDisplayString(m, rr), InterfaceNameForID(m, InterfaceID));
+					continue;
+					}
+
+				if (rr->NextResponse == mDNSNULL && nrp != &rr->NextResponse)	// rr->NR_AnswerTo
+					{ rr->NR_AnswerTo = (mDNSu8*)~0; *nrp = rr; nrp = &rr->NextResponse; }
+				}
+			}
+		}
+
+	AddAdditionalsToResponseList(m, ResponseRecords, &nrp, InterfaceID);
+
+	while (ResponseRecords)
+		{
+		mDNSu8 *responseptr = m->omsg.data;
+		mDNSu8 *newptr;
+		InitializeDNSMessage(&m->omsg.h, zeroID, ResponseFlags);
+		
+		// Put answers in the packet
+		while (ResponseRecords && ResponseRecords->NR_AnswerTo)
+			{
+			rr = ResponseRecords;
+			if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask)
+				rr->resrec.rrclass |= kDNSClass_UniqueRRSet;		// Temporarily set the cache flush bit so PutResourceRecord will set it
+			newptr = PutResourceRecord(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec);
+			rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet;			// Make sure to clear cache flush bit back to normal state
+			if (!newptr && m->omsg.h.numAnswers) break;	// If packet full, send it now
+			if (newptr) responseptr = newptr;
+			ResponseRecords = rr->NextResponse;
+			rr->NextResponse    = mDNSNULL;
+			rr->NR_AnswerTo     = mDNSNULL;
+			rr->NR_AdditionalTo = mDNSNULL;
+			rr->RequireGoodbye  = mDNStrue;
+			}
+		
+		// Add additionals, if there's space
+		while (ResponseRecords && !ResponseRecords->NR_AnswerTo)
+			{
+			rr = ResponseRecords;
+			if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask)
+				rr->resrec.rrclass |= kDNSClass_UniqueRRSet;		// Temporarily set the cache flush bit so PutResourceRecord will set it
+			newptr = PutResourceRecord(&m->omsg, responseptr, &m->omsg.h.numAdditionals, &rr->resrec);
+			rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet;			// Make sure to clear cache flush bit back to normal state
+			
+			if (newptr) responseptr = newptr;
+			if (newptr && m->omsg.h.numAnswers) rr->RequireGoodbye = mDNStrue;
+			else if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask) rr->ImmedAnswer = mDNSInterfaceMark;
+			ResponseRecords = rr->NextResponse;
+			rr->NextResponse    = mDNSNULL;
+			rr->NR_AnswerTo     = mDNSNULL;
+			rr->NR_AdditionalTo = mDNSNULL;
+			}
+
+		if (m->omsg.h.numAnswers)
+			mDNSSendDNSMessage(m, &m->omsg, responseptr, InterfaceID, mDNSNULL, dest, MulticastDNSPort, mDNSNULL, mDNSNULL);
+		}
+	}
+
+// CompleteDeregistration guarantees that on exit the record will have been cut from the m->ResourceRecords list
+// and the client's mStatus_MemFree callback will have been invoked
+mDNSexport void CompleteDeregistration(mDNS *const m, AuthRecord *rr)
+	{
+	LogInfo("CompleteDeregistration: called for Resource record %s", ARDisplayString(m, rr));
+	// Clearing rr->RequireGoodbye signals mDNS_Deregister_internal() that
+	// it should go ahead and immediately dispose of this registration
+	rr->resrec.RecordType = kDNSRecordTypeShared;
+	rr->RequireGoodbye    = mDNSfalse;
+	rr->WakeUp.HMAC       = zeroEthAddr;
+	if (rr->AnsweredLocalQ) { AnswerAllLocalQuestionsWithLocalAuthRecord(m, rr, mDNSfalse); rr->AnsweredLocalQ = mDNSfalse; }
+	mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal);		// Don't touch rr after this
+	}
+
+// DiscardDeregistrations is used on shutdown and sleep to discard (forcibly and immediately)
+// any deregistering records that remain in the m->ResourceRecords list.
+// DiscardDeregistrations calls mDNS_Deregister_internal which can call a user callback,
+// which may change the record list and/or question list.
+// Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
+mDNSlocal void DiscardDeregistrations(mDNS *const m)
+	{
+	if (m->CurrentRecord)
+		LogMsg("DiscardDeregistrations ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord));
+	m->CurrentRecord = m->ResourceRecords;
+	
+	while (m->CurrentRecord)
+		{
+		AuthRecord *rr = m->CurrentRecord;
+		if (!AuthRecord_uDNS(rr) && rr->resrec.RecordType == kDNSRecordTypeDeregistering)
+			CompleteDeregistration(m, rr);		// Don't touch rr after this
+		else
+			m->CurrentRecord = rr->next;
+		}
+	}
+
+mDNSlocal mStatus GetLabelDecimalValue(const mDNSu8 *const src, mDNSu8 *dst)
+	{
+	int i, val = 0;
+	if (src[0] < 1 || src[0] > 3) return(mStatus_Invalid);
+	for (i=1; i<=src[0]; i++)
+		{
+		if (src[i] < '0' || src[i] > '9') return(mStatus_Invalid);
+		val = val * 10 + src[i] - '0';
+		}
+	if (val > 255) return(mStatus_Invalid);
+	*dst = (mDNSu8)val;
+	return(mStatus_NoError);
+	}
+
+mDNSlocal mStatus GetIPv4FromName(mDNSAddr *const a, const domainname *const name)
+	{
+	int skip = CountLabels(name) - 6;
+	if (skip < 0) { LogMsg("GetIPFromName: Need six labels in IPv4 reverse mapping name %##s", name); return mStatus_Invalid; }
+	if (GetLabelDecimalValue(SkipLeadingLabels(name, skip+3)->c, &a->ip.v4.b[0]) ||
+		GetLabelDecimalValue(SkipLeadingLabels(name, skip+2)->c, &a->ip.v4.b[1]) ||
+		GetLabelDecimalValue(SkipLeadingLabels(name, skip+1)->c, &a->ip.v4.b[2]) ||
+		GetLabelDecimalValue(SkipLeadingLabels(name, skip+0)->c, &a->ip.v4.b[3])) return mStatus_Invalid;
+	a->type = mDNSAddrType_IPv4;
+	return(mStatus_NoError);
+	}
+
+#define HexVal(X) ( ((X) >= '0' && (X) <= '9') ? ((X) - '0'     ) :   \
+					((X) >= 'A' && (X) <= 'F') ? ((X) - 'A' + 10) :   \
+					((X) >= 'a' && (X) <= 'f') ? ((X) - 'a' + 10) : -1)
+
+mDNSlocal mStatus GetIPv6FromName(mDNSAddr *const a, const domainname *const name)
+	{
+	int i, h, l;
+	const domainname *n;
+
+	int skip = CountLabels(name) - 34;
+	if (skip < 0) { LogMsg("GetIPFromName: Need 34 labels in IPv6 reverse mapping name %##s", name); return mStatus_Invalid; }
+
+	n = SkipLeadingLabels(name, skip);
+	for (i=0; i<16; i++)
+		{
+		if (n->c[0] != 1) return mStatus_Invalid;
+		l = HexVal(n->c[1]);
+		n = (const domainname *)(n->c + 2);
+
+		if (n->c[0] != 1) return mStatus_Invalid;
+		h = HexVal(n->c[1]);
+		n = (const domainname *)(n->c + 2);
+
+		if (l<0 || h<0) return mStatus_Invalid;
+		a->ip.v6.b[15-i] = (mDNSu8)((h << 4) | l);
+		}
+
+	a->type = mDNSAddrType_IPv6;
+	return(mStatus_NoError);
+	}
+
+mDNSlocal mDNSs32 ReverseMapDomainType(const domainname *const name)
+	{
+	int skip = CountLabels(name) - 2;
+	if (skip >= 0)
+		{
+		const domainname *suffix = SkipLeadingLabels(name, skip);
+		if (SameDomainName(suffix, (const domainname*)"\x7" "in-addr" "\x4" "arpa")) return mDNSAddrType_IPv4;
+		if (SameDomainName(suffix, (const domainname*)"\x3" "ip6"     "\x4" "arpa")) return mDNSAddrType_IPv6;
+		}
+	return(mDNSAddrType_None);
+	}
+
+mDNSlocal void SendARP(mDNS *const m, const mDNSu8 op, const AuthRecord *const rr,
+	const mDNSv4Addr *const spa, const mDNSEthAddr *const tha, const mDNSv4Addr *const tpa, const mDNSEthAddr *const dst)
+	{
+	int i;
+	mDNSu8 *ptr = m->omsg.data;
+	NetworkInterfaceInfo *intf = FirstInterfaceForID(m, rr->resrec.InterfaceID);
+	if (!intf) { LogMsg("SendARP: No interface with InterfaceID %p found %s", rr->resrec.InterfaceID, ARDisplayString(m,rr)); return; }
+
+	// 0x00 Destination address
+	for (i=0; i<6; i++) *ptr++ = dst->b[i];
+
+	// 0x06 Source address (Note: Since we don't currently set the BIOCSHDRCMPLT option, BPF will fill in the real interface address for us)
+	for (i=0; i<6; i++) *ptr++ = intf->MAC.b[0];
+
+	// 0x0C ARP Ethertype (0x0806)
+	*ptr++ = 0x08; *ptr++ = 0x06;
+
+	// 0x0E ARP header
+	*ptr++ = 0x00; *ptr++ = 0x01;	// Hardware address space; Ethernet = 1
+	*ptr++ = 0x08; *ptr++ = 0x00;	// Protocol address space; IP = 0x0800
+	*ptr++ = 6;						// Hardware address length
+	*ptr++ = 4;						// Protocol address length
+	*ptr++ = 0x00; *ptr++ = op;		// opcode; Request = 1, Response = 2
+
+	// 0x16 Sender hardware address (our MAC address)
+	for (i=0; i<6; i++) *ptr++ = intf->MAC.b[i];
+
+	// 0x1C Sender protocol address
+	for (i=0; i<4; i++) *ptr++ = spa->b[i];
+
+	// 0x20 Target hardware address
+	for (i=0; i<6; i++) *ptr++ = tha->b[i];
+
+	// 0x26 Target protocol address
+	for (i=0; i<4; i++) *ptr++ = tpa->b[i];
+
+	// 0x2A Total ARP Packet length 42 bytes
+	mDNSPlatformSendRawPacket(m->omsg.data, ptr, rr->resrec.InterfaceID);
+	}
+
+mDNSlocal mDNSu16 CheckSum(const void *const data, mDNSs32 length, mDNSu32 sum)
+	{
+	const mDNSu16 *ptr = data;
+	while (length > 0) { length -= 2; sum += *ptr++; }
+	sum = (sum & 0xFFFF) + (sum >> 16);
+	sum = (sum & 0xFFFF) + (sum >> 16);
+	return(sum != 0xFFFF ? sum : 0);
+	}
+
+mDNSlocal mDNSu16 IPv6CheckSum(const mDNSv6Addr *const src, const mDNSv6Addr *const dst, const mDNSu8 protocol, const void *const data, const mDNSu32 length)
+	{
+	IPv6PseudoHeader ph;
+	ph.src = *src;
+	ph.dst = *dst;
+	ph.len.b[0] = length >> 24;
+	ph.len.b[1] = length >> 16;
+	ph.len.b[2] = length >> 8;
+	ph.len.b[3] = length;
+	ph.pro.b[0] = 0;
+	ph.pro.b[1] = 0;
+	ph.pro.b[2] = 0;
+	ph.pro.b[3] = protocol;
+	return CheckSum(&ph, sizeof(ph), CheckSum(data, length, 0));
+	}
+
+mDNSlocal void SendNDP(mDNS *const m, const mDNSu8 op, const mDNSu8 flags, const AuthRecord *const rr,
+	const mDNSv6Addr *const spa, const mDNSEthAddr *const tha, const mDNSv6Addr *const tpa, const mDNSEthAddr *const dst)
+	{
+	int i;
+	mDNSOpaque16 checksum;
+	mDNSu8 *ptr = m->omsg.data;
+	// Some recipient hosts seem to ignore Neighbor Solicitations if the IPv6-layer destination address is not the
+	// appropriate IPv6 solicited node multicast address, so we use that IPv6-layer destination address, even though
+	// at the Ethernet-layer we unicast the packet to the intended target, to avoid wasting network bandwidth.
+	const mDNSv6Addr mc = { { 0xFF,0x02,0x00,0x00, 0,0,0,0, 0,0,0,1, 0xFF,tpa->b[0xD],tpa->b[0xE],tpa->b[0xF] } };
+	const mDNSv6Addr *const v6dst = (op == NDP_Sol) ? &mc : tpa;
+	NetworkInterfaceInfo *intf = FirstInterfaceForID(m, rr->resrec.InterfaceID);
+	if (!intf) { LogMsg("SendNDP: No interface with InterfaceID %p found %s", rr->resrec.InterfaceID, ARDisplayString(m,rr)); return; }
+
+	// 0x00 Destination address
+	for (i=0; i<6; i++) *ptr++ = dst->b[i];
+	// Right now we only send Neighbor Solicitations to verify whether the host we're proxying for has gone to sleep yet.
+	// Since we know who we're looking for, we send it via Ethernet-layer unicast, rather than bothering every host on the
+	// link with a pointless link-layer multicast.
+	// Should we want to send traditional Neighbor Solicitations in the future, where we really don't know in advance what
+	// Ethernet-layer address we're looking for, we'll need to send to the appropriate Ethernet-layer multicast address:
+	// *ptr++ = 0x33;
+	// *ptr++ = 0x33;
+	// *ptr++ = 0xFF;
+	// *ptr++ = tpa->b[0xD];
+	// *ptr++ = tpa->b[0xE];
+	// *ptr++ = tpa->b[0xF];
+
+	// 0x06 Source address (Note: Since we don't currently set the BIOCSHDRCMPLT option, BPF will fill in the real interface address for us)
+	for (i=0; i<6; i++) *ptr++ = (tha ? *tha : intf->MAC).b[i];
+
+	// 0x0C IPv6 Ethertype (0x86DD)
+	*ptr++ = 0x86; *ptr++ = 0xDD;
+
+	// 0x0E IPv6 header
+	*ptr++ = 0x60; *ptr++ = 0x00; *ptr++ = 0x00; *ptr++ = 0x00;		// Version, Traffic Class, Flow Label
+	*ptr++ = 0x00; *ptr++ = 0x20;									// Length
+	*ptr++ = 0x3A;													// Protocol == ICMPv6
+	*ptr++ = 0xFF;													// Hop Limit
+
+	// 0x16 Sender IPv6 address
+	for (i=0; i<16; i++) *ptr++ = spa->b[i];
+
+	// 0x26 Destination IPv6 address
+	for (i=0; i<16; i++) *ptr++ = v6dst->b[i];
+
+	// 0x36 NDP header
+	*ptr++ = op;					// 0x87 == Neighbor Solicitation, 0x88 == Neighbor Advertisement
+	*ptr++ = 0x00;					// Code
+	*ptr++ = 0x00; *ptr++ = 0x00;	// Checksum placeholder (0x38, 0x39)
+	*ptr++ = flags;
+	*ptr++ = 0x00; *ptr++ = 0x00; *ptr++ = 0x00;
+
+	if (op == NDP_Sol)	// Neighbor Solicitation. The NDP "target" is the address we seek.
+		{
+		// 0x3E NDP target.
+		for (i=0; i<16; i++) *ptr++ = tpa->b[i];
+		// 0x4E Source Link-layer Address
+		// <http://www.ietf.org/rfc/rfc2461.txt>
+		// MUST NOT be included when the source IP address is the unspecified address.
+		// Otherwise, on link layers that have addresses this option MUST be included
+		// in multicast solicitations and SHOULD be included in unicast solicitations.
+		if (!mDNSIPv6AddressIsZero(*spa))
+			{
+			*ptr++ = NDP_SrcLL;	// Option Type 1 == Source Link-layer Address
+			*ptr++ = 0x01;		// Option length 1 (in units of 8 octets)
+			for (i=0; i<6; i++) *ptr++ = (tha ? *tha : intf->MAC).b[i];
+			}
+		}
+	else			// Neighbor Advertisement. The NDP "target" is the address we're giving information about.
+		{
+		// 0x3E NDP target.
+		for (i=0; i<16; i++) *ptr++ = spa->b[i];
+		// 0x4E Target Link-layer Address
+		*ptr++ = NDP_TgtLL;	// Option Type 2 == Target Link-layer Address
+		*ptr++ = 0x01;		// Option length 1 (in units of 8 octets)
+		for (i=0; i<6; i++) *ptr++ = (tha ? *tha : intf->MAC).b[i];
+		}
+
+	// 0x4E or 0x56 Total NDP Packet length 78 or 86 bytes
+	m->omsg.data[0x13] = ptr - &m->omsg.data[0x36];		// Compute actual length
+	checksum.NotAnInteger = ~IPv6CheckSum(spa, v6dst, 0x3A, &m->omsg.data[0x36], m->omsg.data[0x13]);
+	m->omsg.data[0x38] = checksum.b[0];
+	m->omsg.data[0x39] = checksum.b[1];
+
+	mDNSPlatformSendRawPacket(m->omsg.data, ptr, rr->resrec.InterfaceID);
+	}
+
+mDNSlocal void SetupOwnerOpt(const mDNS *const m, const NetworkInterfaceInfo *const intf, rdataOPT *const owner)
+	{
+	owner->u.owner.vers     = 0;
+	owner->u.owner.seq      = m->SleepSeqNum;
+	owner->u.owner.HMAC     = m->PrimaryMAC;
+	owner->u.owner.IMAC     = intf->MAC;
+	owner->u.owner.password = zeroEthAddr;
+
+	// Don't try to compute the optlen until *after* we've set up the data fields
+	// Right now the DNSOpt_Owner_Space macro does not depend on the owner->u.owner being set up correctly, but in the future it might
+	owner->opt              = kDNSOpt_Owner;
+	owner->optlen           = DNSOpt_Owner_Space(&m->PrimaryMAC, &intf->MAC) - 4;
+	}
+
+mDNSlocal void GrantUpdateCredit(AuthRecord *rr)
+	{
+	if (++rr->UpdateCredits >= kMaxUpdateCredits) rr->NextUpdateCredit = 0;
+	else rr->NextUpdateCredit = NonZeroTime(rr->NextUpdateCredit + kUpdateCreditRefreshInterval);
+	}
+
+// Note about acceleration of announcements to facilitate automatic coalescing of
+// multiple independent threads of announcements into a single synchronized thread:
+// The announcements in the packet may be at different stages of maturity;
+// One-second interval, two-second interval, four-second interval, and so on.
+// After we've put in all the announcements that are due, we then consider
+// whether there are other nearly-due announcements that are worth accelerating.
+// To be eligible for acceleration, a record MUST NOT be older (further along
+// its timeline) than the most mature record we've already put in the packet.
+// In other words, younger records can have their timelines accelerated to catch up
+// with their elder bretheren; this narrows the age gap and helps them eventually get in sync.
+// Older records cannot have their timelines accelerated; this would just widen
+// the gap between them and their younger bretheren and get them even more out of sync.
+
+// Note: SendResponses calls mDNS_Deregister_internal which can call a user callback, which may change
+// the record list and/or question list.
+// Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
+mDNSlocal void SendResponses(mDNS *const m)
+	{
+	int pktcount = 0;
+	AuthRecord *rr, *r2;
+	mDNSs32 maxExistingAnnounceInterval = 0;
+	const NetworkInterfaceInfo *intf = GetFirstActiveInterface(m->HostInterfaces);
+
+	m->NextScheduledResponse = m->timenow + 0x78000000;
+
+	if (m->SleepState == SleepState_Transferring) RetrySPSRegistrations(m);
+
+	for (rr = m->ResourceRecords; rr; rr=rr->next)
+		if (rr->ImmedUnicast)
+			{
+			mDNSAddr v4 = { mDNSAddrType_IPv4, {{{0}}} };
+			mDNSAddr v6 = { mDNSAddrType_IPv6, {{{0}}} };
+			v4.ip.v4 = rr->v4Requester;
+			v6.ip.v6 = rr->v6Requester;
+			if (!mDNSIPv4AddressIsZero(rr->v4Requester)) SendDelayedUnicastResponse(m, &v4, rr->ImmedAnswer);
+			if (!mDNSIPv6AddressIsZero(rr->v6Requester)) SendDelayedUnicastResponse(m, &v6, rr->ImmedAnswer);
+			if (rr->ImmedUnicast)
+				{
+				LogMsg("SendResponses: ERROR: rr->ImmedUnicast still set: %s", ARDisplayString(m, rr));
+				rr->ImmedUnicast = mDNSfalse;
+				}
+			}
+
+	// ***
+	// *** 1. Setup: Set the SendRNow and ImmedAnswer fields to indicate which interface(s) the records need to be sent on
+	// ***
+
+	// Run through our list of records, and decide which ones we're going to announce on all interfaces
+	for (rr = m->ResourceRecords; rr; rr=rr->next)
+		{
+		while (rr->NextUpdateCredit && m->timenow - rr->NextUpdateCredit >= 0) GrantUpdateCredit(rr);
+		if (TimeToAnnounceThisRecord(rr, m->timenow))
+			{
+			if (rr->resrec.RecordType == kDNSRecordTypeDeregistering)
+				{
+				if (!rr->WakeUp.HMAC.l[0])
+					{
+					if (rr->AnnounceCount) rr->ImmedAnswer = mDNSInterfaceMark;		// Send goodbye packet on all interfaces
+					}
+				else
+					{
+					LogSPS("SendResponses: Sending wakeup %2d for %.6a %s", rr->AnnounceCount-3, &rr->WakeUp.IMAC, ARDisplayString(m, rr));
+					SendWakeup(m, rr->resrec.InterfaceID, &rr->WakeUp.IMAC, &rr->WakeUp.password);
+					for (r2 = rr; r2; r2=r2->next)
+						if (r2->AnnounceCount && r2->resrec.InterfaceID == rr->resrec.InterfaceID && mDNSSameEthAddress(&r2->WakeUp.IMAC, &rr->WakeUp.IMAC))
+							{
+							// For now we only want to send a single Unsolicited Neighbor Advertisement restoring the address to the original
+							// owner, because these packets can cause some IPv6 stacks to falsely conclude that there's an address conflict.
+							if (r2->AddressProxy.type == mDNSAddrType_IPv6 && r2->AnnounceCount == WakeupCount)
+								{
+								LogSPS("NDP Announcement %2d Releasing traffic for H-MAC %.6a I-MAC %.6a %s",
+									r2->AnnounceCount-3, &r2->WakeUp.HMAC, &r2->WakeUp.IMAC, ARDisplayString(m,r2));
+								SendNDP(m, NDP_Adv, NDP_Override, r2, &r2->AddressProxy.ip.v6, &r2->WakeUp.IMAC, &AllHosts_v6, &AllHosts_v6_Eth);
+								}
+							r2->LastAPTime = m->timenow;
+							// After 15 wakeups without success (maybe host has left the network) send three goodbyes instead
+							if (--r2->AnnounceCount <= GoodbyeCount) r2->WakeUp.HMAC = zeroEthAddr;
+							}
+					}
+				}
+			else if (ResourceRecordIsValidAnswer(rr))
+				{
+				if (rr->AddressProxy.type)
+					{
+					rr->AnnounceCount--;
+					rr->ThisAPInterval *= 2;
+					rr->LastAPTime = m->timenow;
+					if (rr->AddressProxy.type == mDNSAddrType_IPv4)
+						{
+						LogSPS("ARP Announcement %2d Capturing traffic for H-MAC %.6a I-MAC %.6a %s",
+							rr->AnnounceCount, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m,rr));
+						SendARP(m, 1, rr, &rr->AddressProxy.ip.v4, &zeroEthAddr, &rr->AddressProxy.ip.v4, &onesEthAddr);
+						}
+					else if (rr->AddressProxy.type == mDNSAddrType_IPv6)
+						{
+						LogSPS("NDP Announcement %2d Capturing traffic for H-MAC %.6a I-MAC %.6a %s",
+							rr->AnnounceCount, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m,rr));
+						SendNDP(m, NDP_Adv, NDP_Override, rr, &rr->AddressProxy.ip.v6, mDNSNULL, &AllHosts_v6, &AllHosts_v6_Eth);
+						}
+					}
+				else
+					{
+					rr->ImmedAnswer = mDNSInterfaceMark;		// Send on all interfaces
+					if (maxExistingAnnounceInterval < rr->ThisAPInterval)
+						maxExistingAnnounceInterval = rr->ThisAPInterval;
+					if (rr->UpdateBlocked) rr->UpdateBlocked = 0;
+					}
+				}
+			}
+		}
+
+	// Any interface-specific records we're going to send are marked as being sent on all appropriate interfaces (which is just one)
+	// Eligible records that are more than half-way to their announcement time are accelerated
+	for (rr = m->ResourceRecords; rr; rr=rr->next)
+		if ((rr->resrec.InterfaceID && rr->ImmedAnswer) ||
+			(rr->ThisAPInterval <= maxExistingAnnounceInterval &&
+			TimeToAnnounceThisRecord(rr, m->timenow + rr->ThisAPInterval/2) &&
+			!rr->AddressProxy.type && 					// Don't include ARP Annoucements when considering which records to accelerate
+			ResourceRecordIsValidAnswer(rr)))
+			rr->ImmedAnswer = mDNSInterfaceMark;		// Send on all interfaces
+
+	// When sending SRV records (particularly when announcing a new service) automatically add related Address record(s) as additionals
+	// Note: Currently all address records are interface-specific, so it's safe to set ImmedAdditional to their InterfaceID,
+	// which will be non-null. If by some chance there is an address record that's not interface-specific (should never happen)
+	// then all that means is that it won't get sent -- which would not be the end of the world.
+	for (rr = m->ResourceRecords; rr; rr=rr->next)
+		{
+		if (rr->ImmedAnswer && rr->resrec.rrtype == kDNSType_SRV)
+			for (r2=m->ResourceRecords; r2; r2=r2->next)				// Scan list of resource records
+				if (RRTypeIsAddressType(r2->resrec.rrtype) &&			// For all address records (A/AAAA) ...
+					ResourceRecordIsValidAnswer(r2) &&					// ... which are valid for answer ...
+					rr->LastMCTime - r2->LastMCTime >= 0 &&				// ... which we have not sent recently ...
+					rr->resrec.rdatahash == r2->resrec.namehash &&		// ... whose name is the name of the SRV target
+					SameDomainName(&rr->resrec.rdata->u.srv.target, r2->resrec.name) &&
+					(rr->ImmedAnswer == mDNSInterfaceMark || rr->ImmedAnswer == r2->resrec.InterfaceID))
+					r2->ImmedAdditional = r2->resrec.InterfaceID;		// ... then mark this address record for sending too
+		// We also make sure we send the DeviceInfo TXT record too, if necessary
+		// We check for RecordType == kDNSRecordTypeShared because we don't want to tag the
+		// DeviceInfo TXT record onto a goodbye packet (RecordType == kDNSRecordTypeDeregistering).
+		if (rr->ImmedAnswer && rr->resrec.RecordType == kDNSRecordTypeShared && rr->resrec.rrtype == kDNSType_PTR)
+			if (ResourceRecordIsValidAnswer(&m->DeviceInfo) && SameDomainLabel(rr->resrec.rdata->u.name.c, m->DeviceInfo.resrec.name->c))
+				{
+				if (!m->DeviceInfo.ImmedAnswer) m->DeviceInfo.ImmedAnswer = rr->ImmedAnswer;
+				else                            m->DeviceInfo.ImmedAnswer = mDNSInterfaceMark;
+				}
+		}
+
+	// If there's a record which is supposed to be unique that we're going to send, then make sure that we give
+	// the whole RRSet as an atomic unit. That means that if we have any other records with the same name/type/class
+	// then we need to mark them for sending too. Otherwise, if we set the kDNSClass_UniqueRRSet bit on a
+	// record, then other RRSet members that have not been sent recently will get flushed out of client caches.
+	// -- If a record is marked to be sent on a certain interface, make sure the whole set is marked to be sent on that interface
+	// -- If any record is marked to be sent on all interfaces, make sure the whole set is marked to be sent on all interfaces
+	for (rr = m->ResourceRecords; rr; rr=rr->next)
+		if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask)
+			{
+			if (rr->ImmedAnswer)			// If we're sending this as answer, see that its whole RRSet is similarly marked
+				{
+				for (r2 = m->ResourceRecords; r2; r2=r2->next)
+					if (ResourceRecordIsValidAnswer(r2))
+						if (r2->ImmedAnswer != mDNSInterfaceMark &&
+							r2->ImmedAnswer != rr->ImmedAnswer && SameResourceRecordSignature(r2, rr))
+							r2->ImmedAnswer = !r2->ImmedAnswer ? rr->ImmedAnswer : mDNSInterfaceMark;
+				}
+			else if (rr->ImmedAdditional)	// If we're sending this as additional, see that its whole RRSet is similarly marked
+				{
+				for (r2 = m->ResourceRecords; r2; r2=r2->next)
+					if (ResourceRecordIsValidAnswer(r2))
+						if (r2->ImmedAdditional != rr->ImmedAdditional && SameResourceRecordSignature(r2, rr))
+							r2->ImmedAdditional = rr->ImmedAdditional;
+				}
+			}
+
+	// Now set SendRNow state appropriately
+	for (rr = m->ResourceRecords; rr; rr=rr->next)
+		{
+		if (rr->ImmedAnswer == mDNSInterfaceMark)		// Sending this record on all appropriate interfaces
+			{
+			rr->SendRNow = !intf ? mDNSNULL : (rr->resrec.InterfaceID) ? rr->resrec.InterfaceID : intf->InterfaceID;
+			rr->ImmedAdditional = mDNSNULL;				// No need to send as additional if sending as answer
+			rr->LastMCTime      = m->timenow;
+			rr->LastMCInterface = rr->ImmedAnswer;
+			// If we're announcing this record, and it's at least half-way to its ordained time, then consider this announcement done
+			if (TimeToAnnounceThisRecord(rr, m->timenow + rr->ThisAPInterval/2))
+				{
+				rr->AnnounceCount--;
+				if (rr->resrec.RecordType != kDNSRecordTypeDeregistering)
+					rr->ThisAPInterval *= 2;
+				rr->LastAPTime = m->timenow;
+				debugf("Announcing %##s (%s) %d", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), rr->AnnounceCount);
+				}
+			}
+		else if (rr->ImmedAnswer)						// Else, just respond to a single query on single interface:
+			{
+			rr->SendRNow        = rr->ImmedAnswer;		// Just respond on that interface
+			rr->ImmedAdditional = mDNSNULL;				// No need to send as additional too
+			rr->LastMCTime      = m->timenow;
+			rr->LastMCInterface = rr->ImmedAnswer;
+			}
+		SetNextAnnounceProbeTime(m, rr);
+		//if (rr->SendRNow) LogMsg("%-15.4a %s", &rr->v4Requester, ARDisplayString(m, rr));
+		}
+
+	// ***
+	// *** 2. Loop through interface list, sending records as appropriate
+	// ***
+
+	while (intf)
+		{
+		const int OwnerRecordSpace = (m->AnnounceOwner && intf->MAC.l[0]) ? DNSOpt_Header_Space + DNSOpt_Owner_Space(&m->PrimaryMAC, &intf->MAC) : 0;
+		int numDereg    = 0;
+		int numAnnounce = 0;
+		int numAnswer   = 0;
+		mDNSu8 *responseptr = m->omsg.data;
+		mDNSu8 *newptr;
+		InitializeDNSMessage(&m->omsg.h, zeroID, ResponseFlags);
+	
+		// First Pass. Look for:
+		// 1. Deregistering records that need to send their goodbye packet
+		// 2. Updated records that need to retract their old data
+		// 3. Answers and announcements we need to send
+		for (rr = m->ResourceRecords; rr; rr=rr->next)
+			{
+
+			// Skip this interface if the record InterfaceID is *Any and the record is not 
+			// appropriate for the interface type.
+			if ((rr->SendRNow == intf->InterfaceID) &&
+				((rr->resrec.InterfaceID == mDNSInterface_Any) && !mDNSPlatformValidRecordForInterface(rr, intf)))
+				{
+					LogInfo("SendResponses: Not sending %s, on %s", ARDisplayString(m, rr), InterfaceNameForID(m, rr->SendRNow));
+					rr->SendRNow = GetNextActiveInterfaceID(intf);
+				}
+			else if (rr->SendRNow == intf->InterfaceID)
+				{
+				RData  *OldRData    = rr->resrec.rdata;
+				mDNSu16 oldrdlength = rr->resrec.rdlength;
+				mDNSu8 active = (mDNSu8)
+					(rr->resrec.RecordType != kDNSRecordTypeDeregistering &&
+					(m->SleepState != SleepState_Sleeping || intf->SPSAddr[0].type || intf->SPSAddr[1].type || intf->SPSAddr[2].type));
+				newptr = mDNSNULL;
+				if (rr->NewRData && active)
+					{
+					// See if we should send a courtesy "goodbye" for the old data before we replace it.
+					if (ResourceRecordIsValidAnswer(rr) && rr->resrec.RecordType == kDNSRecordTypeShared && rr->RequireGoodbye)
+						{
+						newptr = PutRR_OS_TTL(responseptr, &m->omsg.h.numAnswers, &rr->resrec, 0);
+						if (newptr) { responseptr = newptr; numDereg++; rr->RequireGoodbye = mDNSfalse; }
+						else continue; // If this packet is already too full to hold the goodbye for this record, skip it for now and we'll retry later
+						}
+					SetNewRData(&rr->resrec, rr->NewRData, rr->newrdlength);
+					}
+				
+				if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask)
+					rr->resrec.rrclass |= kDNSClass_UniqueRRSet;		// Temporarily set the cache flush bit so PutResourceRecord will set it
+				newptr = PutRR_OS_TTL(responseptr, &m->omsg.h.numAnswers, &rr->resrec, active ? rr->resrec.rroriginalttl : 0);
+				rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet;			// Make sure to clear cache flush bit back to normal state
+				if (newptr)
+					{
+					responseptr = newptr;
+					rr->RequireGoodbye = active;
+					if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) numDereg++;
+					else if (rr->LastAPTime == m->timenow) numAnnounce++; else numAnswer++;
+					}
+
+				if (rr->NewRData && active)
+					SetNewRData(&rr->resrec, OldRData, oldrdlength);
+
+				// The first time through (pktcount==0), if this record is verified unique
+				// (i.e. typically A, AAAA, SRV, TXT and reverse-mapping PTR), set the flag to add an NSEC too.
+				if (!pktcount && active && (rr->resrec.RecordType & kDNSRecordTypeActiveUniqueMask) && !rr->SendNSECNow)
+					rr->SendNSECNow = mDNSInterfaceMark;
+
+				if (newptr)		// If succeeded in sending, advance to next interface
+					{
+					// If sending on all interfaces, go to next interface; else we're finished now
+					if (rr->ImmedAnswer == mDNSInterfaceMark && rr->resrec.InterfaceID == mDNSInterface_Any)
+						rr->SendRNow = GetNextActiveInterfaceID(intf);
+					else
+						rr->SendRNow = mDNSNULL;
+					}
+				}
+			}
+	
+		// Second Pass. Add additional records, if there's space.
+		newptr = responseptr;
+		for (rr = m->ResourceRecords; rr; rr=rr->next)
+			if (rr->ImmedAdditional == intf->InterfaceID)
+				if (ResourceRecordIsValidAnswer(rr))
+					{
+					// If we have at least one answer already in the packet, then plan to add additionals too
+					mDNSBool SendAdditional = (m->omsg.h.numAnswers > 0);
+					
+					// If we're not planning to send any additionals, but this record is a unique one, then
+					// make sure we haven't already sent any other members of its RRSet -- if we have, then they
+					// will have had the cache flush bit set, so now we need to finish the job and send the rest.
+					if (!SendAdditional && (rr->resrec.RecordType & kDNSRecordTypeUniqueMask))
+						{
+						const AuthRecord *a;
+						for (a = m->ResourceRecords; a; a=a->next)
+							if (a->LastMCTime      == m->timenow &&
+								a->LastMCInterface == intf->InterfaceID &&
+								SameResourceRecordSignature(a, rr)) { SendAdditional = mDNStrue; break; }
+						}
+					if (!SendAdditional)					// If we don't want to send this after all,
+						rr->ImmedAdditional = mDNSNULL;		// then cancel its ImmedAdditional field
+					else if (newptr)						// Else, try to add it if we can
+						{
+						// The first time through (pktcount==0), if this record is verified unique
+						// (i.e. typically A, AAAA, SRV, TXT and reverse-mapping PTR), set the flag to add an NSEC too.
+						if (!pktcount && (rr->resrec.RecordType & kDNSRecordTypeActiveUniqueMask) && !rr->SendNSECNow)
+							rr->SendNSECNow = mDNSInterfaceMark;
+
+						if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask)
+							rr->resrec.rrclass |= kDNSClass_UniqueRRSet;	// Temporarily set the cache flush bit so PutResourceRecord will set it
+						newptr = PutRR_OS(newptr, &m->omsg.h.numAdditionals, &rr->resrec);
+						rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet;		// Make sure to clear cache flush bit back to normal state
+						if (newptr)
+							{
+							responseptr = newptr;
+							rr->ImmedAdditional = mDNSNULL;
+							rr->RequireGoodbye = mDNStrue;
+							// If we successfully put this additional record in the packet, we record LastMCTime & LastMCInterface.
+							// This matters particularly in the case where we have more than one IPv6 (or IPv4) address, because otherwise,
+							// when we see our own multicast with the cache flush bit set, if we haven't set LastMCTime, then we'll get
+							// all concerned and re-announce our record again to make sure it doesn't get flushed from peer caches.
+							rr->LastMCTime      = m->timenow;
+							rr->LastMCInterface = intf->InterfaceID;
+							}
+						}
+					}
+
+		// Third Pass. Add NSEC records, if there's space.
+		// When we're generating an NSEC record in response to a specify query for that type
+		// (recognized by rr->SendNSECNow == intf->InterfaceID) we should really put the NSEC in the Answer Section,
+		// not Additional Section, but for now it's easier to handle both cases in this Additional Section loop here.
+		for (rr = m->ResourceRecords; rr; rr=rr->next)
+			if (rr->SendNSECNow == mDNSInterfaceMark || rr->SendNSECNow == intf->InterfaceID)
+				{
+				AuthRecord nsec;
+				mDNS_SetupResourceRecord(&nsec, mDNSNULL, mDNSInterface_Any, kDNSType_NSEC, rr->resrec.rroriginalttl, kDNSRecordTypeUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
+				nsec.resrec.rrclass |= kDNSClass_UniqueRRSet;
+				AssignDomainName(&nsec.namestorage, rr->resrec.name);
+				mDNSPlatformMemZero(nsec.rdatastorage.u.nsec.bitmap, sizeof(nsec.rdatastorage.u.nsec.bitmap));
+				for (r2 = m->ResourceRecords; r2; r2=r2->next)
+					if (ResourceRecordIsValidAnswer(r2) && SameResourceRecordNameClassInterface(r2, rr))
+						{
+						if (r2->resrec.rrtype >= kDNSQType_ANY) { LogMsg("Can't create NSEC for record %s", ARDisplayString(m, r2)); break; }
+						else nsec.rdatastorage.u.nsec.bitmap[r2->resrec.rrtype >> 3] |= 128 >> (r2->resrec.rrtype & 7);
+						}
+				newptr = responseptr;
+				if (!r2)	// If we successfully built our NSEC record, add it to the packet now
+					{
+					newptr = PutRR_OS(responseptr, &m->omsg.h.numAdditionals, &nsec.resrec);
+					if (newptr) responseptr = newptr;
+					}
+
+				// If we successfully put the NSEC record, clear the SendNSECNow flag
+				// If we consider this NSEC optional, then we unconditionally clear the SendNSECNow flag, even if we fail to put this additional record
+				if (newptr || rr->SendNSECNow == mDNSInterfaceMark)
+					{
+					rr->SendNSECNow = mDNSNULL;
+					// Run through remainder of list clearing SendNSECNow flag for all other records which would generate the same NSEC
+					for (r2 = rr->next; r2; r2=r2->next)
+						if (SameResourceRecordNameClassInterface(r2, rr))
+							if (r2->SendNSECNow == mDNSInterfaceMark || r2->SendNSECNow == intf->InterfaceID)
+								r2->SendNSECNow = mDNSNULL;
+					}
+				}
+
+		if (m->omsg.h.numAnswers || m->omsg.h.numAdditionals)
+			{
+			// If we have data to send, add OWNER option if necessary, then send packet
+
+			if (OwnerRecordSpace)
+				{
+				AuthRecord opt;
+				mDNS_SetupResourceRecord(&opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
+				opt.resrec.rrclass    = NormalMaxDNSMessageData;
+				opt.resrec.rdlength   = sizeof(rdataOPT);	// One option in this OPT record
+				opt.resrec.rdestimate = sizeof(rdataOPT);
+				SetupOwnerOpt(m, intf, &opt.resrec.rdata->u.opt[0]);
+				newptr = PutResourceRecord(&m->omsg, responseptr, &m->omsg.h.numAdditionals, &opt.resrec);
+				if (newptr) { responseptr = newptr; LogSPS("SendResponses put   %s", ARDisplayString(m, &opt)); }
+				else if (m->omsg.h.numAnswers + m->omsg.h.numAuthorities + m->omsg.h.numAdditionals == 1)
+					LogSPS("SendResponses: No space in packet for Owner OPT record (%d/%d/%d/%d) %s",
+						m->omsg.h.numQuestions, m->omsg.h.numAnswers, m->omsg.h.numAuthorities, m->omsg.h.numAdditionals, ARDisplayString(m, &opt));
+				else
+					LogMsg("SendResponses: How did we fail to have space for Owner OPT record (%d/%d/%d/%d) %s",
+						m->omsg.h.numQuestions, m->omsg.h.numAnswers, m->omsg.h.numAuthorities, m->omsg.h.numAdditionals, ARDisplayString(m, &opt));
+				}
+
+			debugf("SendResponses: Sending %d Deregistration%s, %d Announcement%s, %d Answer%s, %d Additional%s on %p",
+				numDereg,                 numDereg                 == 1 ? "" : "s",
+				numAnnounce,              numAnnounce              == 1 ? "" : "s",
+				numAnswer,                numAnswer                == 1 ? "" : "s",
+				m->omsg.h.numAdditionals, m->omsg.h.numAdditionals == 1 ? "" : "s", intf->InterfaceID);
+
+			if (intf->IPv4Available) mDNSSendDNSMessage(m, &m->omsg, responseptr, intf->InterfaceID, mDNSNULL, &AllDNSLinkGroup_v4, MulticastDNSPort, mDNSNULL, mDNSNULL);
+			if (intf->IPv6Available) mDNSSendDNSMessage(m, &m->omsg, responseptr, intf->InterfaceID, mDNSNULL, &AllDNSLinkGroup_v6, MulticastDNSPort, mDNSNULL, mDNSNULL);
+			if (!m->SuppressSending) m->SuppressSending = NonZeroTime(m->timenow + (mDNSPlatformOneSecond+9)/10);
+			if (++pktcount >= 1000) { LogMsg("SendResponses exceeded loop limit %d: giving up", pktcount); break; }
+			// There might be more things to send on this interface, so go around one more time and try again.
+			}
+		else	// Nothing more to send on this interface; go to next
+			{
+			const NetworkInterfaceInfo *next = GetFirstActiveInterface(intf->next);
+			#if MDNS_DEBUGMSGS && 0
+			const char *const msg = next ? "SendResponses: Nothing more on %p; moving to %p" : "SendResponses: Nothing more on %p";
+			debugf(msg, intf, next);
+			#endif
+			intf = next;
+			pktcount = 0;		// When we move to a new interface, reset packet count back to zero -- NSEC generation logic uses it
+			}
+		}
+
+	// ***
+	// *** 3. Cleanup: Now that everything is sent, call client callback functions, and reset state variables
+	// ***
+
+	if (m->CurrentRecord)
+		LogMsg("SendResponses ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord));
+	m->CurrentRecord = m->ResourceRecords;
+	while (m->CurrentRecord)
+		{
+		rr = m->CurrentRecord;
+		m->CurrentRecord = rr->next;
+
+		if (rr->SendRNow)
+			{
+			if (rr->ARType != AuthRecordLocalOnly && rr->ARType != AuthRecordP2P)
+				LogMsg("SendResponses: No active interface %p to send: %p %02X %s", rr->SendRNow, rr->resrec.InterfaceID, rr->resrec.RecordType, ARDisplayString(m, rr));
+			rr->SendRNow = mDNSNULL;
+			}
+
+		if (rr->ImmedAnswer || rr->resrec.RecordType == kDNSRecordTypeDeregistering)
+			{
+			if (rr->NewRData) CompleteRDataUpdate(m, rr);	// Update our rdata, clear the NewRData pointer, and return memory to the client
+	
+			if (rr->resrec.RecordType == kDNSRecordTypeDeregistering && rr->AnnounceCount == 0)
+				{
+				// For Unicast, when we get the response from the server, we will call CompleteDeregistration
+				if (!AuthRecord_uDNS(rr)) CompleteDeregistration(m, rr);		// Don't touch rr after this
+				}
+			else
+				{
+				rr->ImmedAnswer  = mDNSNULL;
+				rr->ImmedUnicast = mDNSfalse;
+				rr->v4Requester  = zerov4Addr;
+				rr->v6Requester  = zerov6Addr;
+				}
+			}
+		}
+	verbosedebugf("SendResponses: Next in %ld ticks", m->NextScheduledResponse - m->timenow);
+	}
+
+// Calling CheckCacheExpiration() is an expensive operation because it has to look at the entire cache,
+// so we want to be lazy about how frequently we do it.
+// 1. If a cache record is currently referenced by *no* active questions,
+//    then we don't mind expiring it up to a minute late (who will know?)
+// 2. Else, if a cache record is due for some of its final expiration queries,
+//    we'll allow them to be late by up to 2% of the TTL
+// 3. Else, if a cache record has completed all its final expiration queries without success,
+//    and is expiring, and had an original TTL more than ten seconds, we'll allow it to be one second late
+// 4. Else, it is expiring and had an original TTL of ten seconds or less (includes explicit goodbye packets),
+//    so allow at most 1/10 second lateness
+// 5. For records with rroriginalttl set to zero, that means we really want to delete them immediately
+//    (we have a new record with DelayDelivery set, waiting for the old record to go away before we can notify clients).
+#define CacheCheckGracePeriod(RR) (                                                   \
+	((RR)->CRActiveQuestion == mDNSNULL            ) ? (60 * mDNSPlatformOneSecond) : \
+	((RR)->UnansweredQueries < MaxUnansweredQueries) ? (TicksTTL(rr)/50)            : \
+	((RR)->resrec.rroriginalttl > 10               ) ? (mDNSPlatformOneSecond)      : \
+	((RR)->resrec.rroriginalttl > 0                ) ? (mDNSPlatformOneSecond/10)   : 0)
+
+#define NextCacheCheckEvent(RR) ((RR)->NextRequiredQuery + CacheCheckGracePeriod(RR))
+
+mDNSexport void ScheduleNextCacheCheckTime(mDNS *const m, const mDNSu32 slot, const mDNSs32 event)
+	{
+	if (m->rrcache_nextcheck[slot] - event > 0)
+		m->rrcache_nextcheck[slot] = event;
+	if (m->NextCacheCheck          - event > 0)
+		m->NextCacheCheck          = event;
+	}
+
+// Note: MUST call SetNextCacheCheckTimeForRecord any time we change:
+// rr->TimeRcvd
+// rr->resrec.rroriginalttl
+// rr->UnansweredQueries
+// rr->CRActiveQuestion
+mDNSlocal void SetNextCacheCheckTimeForRecord(mDNS *const m, CacheRecord *const rr)
+	{
+	rr->NextRequiredQuery = RRExpireTime(rr);
+
+	// If we have an active question, then see if we want to schedule a refresher query for this record.
+	// Usually we expect to do four queries, at 80-82%, 85-87%, 90-92% and then 95-97% of the TTL.
+	if (rr->CRActiveQuestion && rr->UnansweredQueries < MaxUnansweredQueries)
+		{
+		rr->NextRequiredQuery -= TicksTTL(rr)/20 * (MaxUnansweredQueries - rr->UnansweredQueries);
+		rr->NextRequiredQuery += mDNSRandom((mDNSu32)TicksTTL(rr)/50);
+		verbosedebugf("SetNextCacheCheckTimeForRecord: NextRequiredQuery in %ld sec CacheCheckGracePeriod %d ticks for %s",
+			(rr->NextRequiredQuery - m->timenow) / mDNSPlatformOneSecond, CacheCheckGracePeriod(rr), CRDisplayString(m,rr));
+		}
+
+	ScheduleNextCacheCheckTime(m, HashSlot(rr->resrec.name), NextCacheCheckEvent(rr));
+	}
+
+#define kMinimumReconfirmTime                     ((mDNSu32)mDNSPlatformOneSecond *  5)
+#define kDefaultReconfirmTimeForWake              ((mDNSu32)mDNSPlatformOneSecond *  5)
+#define kDefaultReconfirmTimeForNoAnswer          ((mDNSu32)mDNSPlatformOneSecond *  5)
+#define kDefaultReconfirmTimeForFlappingInterface ((mDNSu32)mDNSPlatformOneSecond * 30)
+
+mDNSlocal mStatus mDNS_Reconfirm_internal(mDNS *const m, CacheRecord *const rr, mDNSu32 interval)
+	{
+	if (interval < kMinimumReconfirmTime)
+		interval = kMinimumReconfirmTime;
+	if (interval > 0x10000000)	// Make sure interval doesn't overflow when we multiply by four below
+		interval = 0x10000000;
+
+	// If the expected expiration time for this record is more than interval+33%, then accelerate its expiration
+	if (RRExpireTime(rr) - m->timenow > (mDNSs32)((interval * 4) / 3))
+		{
+		// Add a 33% random amount to the interval, to avoid synchronization between multiple hosts
+		// For all the reconfirmations in a given batch, we want to use the same random value
+		// so that the reconfirmation questions can be grouped into a single query packet
+		if (!m->RandomReconfirmDelay) m->RandomReconfirmDelay = 1 + mDNSRandom(0x3FFFFFFF);
+		interval += m->RandomReconfirmDelay % ((interval/3) + 1);
+		rr->TimeRcvd          = m->timenow - (mDNSs32)interval * 3;
+		rr->resrec.rroriginalttl     = (interval * 4 + mDNSPlatformOneSecond - 1) / mDNSPlatformOneSecond;
+		SetNextCacheCheckTimeForRecord(m, rr);
+		}
+	debugf("mDNS_Reconfirm_internal:%6ld ticks to go for %s %p",
+		RRExpireTime(rr) - m->timenow, CRDisplayString(m, rr), rr->CRActiveQuestion);
+	return(mStatus_NoError);
+	}
+
+#define MaxQuestionInterval         (3600 * mDNSPlatformOneSecond)
+
+// BuildQuestion puts a question into a DNS Query packet and if successful, updates the value of queryptr.
+// It also appends to the list of known answer records that need to be included,
+// and updates the forcast for the size of the known answer section.
+mDNSlocal mDNSBool BuildQuestion(mDNS *const m, DNSMessage *query, mDNSu8 **queryptr, DNSQuestion *q,
+	CacheRecord ***kalistptrptr, mDNSu32 *answerforecast)
+	{
+	mDNSBool ucast = (q->LargeAnswers || q->RequestUnicast) && m->CanReceiveUnicastOn5353;
+	mDNSu16 ucbit = (mDNSu16)(ucast ? kDNSQClass_UnicastResponse : 0);
+	const mDNSu8 *const limit = query->data + NormalMaxDNSMessageData;
+	mDNSu8 *newptr = putQuestion(query, *queryptr, limit - *answerforecast, &q->qname, q->qtype, (mDNSu16)(q->qclass | ucbit));
+	if (!newptr)
+		{
+		debugf("BuildQuestion: No more space in this packet for question %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+		return(mDNSfalse);
+		}
+	else
+		{
+		mDNSu32 forecast = *answerforecast;
+		const mDNSu32 slot = HashSlot(&q->qname);
+		const CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname);
+		CacheRecord *rr;
+		CacheRecord **ka = *kalistptrptr;	// Make a working copy of the pointer we're going to update
+
+		for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)				// If we have a resource record in our cache,
+			if (rr->resrec.InterfaceID == q->SendQNow &&					// received on this interface
+				!(rr->resrec.RecordType & kDNSRecordTypeUniqueMask) &&		// which is a shared (i.e. not unique) record type
+				rr->NextInKAList == mDNSNULL && ka != &rr->NextInKAList &&	// which is not already in the known answer list
+				rr->resrec.rdlength <= SmallRecordLimit &&					// which is small enough to sensibly fit in the packet
+				SameNameRecordAnswersQuestion(&rr->resrec, q) &&			// which answers our question
+				rr->TimeRcvd + TicksTTL(rr)/2 - m->timenow >				// and its half-way-to-expiry time is at least 1 second away
+												mDNSPlatformOneSecond)		// (also ensures we never include goodbye records with TTL=1)
+				{
+				// We don't want to include unique records in the Known Answer section. The Known Answer section
+				// is intended to suppress floods of shared-record replies from many other devices on the network.
+				// That concept really does not apply to unique records, and indeed if we do send a query for
+				// which we have a unique record already in our cache, then including that unique record as a
+				// Known Answer, so as to suppress the only answer we were expecting to get, makes little sense.
+
+				*ka = rr;	// Link this record into our known answer chain
+				ka = &rr->NextInKAList;
+				// We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n)
+				forecast += 12 + rr->resrec.rdestimate;
+				// If we're trying to put more than one question in this packet, and it doesn't fit
+				// then undo that last question and try again next time
+				if (query->h.numQuestions > 1 && newptr + forecast >= limit)
+					{
+					debugf("BuildQuestion: Retracting question %##s (%s) new forecast total %d",
+						q->qname.c, DNSTypeName(q->qtype), newptr + forecast - query->data);
+					query->h.numQuestions--;
+					ka = *kalistptrptr;		// Go back to where we started and retract these answer records
+					while (*ka) { CacheRecord *c = *ka; *ka = mDNSNULL; ka = &c->NextInKAList; }
+					return(mDNSfalse);		// Return false, so we'll try again in the next packet
+					}
+				}
+
+		// Success! Update our state pointers, increment UnansweredQueries as appropriate, and return
+		*queryptr        = newptr;				// Update the packet pointer
+		*answerforecast  = forecast;			// Update the forecast
+		*kalistptrptr    = ka;					// Update the known answer list pointer
+		if (ucast) q->ExpectUnicastResp = NonZeroTime(m->timenow);
+
+		for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)				// For every resource record in our cache,
+			if (rr->resrec.InterfaceID == q->SendQNow &&					// received on this interface
+				rr->NextInKAList == mDNSNULL && ka != &rr->NextInKAList &&	// which is not in the known answer list
+				SameNameRecordAnswersQuestion(&rr->resrec, q))				// which answers our question
+					{
+					rr->UnansweredQueries++;								// indicate that we're expecting a response
+					rr->LastUnansweredTime = m->timenow;
+					SetNextCacheCheckTimeForRecord(m, rr);
+					}
+
+		return(mDNStrue);
+		}
+	}
+
+// When we have a query looking for a specified name, but there appear to be no answers with
+// that name, ReconfirmAntecedents() is called with depth=0 to start the reconfirmation process
+// for any records in our cache that reference the given name (e.g. PTR and SRV records).
+// For any such cache record we find, we also recursively call ReconfirmAntecedents() for *its* name.
+// We increment depth each time we recurse, to guard against possible infinite loops, with a limit of 5.
+// A typical reconfirmation scenario might go like this:
+// Depth 0: Name "myhost.local" has no address records
+// Depth 1: SRV "My Service._example._tcp.local." refers to "myhost.local"; may be stale
+// Depth 2: PTR "_example._tcp.local." refers to "My Service"; may be stale
+// Depth 3: PTR "_services._dns-sd._udp.local." refers to "_example._tcp.local."; may be stale
+// Currently depths 4 and 5 are not expected to occur; if we did get to depth 5 we'd reconfim any records we
+// found referring to the given name, but not recursively descend any further reconfirm *their* antecedents.
+mDNSlocal void ReconfirmAntecedents(mDNS *const m, const domainname *const name, const mDNSu32 namehash, const int depth)
+	{
+	mDNSu32 slot;
+	CacheGroup *cg;
+	CacheRecord *cr;
+	debugf("ReconfirmAntecedents (depth=%d) for %##s", depth, name->c);
+	FORALL_CACHERECORDS(slot, cg, cr)
+		{
+		domainname *crtarget = GetRRDomainNameTarget(&cr->resrec);
+		if (crtarget && cr->resrec.rdatahash == namehash && SameDomainName(crtarget, name))
+			{
+			LogInfo("ReconfirmAntecedents: Reconfirming (depth=%d) %s", depth, CRDisplayString(m, cr));
+			mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForNoAnswer);
+			if (depth < 5) ReconfirmAntecedents(m, cr->resrec.name, cr->resrec.namehash, depth+1);
+			}
+		}
+	}
+
+// If we get no answer for a AAAA query, then before doing an automatic implicit ReconfirmAntecedents
+// we check if we have an address record for the same name. If we do have an IPv4 address for a given
+// name but not an IPv6 address, that's okay (it just means the device doesn't do IPv6) so the failure
+// to get a AAAA response is not grounds to doubt the PTR/SRV chain that lead us to that name.
+mDNSlocal const CacheRecord *CacheHasAddressTypeForName(mDNS *const m, const domainname *const name, const mDNSu32 namehash)
+	{
+	CacheGroup *const cg = CacheGroupForName(m, HashSlot(name), namehash, name);
+	const CacheRecord *cr = cg ? cg->members : mDNSNULL;
+	while (cr && !RRTypeIsAddressType(cr->resrec.rrtype)) cr=cr->next;
+	return(cr);
+	}
+
+mDNSlocal const CacheRecord *FindSPSInCache1(mDNS *const m, const DNSQuestion *const q, const CacheRecord *const c0, const CacheRecord *const c1)
+	{
+	CacheGroup *const cg = CacheGroupForName(m, HashSlot(&q->qname), q->qnamehash, &q->qname);
+	const CacheRecord *cr, *bestcr = mDNSNULL;
+	mDNSu32 bestmetric = 1000000;
+	for (cr = cg ? cg->members : mDNSNULL; cr; cr=cr->next)
+		if (cr->resrec.rrtype == kDNSType_PTR && cr->resrec.rdlength >= 6)						// If record is PTR type, with long enough name,
+			if (cr != c0 && cr != c1)															// that's not one we've seen before,
+				if (SameNameRecordAnswersQuestion(&cr->resrec, q))								// and answers our browse query,
+					if (!IdenticalSameNameRecord(&cr->resrec, &m->SPSRecords.RR_PTR.resrec))	// and is not our own advertised service...
+						{
+						mDNSu32 metric = SPSMetric(cr->resrec.rdata->u.name.c);
+						if (bestmetric > metric) { bestmetric = metric; bestcr = cr; }
+						}
+	return(bestcr);
+	}
+
+// Finds the three best Sleep Proxies we currently have in our cache
+mDNSexport void FindSPSInCache(mDNS *const m, const DNSQuestion *const q, const CacheRecord *sps[3])
+	{
+	sps[0] =                      FindSPSInCache1(m, q, mDNSNULL, mDNSNULL);
+	sps[1] = !sps[0] ? mDNSNULL : FindSPSInCache1(m, q, sps[0],   mDNSNULL);
+	sps[2] = !sps[1] ? mDNSNULL : FindSPSInCache1(m, q, sps[0],   sps[1]);
+	}
+
+// Only DupSuppressInfos newer than the specified 'time' are allowed to remain active
+mDNSlocal void ExpireDupSuppressInfo(DupSuppressInfo ds[DupSuppressInfoSize], mDNSs32 time)
+	{
+	int i;
+	for (i=0; i<DupSuppressInfoSize; i++) if (ds[i].Time - time < 0) ds[i].InterfaceID = mDNSNULL;
+	}
+
+mDNSlocal void ExpireDupSuppressInfoOnInterface(DupSuppressInfo ds[DupSuppressInfoSize], mDNSs32 time, mDNSInterfaceID InterfaceID)
+	{
+	int i;
+	for (i=0; i<DupSuppressInfoSize; i++) if (ds[i].InterfaceID == InterfaceID && ds[i].Time - time < 0) ds[i].InterfaceID = mDNSNULL;
+	}
+
+mDNSlocal mDNSBool SuppressOnThisInterface(const DupSuppressInfo ds[DupSuppressInfoSize], const NetworkInterfaceInfo * const intf)
+	{
+	int i;
+	mDNSBool v4 = !intf->IPv4Available;		// If this interface doesn't do v4, we don't need to find a v4 duplicate of this query
+	mDNSBool v6 = !intf->IPv6Available;		// If this interface doesn't do v6, we don't need to find a v6 duplicate of this query
+	for (i=0; i<DupSuppressInfoSize; i++)
+		if (ds[i].InterfaceID == intf->InterfaceID)
+			{
+			if      (ds[i].Type == mDNSAddrType_IPv4) v4 = mDNStrue;
+			else if (ds[i].Type == mDNSAddrType_IPv6) v6 = mDNStrue;
+			if (v4 && v6) return(mDNStrue);
+			}
+	return(mDNSfalse);
+	}
+
+mDNSlocal int RecordDupSuppressInfo(DupSuppressInfo ds[DupSuppressInfoSize], mDNSs32 Time, mDNSInterfaceID InterfaceID, mDNSs32 Type)
+	{
+	int i, j;
+
+	// See if we have this one in our list somewhere already
+	for (i=0; i<DupSuppressInfoSize; i++) if (ds[i].InterfaceID == InterfaceID && ds[i].Type == Type) break;
+
+	// If not, find a slot we can re-use
+	if (i >= DupSuppressInfoSize)
+		{
+		i = 0;
+		for (j=1; j<DupSuppressInfoSize && ds[i].InterfaceID; j++)
+			if (!ds[j].InterfaceID || ds[j].Time - ds[i].Time < 0)
+				i = j;
+		}
+	
+	// Record the info about this query we saw
+	ds[i].Time        = Time;
+	ds[i].InterfaceID = InterfaceID;
+	ds[i].Type        = Type;
+	
+	return(i);
+	}
+
+mDNSlocal void mDNSSendWakeOnResolve(mDNS *const m, DNSQuestion *q)
+	{
+	int len, i, cnt;
+	mDNSInterfaceID InterfaceID = q->InterfaceID;
+	domainname *d = &q->qname;
+
+	// We can't send magic packets without knowing which interface to send it on.
+	if (InterfaceID == mDNSInterface_Any || InterfaceID == mDNSInterface_LocalOnly || InterfaceID == mDNSInterface_P2P)
+		{
+		LogMsg("mDNSSendWakeOnResolve: ERROR!! Invalid InterfaceID %p for question %##s", InterfaceID, q->qname.c);
+		return;
+		}
+
+	// Split MAC@IPAddress and pass them separately
+	len = d->c[0];
+	i = 1;
+	cnt = 0;
+	for (i = 1; i < len; i++)
+		{
+		if (d->c[i] == '@')
+			{
+			char EthAddr[18];	// ethernet adddress : 12 bytes + 5 ":" + 1 NULL byte
+			char IPAddr[47];    // Max IP address len: 46 bytes (IPv6) + 1 NULL byte
+			if (cnt != 5)
+				{
+				LogMsg("mDNSSendWakeOnResolve: ERROR!! Malformed Ethernet address %##s, cnt %d", q->qname.c, cnt);
+				return;
+				}
+			if ((i - 1) > (int) (sizeof(EthAddr) - 1))
+				{
+				LogMsg("mDNSSendWakeOnResolve: ERROR!! Malformed Ethernet address %##s, length %d", q->qname.c, i - 1);
+				return;
+				}
+			if ((len - i) > (int)(sizeof(IPAddr) - 1))
+				{
+				LogMsg("mDNSSendWakeOnResolve: ERROR!! Malformed IP address %##s, length %d", q->qname.c, len - i);
+				return;
+				}
+			mDNSPlatformMemCopy(EthAddr, &d->c[1], i - 1);
+			EthAddr[i - 1] = 0;
+			mDNSPlatformMemCopy(IPAddr, &d->c[i + 1], len - i);
+			IPAddr[len - i] = 0;
+			mDNSPlatformSendWakeupPacket(m, InterfaceID, EthAddr, IPAddr, InitialWakeOnResolveCount - q->WakeOnResolveCount);
+			return;
+			}
+		else if (d->c[i] == ':')
+			cnt++;
+		}
+	LogMsg("mDNSSendWakeOnResolve: ERROR!! Malformed WakeOnResolve name %##s", q->qname.c);
+	}
+		
+
+mDNSlocal mDNSBool AccelerateThisQuery(mDNS *const m, DNSQuestion *q)
+	{
+	// If more than 90% of the way to the query time, we should unconditionally accelerate it
+	if (TimeToSendThisQuestion(q, m->timenow + q->ThisQInterval/10))
+		return(mDNStrue);
+
+	// If half-way to next scheduled query time, only accelerate if it will add less than 512 bytes to the packet
+	if (TimeToSendThisQuestion(q, m->timenow + q->ThisQInterval/2))
+		{
+		// We forecast: qname (n) type (2) class (2)
+		mDNSu32 forecast = (mDNSu32)DomainNameLength(&q->qname) + 4;
+		const mDNSu32 slot = HashSlot(&q->qname);
+		const CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname);
+		const CacheRecord *rr;
+		for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)				// If we have a resource record in our cache,
+			if (rr->resrec.rdlength <= SmallRecordLimit &&					// which is small enough to sensibly fit in the packet
+				SameNameRecordAnswersQuestion(&rr->resrec, q) &&			// which answers our question
+				rr->TimeRcvd + TicksTTL(rr)/2 - m->timenow >= 0 &&			// and it is less than half-way to expiry
+				rr->NextRequiredQuery - (m->timenow + q->ThisQInterval) > 0)// and we'll ask at least once again before NextRequiredQuery
+				{
+				// We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n)
+				forecast += 12 + rr->resrec.rdestimate;
+				if (forecast >= 512) return(mDNSfalse);	// If this would add 512 bytes or more to the packet, don't accelerate
+				}
+		return(mDNStrue);
+		}
+
+	return(mDNSfalse);
+	}
+
+// How Standard Queries are generated:
+// 1. The Question Section contains the question
+// 2. The Additional Section contains answers we already know, to suppress duplicate responses
+
+// How Probe Queries are generated:
+// 1. The Question Section contains queries for the name we intend to use, with QType=ANY because
+// if some other host is already using *any* records with this name, we want to know about it.
+// 2. The Authority Section contains the proposed values we intend to use for one or more
+// of our records with that name (analogous to the Update section of DNS Update packets)
+// because if some other host is probing at the same time, we each want to know what the other is
+// planning, in order to apply the tie-breaking rule to see who gets to use the name and who doesn't.
+
+mDNSlocal void SendQueries(mDNS *const m)
+	{
+	mDNSu32 slot;
+	CacheGroup *cg;
+	CacheRecord *cr;
+	AuthRecord *ar;
+	int pktcount = 0;
+	DNSQuestion *q;
+	// For explanation of maxExistingQuestionInterval logic, see comments for maxExistingAnnounceInterval
+	mDNSs32 maxExistingQuestionInterval = 0;
+	const NetworkInterfaceInfo *intf = GetFirstActiveInterface(m->HostInterfaces);
+	CacheRecord *KnownAnswerList = mDNSNULL;
+
+	// 1. If time for a query, work out what we need to do
+
+	// We're expecting to send a query anyway, so see if any expiring cache records are close enough
+	// to their NextRequiredQuery to be worth batching them together with this one
+	FORALL_CACHERECORDS(slot, cg, cr)
+		if (cr->CRActiveQuestion && cr->UnansweredQueries < MaxUnansweredQueries)
+			if (m->timenow + TicksTTL(cr)/50 - cr->NextRequiredQuery >= 0)
+				{
+				debugf("Sending %d%% cache expiration query for %s", 80 + 5 * cr->UnansweredQueries, CRDisplayString(m, cr));
+				q = cr->CRActiveQuestion;
+				ExpireDupSuppressInfoOnInterface(q->DupSuppress, m->timenow - TicksTTL(cr)/20, cr->resrec.InterfaceID);
+				// For uDNS queries (TargetQID non-zero) we adjust LastQTime,
+				// and bump UnansweredQueries so that we don't spin trying to send the same cache expiration query repeatedly
+				if      (q->Target.type)                        q->SendQNow = mDNSInterfaceMark;	// If targeted query, mark it
+				else if (!mDNSOpaque16IsZero(q->TargetQID))     { q->LastQTime = m->timenow - q->ThisQInterval; cr->UnansweredQueries++; }
+				else if (q->SendQNow == mDNSNULL)               q->SendQNow = cr->resrec.InterfaceID;
+				else if (q->SendQNow != cr->resrec.InterfaceID) q->SendQNow = mDNSInterfaceMark;
+				}
+
+	// Scan our list of questions to see which:
+	//     *WideArea*  queries need to be sent
+	//     *unicast*   queries need to be sent
+	//     *multicast* queries we're definitely going to send
+	if (m->CurrentQuestion)
+		LogMsg("SendQueries ERROR m->CurrentQuestion already set: %##s (%s)", m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype));
+	m->CurrentQuestion = m->Questions;
+	while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions)
+		{
+		q = m->CurrentQuestion;
+		if (q->Target.type && (q->SendQNow || TimeToSendThisQuestion(q, m->timenow)))
+			{
+			mDNSu8       *qptr        = m->omsg.data;
+			const mDNSu8 *const limit = m->omsg.data + sizeof(m->omsg.data);
+
+			// If we fail to get a new on-demand socket (should only happen cases of the most extreme resource exhaustion), we'll try again next time
+			if (!q->LocalSocket) q->LocalSocket = mDNSPlatformUDPSocket(m, zeroIPPort);
+			if (q->LocalSocket)
+				{
+				InitializeDNSMessage(&m->omsg.h, q->TargetQID, QueryFlags);
+				qptr = putQuestion(&m->omsg, qptr, limit, &q->qname, q->qtype, q->qclass);
+				mDNSSendDNSMessage(m, &m->omsg, qptr, mDNSInterface_Any, q->LocalSocket, &q->Target, q->TargetPort, mDNSNULL, mDNSNULL);
+				q->ThisQInterval    *= QuestionIntervalStep;
+				}
+			if (q->ThisQInterval > MaxQuestionInterval)
+				q->ThisQInterval = MaxQuestionInterval;
+			q->LastQTime         = m->timenow;
+			q->LastQTxTime       = m->timenow;
+			q->RecentAnswerPkts  = 0;
+			q->SendQNow          = mDNSNULL;
+			q->ExpectUnicastResp = NonZeroTime(m->timenow);
+			}
+		else if (mDNSOpaque16IsZero(q->TargetQID) && !q->Target.type && TimeToSendThisQuestion(q, m->timenow))
+			{
+			//LogInfo("Time to send %##s (%s) %d", q->qname.c, DNSTypeName(q->qtype), m->timenow - NextQSendTime(q));
+			q->SendQNow = mDNSInterfaceMark;		// Mark this question for sending on all interfaces
+			if (maxExistingQuestionInterval < q->ThisQInterval)
+				maxExistingQuestionInterval = q->ThisQInterval;
+			}
+		// If m->CurrentQuestion wasn't modified out from under us, advance it now
+		// We can't do this at the start of the loop because uDNS_CheckCurrentQuestion() depends on having
+		// m->CurrentQuestion point to the right question
+		if (q == m->CurrentQuestion) m->CurrentQuestion = m->CurrentQuestion->next;
+		}
+	while (m->CurrentQuestion)
+		{
+		LogInfo("SendQueries question loop 1: Skipping NewQuestion %##s (%s)", m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype));
+		m->CurrentQuestion = m->CurrentQuestion->next;
+		}
+	m->CurrentQuestion = mDNSNULL;
+
+	// Scan our list of questions
+	// (a) to see if there are any more that are worth accelerating, and
+	// (b) to update the state variables for *all* the questions we're going to send
+	// Note: Don't set NextScheduledQuery until here, because uDNS_CheckCurrentQuestion in the loop above can add new questions to the list,
+	// which causes NextScheduledQuery to get (incorrectly) set to m->timenow. Setting it here is the right place, because the very
+	// next thing we do is scan the list and call SetNextQueryTime() for every question we find, so we know we end up with the right value.
+	m->NextScheduledQuery = m->timenow + 0x78000000;
+	for (q = m->Questions; q && q != m->NewQuestions; q=q->next)
+		{
+		if (mDNSOpaque16IsZero(q->TargetQID) && (q->SendQNow ||
+			(!q->Target.type && ActiveQuestion(q) && q->ThisQInterval <= maxExistingQuestionInterval && AccelerateThisQuery(m,q))))
+			{
+			// If at least halfway to next query time, advance to next interval
+			// If less than halfway to next query time, then
+			// treat this as logically a repeat of the last transmission, without advancing the interval
+			if (m->timenow - (q->LastQTime + (q->ThisQInterval/2)) >= 0)
+				{
+				//LogInfo("Accelerating %##s (%s) %d", q->qname.c, DNSTypeName(q->qtype), m->timenow - NextQSendTime(q));
+				q->SendQNow = mDNSInterfaceMark;	// Mark this question for sending on all interfaces
+				debugf("SendQueries: %##s (%s) next interval %d seconds RequestUnicast = %d",
+					q->qname.c, DNSTypeName(q->qtype), q->ThisQInterval / InitialQuestionInterval, q->RequestUnicast);
+				q->ThisQInterval *= QuestionIntervalStep;
+				if (q->ThisQInterval > MaxQuestionInterval)
+					q->ThisQInterval = MaxQuestionInterval;
+				else if (q->CurrentAnswers == 0 && q->ThisQInterval == InitialQuestionInterval * QuestionIntervalStep3 && !q->RequestUnicast &&
+						!(RRTypeIsAddressType(q->qtype) && CacheHasAddressTypeForName(m, &q->qname, q->qnamehash)))
+					{
+					// Generally don't need to log this.
+					// It's not especially noteworthy if a query finds no results -- this usually happens for domain
+					// enumeration queries in the LL subdomain (e.g. "db._dns-sd._udp.0.0.254.169.in-addr.arpa")
+					// and when there simply happen to be no instances of the service the client is looking
+					// for (e.g. iTunes is set to look for RAOP devices, and the current network has none).
+					debugf("SendQueries: Zero current answers for %##s (%s); will reconfirm antecedents",
+						q->qname.c, DNSTypeName(q->qtype));
+					// Sending third query, and no answers yet; time to begin doubting the source
+					ReconfirmAntecedents(m, &q->qname, q->qnamehash, 0);
+					}
+				}
+
+			// Mark for sending. (If no active interfaces, then don't even try.)
+			q->SendOnAll = (q->SendQNow == mDNSInterfaceMark);
+			if (q->SendOnAll)
+				{
+				q->SendQNow  = !intf ? mDNSNULL : (q->InterfaceID) ? q->InterfaceID : intf->InterfaceID;
+				q->LastQTime = m->timenow;
+				}
+
+			// If we recorded a duplicate suppression for this question less than half an interval ago,
+			// then we consider it recent enough that we don't need to do an identical query ourselves.
+			ExpireDupSuppressInfo(q->DupSuppress, m->timenow - q->ThisQInterval/2);
+
+			q->LastQTxTime      = m->timenow;
+			q->RecentAnswerPkts = 0;
+			if (q->RequestUnicast) q->RequestUnicast--;
+			}
+		// For all questions (not just the ones we're sending) check what the next scheduled event will be
+		// We don't need to consider NewQuestions here because for those we'll set m->NextScheduledQuery in AnswerNewQuestion
+		SetNextQueryTime(m,q);
+		}
+
+	// 2. Scan our authoritative RR list to see what probes we might need to send
+
+	m->NextScheduledProbe = m->timenow + 0x78000000;
+
+	if (m->CurrentRecord)
+		LogMsg("SendQueries ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord));
+	m->CurrentRecord = m->ResourceRecords;
+	while (m->CurrentRecord)
+		{
+		ar = m->CurrentRecord;
+		m->CurrentRecord = ar->next;
+		if (!AuthRecord_uDNS(ar) && ar->resrec.RecordType == kDNSRecordTypeUnique)	// For all records that are still probing...
+			{
+			// 1. If it's not reached its probe time, just make sure we update m->NextScheduledProbe correctly
+			if (m->timenow - (ar->LastAPTime + ar->ThisAPInterval) < 0)
+				{
+				SetNextAnnounceProbeTime(m, ar);
+				}
+			// 2. else, if it has reached its probe time, mark it for sending and then update m->NextScheduledProbe correctly
+			else if (ar->ProbeCount)
+				{
+				if (ar->AddressProxy.type == mDNSAddrType_IPv4)
+					{
+					LogSPS("SendQueries ARP Probe %d %s %s", ar->ProbeCount, InterfaceNameForID(m, ar->resrec.InterfaceID), ARDisplayString(m,ar));
+					SendARP(m, 1, ar, &zerov4Addr, &zeroEthAddr, &ar->AddressProxy.ip.v4, &ar->WakeUp.IMAC);
+					}
+				else if (ar->AddressProxy.type == mDNSAddrType_IPv6)
+					{
+					LogSPS("SendQueries NDP Probe %d %s %s", ar->ProbeCount, InterfaceNameForID(m, ar->resrec.InterfaceID), ARDisplayString(m,ar));
+					// IPv6 source = zero
+					// No target hardware address
+					// IPv6 target address is address we're probing
+					// Ethernet destination address is Ethernet interface address of the Sleep Proxy client we're probing
+					SendNDP(m, NDP_Sol, 0, ar, &zerov6Addr, mDNSNULL, &ar->AddressProxy.ip.v6, &ar->WakeUp.IMAC);
+					}
+				// Mark for sending. (If no active interfaces, then don't even try.)
+				ar->SendRNow   = (!intf || ar->WakeUp.HMAC.l[0]) ? mDNSNULL : ar->resrec.InterfaceID ? ar->resrec.InterfaceID : intf->InterfaceID;
+				ar->LastAPTime = m->timenow;
+				// When we have a late conflict that resets a record to probing state we use a special marker value greater
+				// than DefaultProbeCountForTypeUnique. Here we detect that state and reset ar->ProbeCount back to the right value.
+				if (ar->ProbeCount > DefaultProbeCountForTypeUnique)
+					ar->ProbeCount = DefaultProbeCountForTypeUnique;
+				ar->ProbeCount--;
+				SetNextAnnounceProbeTime(m, ar);
+				if (ar->ProbeCount == 0)
+					{
+					// If this is the last probe for this record, then see if we have any matching records
+					// on our duplicate list which should similarly have their ProbeCount cleared to zero...
+					AuthRecord *r2;
+					for (r2 = m->DuplicateRecords; r2; r2=r2->next)
+						if (r2->resrec.RecordType == kDNSRecordTypeUnique && RecordIsLocalDuplicate(r2, ar))
+							r2->ProbeCount = 0;
+					// ... then acknowledge this record to the client.
+					// We do this optimistically, just as we're about to send the third probe.
+					// This helps clients that both advertise and browse, and want to filter themselves
+					// from the browse results list, because it helps ensure that the registration
+					// confirmation will be delivered 1/4 second *before* the browse "add" event.
+					// A potential downside is that we could deliver a registration confirmation and then find out
+					// moments later that there's a name conflict, but applications have to be prepared to handle
+					// late conflicts anyway (e.g. on connection of network cable, etc.), so this is nothing new.
+					if (!ar->Acknowledged) AcknowledgeRecord(m, ar);
+					}
+				}
+			// else, if it has now finished probing, move it to state Verified,
+			// and update m->NextScheduledResponse so it will be announced
+			else
+				{
+				if (!ar->Acknowledged) AcknowledgeRecord(m, ar);	// Defensive, just in case it got missed somehow
+				ar->resrec.RecordType     = kDNSRecordTypeVerified;
+				ar->ThisAPInterval = DefaultAnnounceIntervalForTypeUnique;
+				ar->LastAPTime     = m->timenow - DefaultAnnounceIntervalForTypeUnique;
+				SetNextAnnounceProbeTime(m, ar);
+				}
+			}
+		}
+	m->CurrentRecord = m->DuplicateRecords;
+	while (m->CurrentRecord)
+		{
+		ar = m->CurrentRecord;
+		m->CurrentRecord = ar->next;
+		if (ar->resrec.RecordType == kDNSRecordTypeUnique && ar->ProbeCount == 0 && !ar->Acknowledged)
+			AcknowledgeRecord(m, ar);
+		}
+
+	// 3. Now we know which queries and probes we're sending,
+	// go through our interface list sending the appropriate queries on each interface
+	while (intf)
+		{
+		const int OwnerRecordSpace = (m->AnnounceOwner && intf->MAC.l[0]) ? DNSOpt_Header_Space + DNSOpt_Owner_Space(&m->PrimaryMAC, &intf->MAC) : 0;
+		mDNSu8 *queryptr = m->omsg.data;
+		InitializeDNSMessage(&m->omsg.h, zeroID, QueryFlags);
+		if (KnownAnswerList) verbosedebugf("SendQueries:   KnownAnswerList set... Will continue from previous packet");
+		if (!KnownAnswerList)
+			{
+			// Start a new known-answer list
+			CacheRecord **kalistptr = &KnownAnswerList;
+			mDNSu32 answerforecast = OwnerRecordSpace;		// We start by assuming we'll need at least enough space to put the Owner Option
+			
+			// Put query questions in this packet
+			for (q = m->Questions; q && q != m->NewQuestions; q=q->next)
+				{
+				if (mDNSOpaque16IsZero(q->TargetQID) && (q->SendQNow == intf->InterfaceID))
+					{
+					debugf("SendQueries: %s question for %##s (%s) at %d forecast total %d",
+						SuppressOnThisInterface(q->DupSuppress, intf) ? "Suppressing" : "Putting    ",
+						q->qname.c, DNSTypeName(q->qtype), queryptr - m->omsg.data, queryptr + answerforecast - m->omsg.data);
+
+					// If we're suppressing this question, or we successfully put it, update its SendQNow state
+					if (SuppressOnThisInterface(q->DupSuppress, intf) ||
+						BuildQuestion(m, &m->omsg, &queryptr, q, &kalistptr, &answerforecast))
+						{
+						q->SendQNow = (q->InterfaceID || !q->SendOnAll) ? mDNSNULL : GetNextActiveInterfaceID(intf);
+						if (q->WakeOnResolveCount)
+							{
+							mDNSSendWakeOnResolve(m, q);
+							q->WakeOnResolveCount--;
+							}
+						}
+					}
+				}
+
+			// Put probe questions in this packet
+			for (ar = m->ResourceRecords; ar; ar=ar->next)
+				if (ar->SendRNow == intf->InterfaceID)
+					{
+					mDNSBool ucast = (ar->ProbeCount >= DefaultProbeCountForTypeUnique-1) && m->CanReceiveUnicastOn5353;
+					mDNSu16 ucbit = (mDNSu16)(ucast ? kDNSQClass_UnicastResponse : 0);
+					const mDNSu8 *const limit = m->omsg.data + (m->omsg.h.numQuestions ? NormalMaxDNSMessageData : AbsoluteMaxDNSMessageData);
+					// We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n)
+					mDNSu32 forecast = answerforecast + 12 + ar->resrec.rdestimate;
+					mDNSu8 *newptr = putQuestion(&m->omsg, queryptr, limit - forecast, ar->resrec.name, kDNSQType_ANY, (mDNSu16)(ar->resrec.rrclass | ucbit));
+					if (newptr)
+						{
+						queryptr       = newptr;
+						answerforecast = forecast;
+						ar->SendRNow = (ar->resrec.InterfaceID) ? mDNSNULL : GetNextActiveInterfaceID(intf);
+						ar->IncludeInProbe = mDNStrue;
+						verbosedebugf("SendQueries:   Put Question %##s (%s) probecount %d",
+							ar->resrec.name->c, DNSTypeName(ar->resrec.rrtype), ar->ProbeCount);
+						}
+					}
+			}
+
+		// Put our known answer list (either new one from this question or questions, or remainder of old one from last time)
+		while (KnownAnswerList)
+			{
+			CacheRecord *ka = KnownAnswerList;
+			mDNSu32 SecsSinceRcvd = ((mDNSu32)(m->timenow - ka->TimeRcvd)) / mDNSPlatformOneSecond;
+			mDNSu8 *newptr = PutResourceRecordTTLWithLimit(&m->omsg, queryptr, &m->omsg.h.numAnswers,
+				&ka->resrec, ka->resrec.rroriginalttl - SecsSinceRcvd, m->omsg.data + NormalMaxDNSMessageData - OwnerRecordSpace);
+			if (newptr)
+				{
+				verbosedebugf("SendQueries:   Put %##s (%s) at %d - %d",
+					ka->resrec.name->c, DNSTypeName(ka->resrec.rrtype), queryptr - m->omsg.data, newptr - m->omsg.data);
+				queryptr = newptr;
+				KnownAnswerList = ka->NextInKAList;
+				ka->NextInKAList = mDNSNULL;
+				}
+			else
+				{
+				// If we ran out of space and we have more than one question in the packet, that's an error --
+				// we shouldn't have put more than one question if there was a risk of us running out of space.
+				if (m->omsg.h.numQuestions > 1)
+					LogMsg("SendQueries:   Put %d answers; No more space for known answers", m->omsg.h.numAnswers);
+				m->omsg.h.flags.b[0] |= kDNSFlag0_TC;
+				break;
+				}
+			}
+
+		for (ar = m->ResourceRecords; ar; ar=ar->next)
+			if (ar->IncludeInProbe)
+				{
+				mDNSu8 *newptr = PutResourceRecord(&m->omsg, queryptr, &m->omsg.h.numAuthorities, &ar->resrec);
+				ar->IncludeInProbe = mDNSfalse;
+				if (newptr) queryptr = newptr;
+				else LogMsg("SendQueries:   How did we fail to have space for the Update record %s", ARDisplayString(m,ar));
+				}
+
+		if (queryptr > m->omsg.data)
+			{
+			if (OwnerRecordSpace)
+				{
+				AuthRecord opt;
+				mDNS_SetupResourceRecord(&opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
+				opt.resrec.rrclass    = NormalMaxDNSMessageData;
+				opt.resrec.rdlength   = sizeof(rdataOPT);	// One option in this OPT record
+				opt.resrec.rdestimate = sizeof(rdataOPT);
+				SetupOwnerOpt(m, intf, &opt.resrec.rdata->u.opt[0]);
+				LogSPS("SendQueries putting %s", ARDisplayString(m, &opt));
+				queryptr = PutResourceRecordTTLWithLimit(&m->omsg, queryptr, &m->omsg.h.numAdditionals,
+					&opt.resrec, opt.resrec.rroriginalttl, m->omsg.data + AbsoluteMaxDNSMessageData);
+				if (!queryptr)
+					LogMsg("SendQueries: How did we fail to have space for the OPT record (%d/%d/%d/%d) %s",
+						m->omsg.h.numQuestions, m->omsg.h.numAnswers, m->omsg.h.numAuthorities, m->omsg.h.numAdditionals, ARDisplayString(m, &opt));
+				if (queryptr > m->omsg.data + NormalMaxDNSMessageData)
+					if (m->omsg.h.numQuestions != 1 || m->omsg.h.numAnswers != 0 || m->omsg.h.numAuthorities != 1 || m->omsg.h.numAdditionals != 1)
+						LogMsg("SendQueries: Why did we generate oversized packet with OPT record %p %p %p (%d/%d/%d/%d) %s",
+							m->omsg.data, m->omsg.data + NormalMaxDNSMessageData, queryptr,
+							m->omsg.h.numQuestions, m->omsg.h.numAnswers, m->omsg.h.numAuthorities, m->omsg.h.numAdditionals, ARDisplayString(m, &opt));
+				}
+
+			if ((m->omsg.h.flags.b[0] & kDNSFlag0_TC) && m->omsg.h.numQuestions > 1)
+				LogMsg("SendQueries: Should not have more than one question (%d) in a truncated packet", m->omsg.h.numQuestions);
+			debugf("SendQueries:   Sending %d Question%s %d Answer%s %d Update%s on %p",
+				m->omsg.h.numQuestions,   m->omsg.h.numQuestions   == 1 ? "" : "s",
+				m->omsg.h.numAnswers,     m->omsg.h.numAnswers     == 1 ? "" : "s",
+				m->omsg.h.numAuthorities, m->omsg.h.numAuthorities == 1 ? "" : "s", intf->InterfaceID);
+			if (intf->IPv4Available) mDNSSendDNSMessage(m, &m->omsg, queryptr, intf->InterfaceID, mDNSNULL, &AllDNSLinkGroup_v4, MulticastDNSPort, mDNSNULL, mDNSNULL);
+			if (intf->IPv6Available) mDNSSendDNSMessage(m, &m->omsg, queryptr, intf->InterfaceID, mDNSNULL, &AllDNSLinkGroup_v6, MulticastDNSPort, mDNSNULL, mDNSNULL);
+			if (!m->SuppressSending) m->SuppressSending = NonZeroTime(m->timenow + (mDNSPlatformOneSecond+9)/10);
+			if (++pktcount >= 1000)
+				{ LogMsg("SendQueries exceeded loop limit %d: giving up", pktcount); break; }
+			// There might be more records left in the known answer list, or more questions to send
+			// on this interface, so go around one more time and try again.
+			}
+		else	// Nothing more to send on this interface; go to next
+			{
+			const NetworkInterfaceInfo *next = GetFirstActiveInterface(intf->next);
+			#if MDNS_DEBUGMSGS && 0
+			const char *const msg = next ? "SendQueries:   Nothing more on %p; moving to %p" : "SendQueries:   Nothing more on %p";
+			debugf(msg, intf, next);
+			#endif
+			intf = next;
+			}
+		}
+
+	// 4. Final housekeeping
+	
+	// 4a. Debugging check: Make sure we announced all our records
+	for (ar = m->ResourceRecords; ar; ar=ar->next)
+		if (ar->SendRNow)
+			{
+			if (ar->ARType != AuthRecordLocalOnly && ar->ARType != AuthRecordP2P)
+				LogMsg("SendQueries: No active interface %p to send probe: %p %s", ar->SendRNow, ar->resrec.InterfaceID, ARDisplayString(m, ar));
+			ar->SendRNow = mDNSNULL;
+			}
+
+	// 4b. When we have lingering cache records that we're keeping around for a few seconds in the hope
+	// that their interface which went away might come back again, the logic will want to send queries
+	// for those records, but we can't because their interface isn't here any more, so to keep the
+	// state machine ticking over we just pretend we did so.
+	// If the interface does not come back in time, the cache record will expire naturally
+	FORALL_CACHERECORDS(slot, cg, cr)
+		if (cr->CRActiveQuestion && cr->UnansweredQueries < MaxUnansweredQueries)
+			if (m->timenow + TicksTTL(cr)/50 - cr->NextRequiredQuery >= 0)
+				{
+				cr->UnansweredQueries++;
+				cr->CRActiveQuestion->SendQNow = mDNSNULL;
+				SetNextCacheCheckTimeForRecord(m, cr);
+				}
+
+	// 4c. Debugging check: Make sure we sent all our planned questions
+	// Do this AFTER the lingering cache records check above, because that will prevent spurious warnings for questions
+	// we legitimately couldn't send because the interface is no longer available
+	for (q = m->Questions; q; q=q->next)
+		if (q->SendQNow)
+			{
+			DNSQuestion *x;
+			for (x = m->NewQuestions; x; x=x->next) if (x == q) break;	// Check if this question is a NewQuestion
+			LogMsg("SendQueries: No active interface %p to send %s question: %p %##s (%s)", q->SendQNow, x ? "new" : "old", q->InterfaceID, q->qname.c, DNSTypeName(q->qtype));
+			q->SendQNow = mDNSNULL;
+			}
+	}
+
+mDNSlocal void SendWakeup(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSEthAddr *EthAddr, mDNSOpaque48 *password)
+	{
+	int i, j;
+	mDNSu8 *ptr = m->omsg.data;
+	NetworkInterfaceInfo *intf = FirstInterfaceForID(m, InterfaceID);
+	if (!intf) { LogMsg("SendARP: No interface with InterfaceID %p found", InterfaceID); return; }
+
+	// 0x00 Destination address
+	for (i=0; i<6; i++) *ptr++ = EthAddr->b[i];
+
+	// 0x06 Source address (Note: Since we don't currently set the BIOCSHDRCMPLT option, BPF will fill in the real interface address for us)
+	for (i=0; i<6; i++) *ptr++ = intf->MAC.b[0];
+
+	// 0x0C Ethertype (0x0842)
+	*ptr++ = 0x08;
+	*ptr++ = 0x42;
+
+	// 0x0E Wakeup sync sequence
+	for (i=0; i<6; i++) *ptr++ = 0xFF;
+
+	// 0x14 Wakeup data
+	for (j=0; j<16; j++) for (i=0; i<6; i++) *ptr++ = EthAddr->b[i];
+
+	// 0x74 Password
+	for (i=0; i<6; i++) *ptr++ = password->b[i];
+
+	mDNSPlatformSendRawPacket(m->omsg.data, ptr, InterfaceID);
+
+	// For Ethernet switches that don't flood-foward packets with unknown unicast destination MAC addresses,
+	// broadcast is the only reliable way to get a wakeup packet to the intended target machine.
+	// For 802.11 WPA networks, where a sleeping target machine may have missed a broadcast/multicast
+	// key rotation, unicast is the only way to get a wakeup packet to the intended target machine.
+	// So, we send one of each, unicast first, then broadcast second.
+	for (i=0; i<6; i++) m->omsg.data[i] = 0xFF;
+	mDNSPlatformSendRawPacket(m->omsg.data, ptr, InterfaceID);
+	}
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - RR List Management & Task Management
+#endif
+
+// Note: AnswerCurrentQuestionWithResourceRecord can call a user callback, which may change the record list and/or question list.
+// Any code walking either list must use the m->CurrentQuestion (and possibly m->CurrentRecord) mechanism to protect against this.
+// In fact, to enforce this, the routine will *only* answer the question currently pointed to by m->CurrentQuestion,
+// which will be auto-advanced (possibly to NULL) if the client callback cancels the question.
+mDNSexport void AnswerCurrentQuestionWithResourceRecord(mDNS *const m, CacheRecord *const rr, const QC_result AddRecord)
+	{
+	DNSQuestion *const q = m->CurrentQuestion;
+	mDNSBool followcname = FollowCNAME(q, &rr->resrec, AddRecord);
+
+	verbosedebugf("AnswerCurrentQuestionWithResourceRecord:%4lu %s TTL %d %s",
+		q->CurrentAnswers, AddRecord ? "Add" : "Rmv", rr->resrec.rroriginalttl, CRDisplayString(m, rr));
+
+	// Normally we don't send out the unicast query if we have answered using our local only auth records e.g., /etc/hosts.
+	// But if the query for "A" record has a local answer but query for "AAAA" record has no local answer, we might
+	// send the AAAA query out which will come back with CNAME and will also answer the "A" query. To prevent that,
+	// we check to see if that query already has a unique local answer.
+	if (q->LOAddressAnswers)
+		{
+		LogInfo("AnswerCurrentQuestionWithResourceRecord: Question %p %##s (%s) not answering with record %s due to "
+			"LOAddressAnswers %d", q, q->qname.c, DNSTypeName(q->qtype), ARDisplayString(m, rr),
+			q->LOAddressAnswers);
+		return;
+		}
+
+	if (QuerySuppressed(q))
+		{
+		// If the query is suppressed, then we don't want to answer from the cache. But if this query is
+		// supposed to time out, we still want to callback the clients. We do this only for TimeoutQuestions
+		// that are timing out, which we know are answered with Negative cache record when timing out.
+		if (!q->TimeoutQuestion || rr->resrec.RecordType != kDNSRecordTypePacketNegative || (m->timenow - q->StopTime < 0))
+			return;
+		}
+
+	// Note: Use caution here. In the case of records with rr->DelayDelivery set, AnswerCurrentQuestionWithResourceRecord(... mDNStrue)
+	// may be called twice, once when the record is received, and again when it's time to notify local clients.
+	// If any counters or similar are added here, care must be taken to ensure that they are not double-incremented by this.
+
+	rr->LastUsed = m->timenow;
+	if (AddRecord == QC_add && !q->DuplicateOf && rr->CRActiveQuestion != q)
+		{
+		if (!rr->CRActiveQuestion) m->rrcache_active++;	// If not previously active, increment rrcache_active count
+		debugf("AnswerCurrentQuestionWithResourceRecord: Updating CRActiveQuestion from %p to %p for cache record %s, CurrentAnswer %d",
+			rr->CRActiveQuestion, q, CRDisplayString(m,rr), q->CurrentAnswers);
+		rr->CRActiveQuestion = q;						// We know q is non-null
+		SetNextCacheCheckTimeForRecord(m, rr);
+		}
+
+	// If this is:
+	// (a) a no-cache add, where we've already done at least one 'QM' query, or
+	// (b) a normal add, where we have at least one unique-type answer,
+	// then there's no need to keep polling the network.
+	// (If we have an answer in the cache, then we'll automatically ask again in time to stop it expiring.)
+	// We do this for mDNS questions and uDNS one-shot questions, but not for
+	// uDNS LongLived questions, because that would mess up our LLQ lease renewal timing.
+	if ((AddRecord == QC_addnocache && !q->RequestUnicast) ||
+		(AddRecord == QC_add && (q->ExpectUnique || (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask))))
+		if (ActiveQuestion(q) && (mDNSOpaque16IsZero(q->TargetQID) || !q->LongLived))
+			{
+			q->LastQTime        = m->timenow;
+			q->LastQTxTime      = m->timenow;
+			q->RecentAnswerPkts = 0;
+			q->ThisQInterval    = MaxQuestionInterval;
+			q->RequestUnicast   = mDNSfalse;
+			debugf("AnswerCurrentQuestionWithResourceRecord: Set MaxQuestionInterval for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+			}
+
+	if (rr->DelayDelivery) return;		// We'll come back later when CacheRecordDeferredAdd() calls us
+
+	// Only deliver negative answers if client has explicitly requested them
+	if (rr->resrec.RecordType == kDNSRecordTypePacketNegative || (q->qtype != kDNSType_NSEC && RRAssertsNonexistence(&rr->resrec, q->qtype)))
+		if (!AddRecord || !q->ReturnIntermed) return;
+
+	// For CNAME results to non-CNAME questions, only inform the client if they explicitly requested that
+	if (q->QuestionCallback && !q->NoAnswer && (!followcname || q->ReturnIntermed))
+		{
+		mDNS_DropLockBeforeCallback();		// Allow client (and us) to legally make mDNS API calls
+		if (q->qtype != kDNSType_NSEC && RRAssertsNonexistence(&rr->resrec, q->qtype))
+			{
+			CacheRecord neg;
+			MakeNegativeCacheRecord(m, &neg, &q->qname, q->qnamehash, q->qtype, q->qclass, 1, rr->resrec.InterfaceID, q->qDNSServer);
+			q->QuestionCallback(m, q, &neg.resrec, AddRecord);
+			}
+		else
+			q->QuestionCallback(m, q, &rr->resrec, AddRecord);
+		mDNS_ReclaimLockAfterCallback();	// Decrement mDNS_reentrancy to block mDNS API calls again
+		}
+	// Note: Proceed with caution here because client callback function is allowed to do anything,
+	// including starting/stopping queries, registering/deregistering records, etc.
+
+	if (followcname && m->CurrentQuestion == q)
+		AnswerQuestionByFollowingCNAME(m, q, &rr->resrec);
+	}
+
+// New Questions are answered through AnswerNewQuestion. But there may not have been any
+// matching cache records for the questions when it is called. There are two possibilities.
+//
+// 1) There are no cache records
+// 2) There are cache records but the DNSServers between question and cache record don't match.
+//
+// In the case of (1), where there are no cache records and later we add them when we get a response,
+// CacheRecordAdd/CacheRecordDeferredAdd will take care of adding the cache and delivering the ADD
+// events to the application. If we already have a cache entry, then no ADD events are delivered
+// unless the RDATA has changed
+//
+// In the case of (2) where we had the cache records and did not answer because of the DNSServer mismatch,
+// we need to answer them whenever we change the DNSServer.  But we can't do it at the instant the DNSServer
+// changes because when we do the callback, the question can get deleted and the calling function would not
+// know how to handle it. So, we run this function from mDNS_Execute to handle DNSServer changes on the
+// question
+
+mDNSlocal void AnswerQuestionsForDNSServerChanges(mDNS *const m)
+	{
+	DNSQuestion *q;
+	DNSQuestion *qnext;
+	CacheRecord *rr;
+	mDNSu32 slot;
+	CacheGroup *cg;
+
+	if (m->CurrentQuestion)
+		LogMsg("AnswerQuestionsForDNSServerChanges: ERROR m->CurrentQuestion already set: %##s (%s)",
+				m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype));
+
+	for (q = m->Questions; q && q != m->NewQuestions; q = qnext)
+		{
+		qnext = q->next;
+
+		// multicast or DNSServers did not change.
+		if (mDNSOpaque16IsZero(q->TargetQID)) continue;
+		if (!q->deliverAddEvents) continue;
+
+		// We are going to look through the cache for this question since it changed
+		// its DNSserver last time. Reset it so that we don't call them again. Calling
+		// them again will deliver duplicate events to the application
+		q->deliverAddEvents = mDNSfalse;
+		if (QuerySuppressed(q)) continue;
+		m->CurrentQuestion = q;
+		slot = HashSlot(&q->qname);
+		cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname);
+		for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
+			{
+			if (SameNameRecordAnswersQuestion(&rr->resrec, q))
+				{
+				LogInfo("AnswerQuestionsForDNSServerChanges: Calling AnswerCurrentQuestionWithResourceRecord for question %p %##s using resource record %s",
+					q, q->qname.c, CRDisplayString(m, rr));
+				// When this question penalizes a DNS server and has no more DNS servers to pick, we normally
+				// deliver a negative cache response and suspend the question for 60 seconds (see uDNS_CheckCurrentQuestion).
+				// But sometimes we may already find the negative cache entry and deliver that here as the process
+				// of changing DNS servers. When the cache entry is about to expire, we will resend the question and
+				// that time, we need to make sure that we have a valid DNS server. Otherwise, we will deliver
+				// a negative cache response without trying the server.
+				if (!q->qDNSServer && !q->DuplicateOf && rr->resrec.RecordType == kDNSRecordTypePacketNegative)
+					{
+					DNSQuestion *qptr;
+					SetValidDNSServers(m, q);
+					q->qDNSServer = GetServerForQuestion(m, q);
+					for (qptr = q->next ; qptr; qptr = qptr->next)
+						if (qptr->DuplicateOf == q) { qptr->validDNSServers = q->validDNSServers; qptr->qDNSServer = q->qDNSServer; }
+					}
+				q->CurrentAnswers++;
+				if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers++;
+				if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers++;
+				AnswerCurrentQuestionWithResourceRecord(m, rr, QC_add);
+				if (m->CurrentQuestion != q) break;		// If callback deleted q, then we're finished here
+				}
+			}
+		}
+		m->CurrentQuestion = mDNSNULL;
+	}
+
+mDNSlocal void CacheRecordDeferredAdd(mDNS *const m, CacheRecord *rr)
+	{
+	rr->DelayDelivery = 0;
+	if (m->CurrentQuestion)
+		LogMsg("CacheRecordDeferredAdd ERROR m->CurrentQuestion already set: %##s (%s)",
+			m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype));
+	m->CurrentQuestion = m->Questions;
+	while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions)
+		{
+		DNSQuestion *q = m->CurrentQuestion;
+		if (ResourceRecordAnswersQuestion(&rr->resrec, q))
+			AnswerCurrentQuestionWithResourceRecord(m, rr, QC_add);
+		if (m->CurrentQuestion == q)	// If m->CurrentQuestion was not auto-advanced, do it ourselves now
+			m->CurrentQuestion = q->next;
+		}
+	m->CurrentQuestion = mDNSNULL;
+	}
+
+mDNSlocal mDNSs32 CheckForSoonToExpireRecords(mDNS *const m, const domainname *const name, const mDNSu32 namehash, const mDNSu32 slot)
+	{
+	const mDNSs32 threshhold = m->timenow + mDNSPlatformOneSecond;	// See if there are any records expiring within one second
+	const mDNSs32 start      = m->timenow - 0x10000000;
+	mDNSs32 delay = start;
+	CacheGroup *cg = CacheGroupForName(m, slot, namehash, name);
+	const CacheRecord *rr;
+	for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
+		if (threshhold - RRExpireTime(rr) >= 0)		// If we have records about to expire within a second
+			if (delay - RRExpireTime(rr) < 0)		// then delay until after they've been deleted
+				delay = RRExpireTime(rr);
+	if (delay - start > 0) return(NonZeroTime(delay));
+	else return(0);
+	}
+
+// CacheRecordAdd is only called from CreateNewCacheEntry, *never* directly as a result of a client API call.
+// If new questions are created as a result of invoking client callbacks, they will be added to
+// the end of the question list, and m->NewQuestions will be set to indicate the first new question.
+// rr is a new CacheRecord just received into our cache
+// (kDNSRecordTypePacketAns/PacketAnsUnique/PacketAdd/PacketAddUnique).
+// Note: CacheRecordAdd calls AnswerCurrentQuestionWithResourceRecord which can call a user callback,
+// which may change the record list and/or question list.
+// Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
+mDNSlocal void CacheRecordAdd(mDNS *const m, CacheRecord *rr)
+	{
+	DNSQuestion *q;
+
+	// We stop when we get to NewQuestions -- if we increment their CurrentAnswers/LargeAnswers/UniqueAnswers
+	// counters here we'll end up double-incrementing them when we do it again in AnswerNewQuestion().
+	for (q = m->Questions; q && q != m->NewQuestions; q=q->next)
+		{
+		if (ResourceRecordAnswersQuestion(&rr->resrec, q))
+			{
+			// If this question is one that's actively sending queries, and it's received ten answers within one
+			// second of sending the last query packet, then that indicates some radical network topology change,
+			// so reset its exponential backoff back to the start. We must be at least at the eight-second interval
+			// to do this. If we're at the four-second interval, or less, there's not much benefit accelerating
+			// because we will anyway send another query within a few seconds. The first reset query is sent out
+			// randomized over the next four seconds to reduce possible synchronization between machines.
+			if (q->LastAnswerPktNum != m->PktNum)
+				{
+				q->LastAnswerPktNum = m->PktNum;
+				if (mDNSOpaque16IsZero(q->TargetQID) && ActiveQuestion(q) && ++q->RecentAnswerPkts >= 10 &&
+					q->ThisQInterval > InitialQuestionInterval * QuestionIntervalStep3 && m->timenow - q->LastQTxTime < mDNSPlatformOneSecond)
+					{
+					LogMsg("CacheRecordAdd: %##s (%s) got immediate answer burst (%d); restarting exponential backoff sequence (%d)",
+						q->qname.c, DNSTypeName(q->qtype), q->RecentAnswerPkts, q->ThisQInterval);
+					q->LastQTime      = m->timenow - InitialQuestionInterval + (mDNSs32)mDNSRandom((mDNSu32)mDNSPlatformOneSecond*4);
+					q->ThisQInterval  = InitialQuestionInterval;
+					SetNextQueryTime(m,q);
+					}
+				}
+			verbosedebugf("CacheRecordAdd %p %##s (%s) %lu %#a:%d question %p", rr, rr->resrec.name->c,
+				DNSTypeName(rr->resrec.rrtype), rr->resrec.rroriginalttl, rr->resrec.rDNSServer ?
+				&rr->resrec.rDNSServer->addr : mDNSNULL, mDNSVal16(rr->resrec.rDNSServer ?
+				rr->resrec.rDNSServer->port : zeroIPPort), q);
+			q->CurrentAnswers++;
+			q->unansweredQueries = 0;
+			if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers++;
+			if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers++;
+			if (q->CurrentAnswers > 4000)
+				{
+				static int msgcount = 0;
+				if (msgcount++ < 10)
+					LogMsg("CacheRecordAdd: %##s (%s) has %d answers; shedding records to resist DOS attack",
+						q->qname.c, DNSTypeName(q->qtype), q->CurrentAnswers);
+				rr->resrec.rroriginalttl = 0;
+				rr->UnansweredQueries = MaxUnansweredQueries;
+				}
+			}
+		}
+
+	if (!rr->DelayDelivery)
+		{
+		if (m->CurrentQuestion)
+			LogMsg("CacheRecordAdd ERROR m->CurrentQuestion already set: %##s (%s)", m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype));
+		m->CurrentQuestion = m->Questions;
+		while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions)
+			{
+			q = m->CurrentQuestion;
+			if (ResourceRecordAnswersQuestion(&rr->resrec, q))
+				AnswerCurrentQuestionWithResourceRecord(m, rr, QC_add);
+			if (m->CurrentQuestion == q)	// If m->CurrentQuestion was not auto-advanced, do it ourselves now
+				m->CurrentQuestion = q->next;
+			}
+		m->CurrentQuestion = mDNSNULL;
+		}
+
+	SetNextCacheCheckTimeForRecord(m, rr);
+	}
+
+// NoCacheAnswer is only called from mDNSCoreReceiveResponse, *never* directly as a result of a client API call.
+// If new questions are created as a result of invoking client callbacks, they will be added to
+// the end of the question list, and m->NewQuestions will be set to indicate the first new question.
+// rr is a new CacheRecord just received from the wire (kDNSRecordTypePacketAns/AnsUnique/Add/AddUnique)
+// but we don't have any place to cache it. We'll deliver question 'add' events now, but we won't have any
+// way to deliver 'remove' events in future, nor will we be able to include this in known-answer lists,
+// so we immediately bump ThisQInterval up to MaxQuestionInterval to avoid pounding the network.
+// Note: NoCacheAnswer calls AnswerCurrentQuestionWithResourceRecord which can call a user callback,
+// which may change the record list and/or question list.
+// Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
+mDNSlocal void NoCacheAnswer(mDNS *const m, CacheRecord *rr)
+	{
+	LogMsg("No cache space: Delivering non-cached result for %##s", m->rec.r.resrec.name->c);
+	if (m->CurrentQuestion)
+		LogMsg("NoCacheAnswer ERROR m->CurrentQuestion already set: %##s (%s)", m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype));
+	m->CurrentQuestion = m->Questions;
+	// We do this for *all* questions, not stopping when we get to m->NewQuestions,
+	// since we're not caching the record and we'll get no opportunity to do this later
+	while (m->CurrentQuestion)
+		{
+		DNSQuestion *q = m->CurrentQuestion;
+		if (ResourceRecordAnswersQuestion(&rr->resrec, q))
+			AnswerCurrentQuestionWithResourceRecord(m, rr, QC_addnocache);	// QC_addnocache means "don't expect remove events for this"
+		if (m->CurrentQuestion == q)	// If m->CurrentQuestion was not auto-advanced, do it ourselves now
+			m->CurrentQuestion = q->next;
+		}
+	m->CurrentQuestion = mDNSNULL;
+	}
+
+// CacheRecordRmv is only called from CheckCacheExpiration, which is called from mDNS_Execute.
+// Note that CacheRecordRmv is *only* called for records that are referenced by at least one active question.
+// If new questions are created as a result of invoking client callbacks, they will be added to
+// the end of the question list, and m->NewQuestions will be set to indicate the first new question.
+// rr is an existing cache CacheRecord that just expired and is being deleted
+// (kDNSRecordTypePacketAns/PacketAnsUnique/PacketAdd/PacketAddUnique).
+// Note: CacheRecordRmv calls AnswerCurrentQuestionWithResourceRecord which can call a user callback,
+// which may change the record list and/or question list.
+// Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
+mDNSlocal void CacheRecordRmv(mDNS *const m, CacheRecord *rr)
+	{
+	if (m->CurrentQuestion)
+		LogMsg("CacheRecordRmv ERROR m->CurrentQuestion already set: %##s (%s)",
+			m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype));
+	m->CurrentQuestion = m->Questions;
+
+	// We stop when we get to NewQuestions -- for new questions their CurrentAnswers/LargeAnswers/UniqueAnswers counters
+	// will all still be zero because we haven't yet gone through the cache counting how many answers we have for them.
+	while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions)
+		{
+		DNSQuestion *q = m->CurrentQuestion;
+		// When a question enters suppressed state, we generate RMV events and generate a negative
+		// response. A cache may be present that answers this question e.g., cache entry generated
+		// before the question became suppressed. We need to skip the suppressed questions here as
+		// the RMV event has already been generated.
+		if (!QuerySuppressed(q) && ResourceRecordAnswersQuestion(&rr->resrec, q))
+			{
+			verbosedebugf("CacheRecordRmv %p %s", rr, CRDisplayString(m, rr));
+			q->FlappingInterface1 = mDNSNULL;
+			q->FlappingInterface2 = mDNSNULL;
+		
+			// When a question changes DNS server, it is marked with deliverAddEvents if we find any
+			// cache entry corresponding to the new DNS server. Before we deliver the ADD event, the
+			// cache entry may be removed in which case CurrentAnswers can be zero.
+			if (q->deliverAddEvents && !q->CurrentAnswers)
+				{
+				LogInfo("CacheRecordRmv: Question %p %##s (%s) deliverAddEvents set, DNSServer %#a:%d",
+					q, q->qname.c, DNSTypeName(q->qtype), q->qDNSServer ? &q->qDNSServer->addr : mDNSNULL,
+					mDNSVal16(q->qDNSServer ? q->qDNSServer->port : zeroIPPort));
+				m->CurrentQuestion = q->next;
+				continue;
+				}
+			if (q->CurrentAnswers == 0)
+				LogMsg("CacheRecordRmv ERROR!!: How can CurrentAnswers already be zero for %p %##s (%s) DNSServer %#a:%d",
+					q, q->qname.c, DNSTypeName(q->qtype), q->qDNSServer ? &q->qDNSServer->addr : mDNSNULL,
+					mDNSVal16(q->qDNSServer ? q->qDNSServer->port : zeroIPPort));
+			else
+				{
+				q->CurrentAnswers--;
+				if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers--;
+				if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers--;
+				}
+			if (rr->resrec.rdata->MaxRDLength) // Never generate "remove" events for negative results
+				{
+				if (q->CurrentAnswers == 0)
+					{
+					LogInfo("CacheRecordRmv: Last answer for %##s (%s) expired from cache; will reconfirm antecedents",
+						q->qname.c, DNSTypeName(q->qtype));
+					ReconfirmAntecedents(m, &q->qname, q->qnamehash, 0);
+					}
+				AnswerCurrentQuestionWithResourceRecord(m, rr, QC_rmv);
+				}
+			}
+		if (m->CurrentQuestion == q)	// If m->CurrentQuestion was not auto-advanced, do it ourselves now
+			m->CurrentQuestion = q->next;
+		}
+	m->CurrentQuestion = mDNSNULL;
+	}
+
+mDNSlocal void ReleaseCacheEntity(mDNS *const m, CacheEntity *e)
+	{
+#if APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING >= 1
+	unsigned int i;
+	for (i=0; i<sizeof(*e); i++) ((char*)e)[i] = 0xFF;
+#endif
+	e->next = m->rrcache_free;
+	m->rrcache_free = e;
+	m->rrcache_totalused--;
+	}
+
+mDNSlocal void ReleaseCacheGroup(mDNS *const m, CacheGroup **cp)
+	{
+	CacheEntity *e = (CacheEntity *)(*cp);
+	//LogMsg("ReleaseCacheGroup:  Releasing CacheGroup for %p, %##s", (*cp)->name->c, (*cp)->name->c);
+	if ((*cp)->rrcache_tail != &(*cp)->members)
+		LogMsg("ERROR: (*cp)->members == mDNSNULL but (*cp)->rrcache_tail != &(*cp)->members)");
+	//if ((*cp)->name != (domainname*)((*cp)->namestorage))
+	//	LogMsg("ReleaseCacheGroup: %##s, %p %p", (*cp)->name->c, (*cp)->name, (domainname*)((*cp)->namestorage));
+	if ((*cp)->name != (domainname*)((*cp)->namestorage)) mDNSPlatformMemFree((*cp)->name);
+	(*cp)->name = mDNSNULL;
+	*cp = (*cp)->next;			// Cut record from list
+	ReleaseCacheEntity(m, e);
+	}
+
+mDNSlocal void ReleaseCacheRecord(mDNS *const m, CacheRecord *r)
+	{
+	//LogMsg("ReleaseCacheRecord: Releasing %s", CRDisplayString(m, r));
+	if (r->resrec.rdata && r->resrec.rdata != (RData*)&r->smallrdatastorage) mDNSPlatformMemFree(r->resrec.rdata);
+	r->resrec.rdata = mDNSNULL;
+	ReleaseCacheEntity(m, (CacheEntity *)r);
+	}
+
+// Note: We want to be careful that we deliver all the CacheRecordRmv calls before delivering
+// CacheRecordDeferredAdd calls. The in-order nature of the cache lists ensures that all
+// callbacks for old records are delivered before callbacks for newer records.
+mDNSlocal void CheckCacheExpiration(mDNS *const m, const mDNSu32 slot, CacheGroup *const cg)
+	{
+	CacheRecord **rp = &cg->members;
+
+	if (m->lock_rrcache) { LogMsg("CheckCacheExpiration ERROR! Cache already locked!"); return; }
+	m->lock_rrcache = 1;
+
+	while (*rp)
+		{
+		CacheRecord *const rr = *rp;
+		mDNSs32 event = RRExpireTime(rr);
+		if (m->timenow - event >= 0)	// If expired, delete it
+			{
+			*rp = rr->next;				// Cut it from the list
+			verbosedebugf("CheckCacheExpiration: Deleting%7d %7d %p %s",
+				m->timenow - rr->TimeRcvd, rr->resrec.rroriginalttl, rr->CRActiveQuestion, CRDisplayString(m, rr));
+			if (rr->CRActiveQuestion)	// If this record has one or more active questions, tell them it's going away
+				{
+				DNSQuestion *q = rr->CRActiveQuestion;
+				// When a cache record is about to expire, we expect to do four queries at 80-82%, 85-87%, 90-92% and
+				// then 95-97% of the TTL. If the DNS server does not respond, then we will remove the cache entry
+				// before we pick a new DNS server. As the question interval is set to MaxQuestionInterval, we may
+				// not send out a query anytime soon. Hence, we need to reset the question interval. If this is
+				// a normal deferred ADD case, then AnswerCurrentQuestionWithResourceRecord will reset it to
+				// MaxQuestionInterval. If we have inactive questions referring to negative cache entries,
+				// don't ressurect them as they will deliver duplicate "No such Record" ADD events
+				if (!mDNSOpaque16IsZero(q->TargetQID) && !q->LongLived && ActiveQuestion(q))
+					{
+					q->ThisQInterval = InitialQuestionInterval;
+					q->LastQTime     = m->timenow - q->ThisQInterval;
+					SetNextQueryTime(m, q);
+					}
+				CacheRecordRmv(m, rr);
+				m->rrcache_active--;
+				}
+			ReleaseCacheRecord(m, rr);
+			}
+		else							// else, not expired; see if we need to query
+			{
+			// If waiting to delay delivery, do nothing until then
+			if (rr->DelayDelivery && rr->DelayDelivery - m->timenow > 0)
+				event = rr->DelayDelivery;
+			else
+				{
+				if (rr->DelayDelivery) CacheRecordDeferredAdd(m, rr);
+				if (rr->CRActiveQuestion && rr->UnansweredQueries < MaxUnansweredQueries)
+					{
+					if (m->timenow - rr->NextRequiredQuery < 0)		// If not yet time for next query
+						event = NextCacheCheckEvent(rr);			// then just record when we want the next query
+					else											// else trigger our question to go out now
+						{
+						// Set NextScheduledQuery to timenow so that SendQueries() will run.
+						// SendQueries() will see that we have records close to expiration, and send FEQs for them.
+						m->NextScheduledQuery = m->timenow;
+						// After sending the query we'll increment UnansweredQueries and call SetNextCacheCheckTimeForRecord(),
+						// which will correctly update m->NextCacheCheck for us.
+						event = m->timenow + 0x3FFFFFFF;
+						}
+					}
+				}
+			verbosedebugf("CheckCacheExpiration:%6d %5d %s",
+				(event - m->timenow) / mDNSPlatformOneSecond, CacheCheckGracePeriod(rr), CRDisplayString(m, rr));
+			if (m->rrcache_nextcheck[slot] - event > 0)
+				m->rrcache_nextcheck[slot] = event;
+			rp = &rr->next;
+			}
+		}
+	if (cg->rrcache_tail != rp) verbosedebugf("CheckCacheExpiration: Updating CacheGroup tail from %p to %p", cg->rrcache_tail, rp);
+	cg->rrcache_tail = rp;
+	m->lock_rrcache = 0;
+	}
+
+mDNSlocal void AnswerNewQuestion(mDNS *const m)
+	{
+	mDNSBool ShouldQueryImmediately = mDNStrue;
+	DNSQuestion *const q = m->NewQuestions;		// Grab the question we're going to answer
+	mDNSu32 slot = HashSlot(&q->qname);
+	CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname);
+	AuthRecord *lr;
+	AuthGroup *ag;
+	mDNSBool AnsweredFromCache = mDNSfalse;
+
+	verbosedebugf("AnswerNewQuestion: Answering %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+
+	if (cg) CheckCacheExpiration(m, slot, cg);
+	if (m->NewQuestions != q) { LogInfo("AnswerNewQuestion: Question deleted while doing CheckCacheExpiration"); goto exit; }
+	m->NewQuestions = q->next;
+	// Advance NewQuestions to the next *after* calling CheckCacheExpiration, because if we advance it first
+	// then CheckCacheExpiration may give this question add/remove callbacks, and it's not yet ready for that.
+	//
+	// Also, CheckCacheExpiration() calls CacheRecordDeferredAdd() and CacheRecordRmv(), which invoke
+	// client callbacks, which may delete their own or any other question. Our mechanism for detecting
+	// whether our current m->NewQuestions question got deleted by one of these callbacks is to store the
+	// value of m->NewQuestions in 'q' before calling CheckCacheExpiration(), and then verify afterwards
+	// that they're still the same. If m->NewQuestions has changed (because mDNS_StopQuery_internal
+	// advanced it), that means the question was deleted, so we no longer need to worry about answering
+	// it (and indeed 'q' is now a dangling pointer, so dereferencing it at all would be bad, and the
+	// values we computed for slot and cg are now stale and relate to a question that no longer exists).
+	//
+	// We can't use the usual m->CurrentQuestion mechanism for this because  CacheRecordDeferredAdd() and
+	// CacheRecordRmv() both use that themselves when walking the list of (non-new) questions generating callbacks.
+	// Fortunately mDNS_StopQuery_internal auto-advances both m->CurrentQuestion *AND* m->NewQuestions when
+	// deleting a question, so luckily we have an easy alternative way of detecting if our question got deleted.
+	
+	if (m->lock_rrcache) LogMsg("AnswerNewQuestion ERROR! Cache already locked!");
+	// This should be safe, because calling the client's question callback may cause the
+	// question list to be modified, but should not ever cause the rrcache list to be modified.
+	// If the client's question callback deletes the question, then m->CurrentQuestion will
+	// be advanced, and we'll exit out of the loop
+	m->lock_rrcache = 1;
+	if (m->CurrentQuestion)
+		LogMsg("AnswerNewQuestion ERROR m->CurrentQuestion already set: %##s (%s)",
+			m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype));
+	m->CurrentQuestion = q;		// Indicate which question we're answering, so we'll know if it gets deleted
+
+	if (q->NoAnswer == NoAnswer_Fail)
+		{
+		LogMsg("AnswerNewQuestion: NoAnswer_Fail %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+		MakeNegativeCacheRecord(m, &m->rec.r, &q->qname, q->qnamehash, q->qtype, q->qclass, 60, mDNSInterface_Any, q->qDNSServer);
+		q->NoAnswer = NoAnswer_Normal;		// Temporarily turn off answer suppression
+		AnswerCurrentQuestionWithResourceRecord(m, &m->rec.r, QC_addnocache);
+		// Don't touch the question if it has been stopped already
+		if (m->CurrentQuestion == q) q->NoAnswer = NoAnswer_Fail;		// Restore NoAnswer state
+		m->rec.r.resrec.RecordType = 0;		// Clear RecordType to show we're not still using it
+		}
+	if (m->CurrentQuestion != q) { LogInfo("AnswerNewQuestion: Question deleted while generating NoAnswer_Fail response"); goto exit; }
+
+	// See if we want to tell it about LocalOnly records
+	if (m->CurrentRecord)
+		LogMsg("AnswerNewQuestion ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord));
+	slot = AuthHashSlot(&q->qname);
+	ag = AuthGroupForName(&m->rrauth, slot, q->qnamehash, &q->qname);
+	if (ag)
+		{
+		m->CurrentRecord = ag->members;
+		while (m->CurrentRecord && m->CurrentRecord != ag->NewLocalOnlyRecords)
+			{
+			AuthRecord *rr = m->CurrentRecord;
+			m->CurrentRecord = rr->next;
+			//
+			// If the question is mDNSInterface_LocalOnly, all records local to the machine should be used
+			// to answer the query. This is handled in AnswerNewLocalOnlyQuestion.
+			//
+			// We handle mDNSInterface_Any and scoped questions here. See LocalOnlyRecordAnswersQuestion for more
+			// details on how we handle this case. For P2P we just handle "Interface_Any" questions. For LocalOnly
+			// we handle both mDNSInterface_Any and scoped questions.
+
+			if (rr->ARType == AuthRecordLocalOnly || (rr->ARType == AuthRecordP2P && q->InterfaceID == mDNSInterface_Any))
+				if (LocalOnlyRecordAnswersQuestion(rr, q))
+					{
+					AnswerLocalQuestionWithLocalAuthRecord(m, rr, mDNStrue);
+					if (m->CurrentQuestion != q) break;		// If callback deleted q, then we're finished here
+					}
+			}
+		}
+	m->CurrentRecord = mDNSNULL;
+	
+	if (m->CurrentQuestion != q) { LogInfo("AnswerNewQuestion: Question deleted while while giving LocalOnly record answers"); goto exit; }
+
+	if (q->LOAddressAnswers)
+		{
+		LogInfo("AnswerNewQuestion: Question %p %##s (%s) answered using local auth records LOAddressAnswers %d",
+			q, q->qname.c, DNSTypeName(q->qtype), q->LOAddressAnswers);
+		goto exit;
+		}
+
+	// Before we go check the cache and ship this query on the wire, we have to be sure that there are
+	// no local records that could possibly answer this question. As we did not check the NewLocalRecords, we
+	// need to just peek at them to see whether it will answer this question. If it would answer, pretend
+	// that we answered. AnswerAllLocalQuestionsWithLocalAuthRecord will answer shortly. This happens normally
+	// when we add new /etc/hosts entries and restart the question. It is a new question and also a new record.
+	if (ag)
+		{
+		lr = ag->NewLocalOnlyRecords;
+		while (lr)
+			{
+			if (LORecordAnswersAddressType(lr) && LocalOnlyRecordAnswersQuestion(lr, q))
+				{
+				LogInfo("AnswerNewQuestion: Question %p %##s (%s) will be answered using new local auth records "
+					" LOAddressAnswers %d", q, q->qname.c, DNSTypeName(q->qtype), q->LOAddressAnswers);
+				goto exit;
+				}
+			lr = lr->next;
+			}
+		}
+	
+
+	// If we are not supposed to answer this question, generate a negative response.
+	// Temporarily suspend the SuppressQuery so that AnswerCurrentQuestionWithResourceRecord can answer the question
+	if (QuerySuppressed(q)) { q->SuppressQuery = mDNSfalse; GenerateNegativeResponse(m); q->SuppressQuery = mDNStrue; }
+	else
+		{
+		CacheRecord *rr;
+		for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
+			if (SameNameRecordAnswersQuestion(&rr->resrec, q))
+				{
+				// SecsSinceRcvd is whole number of elapsed seconds, rounded down
+				mDNSu32 SecsSinceRcvd = ((mDNSu32)(m->timenow - rr->TimeRcvd)) / mDNSPlatformOneSecond;
+				if (rr->resrec.rroriginalttl <= SecsSinceRcvd)
+					{
+					LogMsg("AnswerNewQuestion: How is rr->resrec.rroriginalttl %lu <= SecsSinceRcvd %lu for %s %d %d",
+						rr->resrec.rroriginalttl, SecsSinceRcvd, CRDisplayString(m, rr), m->timenow, rr->TimeRcvd);
+					continue;	// Go to next one in loop
+					}
+	
+				// If this record set is marked unique, then that means we can reasonably assume we have the whole set
+				// -- we don't need to rush out on the network and query immediately to see if there are more answers out there
+				if ((rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) || (q->ExpectUnique))
+					ShouldQueryImmediately = mDNSfalse;
+				q->CurrentAnswers++;
+				if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers++;
+				if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers++;
+				AnsweredFromCache = mDNStrue;
+				AnswerCurrentQuestionWithResourceRecord(m, rr, QC_add);
+				if (m->CurrentQuestion != q) break;		// If callback deleted q, then we're finished here
+				}
+			else if (RRTypeIsAddressType(rr->resrec.rrtype) && RRTypeIsAddressType(q->qtype))
+				ShouldQueryImmediately = mDNSfalse;
+		}
+	// We don't use LogInfo for this "Question deleted" message because it happens so routinely that
+	// it's not remotely remarkable, and therefore unlikely to be of much help tracking down bugs.
+	if (m->CurrentQuestion != q) { debugf("AnswerNewQuestion: Question deleted while giving cache answers"); goto exit; }
+
+	// Neither a local record nor a cache entry could answer this question. If this question need to be retried
+	// with search domains, generate a negative response which will now retry after appending search domains.
+	// If the query was suppressed above, we already generated a negative response. When it gets unsuppressed,
+	// we will retry with search domains.
+	if (!QuerySuppressed(q) && !AnsweredFromCache && q->RetryWithSearchDomains)
+		{
+		LogInfo("AnswerNewQuestion: Generating response for retrying with search domains %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+		GenerateNegativeResponse(m);
+		}
+
+	if (m->CurrentQuestion != q) { debugf("AnswerNewQuestion: Question deleted while giving negative answer"); goto exit; }
+
+	// Note: When a query gets suppressed or retried with search domains, we de-activate the question.
+	// Hence we don't execute the following block of code for those cases.
+	if (ShouldQueryImmediately && ActiveQuestion(q))
+		{
+		debugf("AnswerNewQuestion: ShouldQueryImmediately %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+		q->ThisQInterval  = InitialQuestionInterval;
+		q->LastQTime      = m->timenow - q->ThisQInterval;
+		if (mDNSOpaque16IsZero(q->TargetQID))		// For mDNS, spread packets to avoid a burst of simultaneous queries
+			{
+			// Compute random delay in the range 1-6 seconds, then divide by 50 to get 20-120ms
+			if (!m->RandomQueryDelay)
+				m->RandomQueryDelay = (mDNSPlatformOneSecond + mDNSRandom(mDNSPlatformOneSecond*5) - 1) / 50 + 1;
+			q->LastQTime += m->RandomQueryDelay;
+			}
+		}
+
+	// IN ALL CASES make sure that m->NextScheduledQuery is set appropriately.
+	// In cases where m->NewQuestions->DelayAnswering is set, we may have delayed generating our
+	// answers for this question until *after* its scheduled transmission time, in which case
+	// m->NextScheduledQuery may now be set to 'never', and in that case -- even though we're *not* doing
+	// ShouldQueryImmediately -- we still need to make sure we set m->NextScheduledQuery correctly.
+	SetNextQueryTime(m,q);
+
+exit:
+	m->CurrentQuestion = mDNSNULL;
+	m->lock_rrcache = 0;
+	}
+
+// When a NewLocalOnlyQuestion is created, AnswerNewLocalOnlyQuestion runs though our ResourceRecords delivering any
+// appropriate answers, stopping if it reaches a NewLocalOnlyRecord -- these will be handled by AnswerAllLocalQuestionsWithLocalAuthRecord
+mDNSlocal void AnswerNewLocalOnlyQuestion(mDNS *const m)
+	{
+	mDNSu32 slot;
+	AuthGroup *ag;
+	DNSQuestion *q = m->NewLocalOnlyQuestions;		// Grab the question we're going to answer
+	m->NewLocalOnlyQuestions = q->next;				// Advance NewLocalOnlyQuestions to the next (if any)
+
+	debugf("AnswerNewLocalOnlyQuestion: Answering %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+
+	if (m->CurrentQuestion)
+		LogMsg("AnswerNewLocalOnlyQuestion ERROR m->CurrentQuestion already set: %##s (%s)",
+			m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype));
+	m->CurrentQuestion = q;		// Indicate which question we're answering, so we'll know if it gets deleted
+
+	if (m->CurrentRecord)
+		LogMsg("AnswerNewLocalOnlyQuestion ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord));
+
+	// 1. First walk the LocalOnly records answering the LocalOnly question
+	// 2. As LocalOnly questions should also be answered by any other Auth records local to the machine,
+	//    walk the ResourceRecords list delivering the answers
+	slot = AuthHashSlot(&q->qname);
+	ag = AuthGroupForName(&m->rrauth, slot, q->qnamehash, &q->qname);
+	if (ag)
+		{
+		m->CurrentRecord = ag->members;
+		while (m->CurrentRecord && m->CurrentRecord != ag->NewLocalOnlyRecords)
+			{
+			AuthRecord *rr = m->CurrentRecord;
+			m->CurrentRecord = rr->next;
+			if (LocalOnlyRecordAnswersQuestion(rr, q))
+				{
+				AnswerLocalQuestionWithLocalAuthRecord(m, rr, mDNStrue);
+				if (m->CurrentQuestion != q) break;		// If callback deleted q, then we're finished here
+				}
+			}
+		}
+
+	if (m->CurrentQuestion == q)
+		{
+		m->CurrentRecord = m->ResourceRecords;
+
+		while (m->CurrentRecord && m->CurrentRecord != m->NewLocalRecords)
+			{
+			AuthRecord *rr = m->CurrentRecord;
+			m->CurrentRecord = rr->next;
+			if (ResourceRecordAnswersQuestion(&rr->resrec, q))
+				{
+				AnswerLocalQuestionWithLocalAuthRecord(m, rr, mDNStrue);
+				if (m->CurrentQuestion != q) break;		// If callback deleted q, then we're finished here
+				}
+			}
+		}
+
+	m->CurrentQuestion = mDNSNULL;
+	m->CurrentRecord   = mDNSNULL;
+	}
+
+mDNSlocal CacheEntity *GetCacheEntity(mDNS *const m, const CacheGroup *const PreserveCG)
+	{
+	CacheEntity *e = mDNSNULL;
+
+	if (m->lock_rrcache) { LogMsg("GetFreeCacheRR ERROR! Cache already locked!"); return(mDNSNULL); }
+	m->lock_rrcache = 1;
+	
+	// If we have no free records, ask the client layer to give us some more memory
+	if (!m->rrcache_free && m->MainCallback)
+		{
+		if (m->rrcache_totalused != m->rrcache_size)
+			LogMsg("GetFreeCacheRR: count mismatch: m->rrcache_totalused %lu != m->rrcache_size %lu",
+				m->rrcache_totalused, m->rrcache_size);
+		
+		// We don't want to be vulnerable to a malicious attacker flooding us with an infinite
+		// number of bogus records so that we keep growing our cache until the machine runs out of memory.
+		// To guard against this, if our cache grows above 512kB (approx 3168 records at 164 bytes each),
+		// and we're actively using less than 1/32 of that cache, then we purge all the unused records
+		// and recycle them, instead of allocating more memory.
+		if (m->rrcache_size > 5000 && m->rrcache_size / 32 > m->rrcache_active)
+			LogInfo("Possible denial-of-service attack in progress: m->rrcache_size %lu; m->rrcache_active %lu",
+				m->rrcache_size, m->rrcache_active);
+		else
+			{
+			mDNS_DropLockBeforeCallback();		// Allow client to legally make mDNS API calls from the callback
+			m->MainCallback(m, mStatus_GrowCache);
+			mDNS_ReclaimLockAfterCallback();	// Decrement mDNS_reentrancy to block mDNS API calls again
+			}
+		}
+	
+	// If we still have no free records, recycle all the records we can.
+	// Enumerating the entire cache is moderately expensive, so when we do it, we reclaim all the records we can in one pass.
+	if (!m->rrcache_free)
+		{
+		mDNSu32 oldtotalused = m->rrcache_totalused;
+		mDNSu32 slot;
+		for (slot = 0; slot < CACHE_HASH_SLOTS; slot++)
+			{
+			CacheGroup **cp = &m->rrcache_hash[slot];
+			while (*cp)
+				{
+				CacheRecord **rp = &(*cp)->members;
+				while (*rp)
+					{
+					// Records that answer still-active questions are not candidates for recycling
+					// Records that are currently linked into the CacheFlushRecords list may not be recycled, or we'll crash
+					if ((*rp)->CRActiveQuestion || (*rp)->NextInCFList)
+						rp=&(*rp)->next;
+					else
+						{
+						CacheRecord *rr = *rp;
+						*rp = (*rp)->next;			// Cut record from list
+						ReleaseCacheRecord(m, rr);
+						}
+					}
+				if ((*cp)->rrcache_tail != rp)
+					verbosedebugf("GetFreeCacheRR: Updating rrcache_tail[%lu] from %p to %p", slot, (*cp)->rrcache_tail, rp);
+				(*cp)->rrcache_tail = rp;
+				if ((*cp)->members || (*cp)==PreserveCG) cp=&(*cp)->next;
+				else ReleaseCacheGroup(m, cp);
+				}
+			}
+		LogInfo("GetCacheEntity recycled %d records to reduce cache from %d to %d",
+			oldtotalused - m->rrcache_totalused, oldtotalused, m->rrcache_totalused);
+		}
+
+	if (m->rrcache_free)	// If there are records in the free list, take one
+		{
+		e = m->rrcache_free;
+		m->rrcache_free = e->next;
+		if (++m->rrcache_totalused >= m->rrcache_report)
+			{
+			LogInfo("RR Cache now using %ld objects", m->rrcache_totalused);
+			if      (m->rrcache_report <  100) m->rrcache_report += 10;
+			else if (m->rrcache_report < 1000) m->rrcache_report += 100;
+			else                               m->rrcache_report += 1000;
+			}
+		mDNSPlatformMemZero(e, sizeof(*e));
+		}
+
+	m->lock_rrcache = 0;
+
+	return(e);
+	}
+
+mDNSlocal CacheRecord *GetCacheRecord(mDNS *const m, CacheGroup *cg, mDNSu16 RDLength)
+	{
+	CacheRecord *r = (CacheRecord *)GetCacheEntity(m, cg);
+	if (r)
+		{
+		r->resrec.rdata = (RData*)&r->smallrdatastorage;	// By default, assume we're usually going to be using local storage
+		if (RDLength > InlineCacheRDSize)			// If RDLength is too big, allocate extra storage
+			{
+			r->resrec.rdata = (RData*)mDNSPlatformMemAllocate(sizeofRDataHeader + RDLength);
+			if (r->resrec.rdata) r->resrec.rdata->MaxRDLength = r->resrec.rdlength = RDLength;
+			else { ReleaseCacheEntity(m, (CacheEntity*)r); r = mDNSNULL; }
+			}
+		}
+	return(r);
+	}
+
+mDNSlocal CacheGroup *GetCacheGroup(mDNS *const m, const mDNSu32 slot, const ResourceRecord *const rr)
+	{
+	mDNSu16 namelen = DomainNameLength(rr->name);
+	CacheGroup *cg = (CacheGroup*)GetCacheEntity(m, mDNSNULL);
+	if (!cg) { LogMsg("GetCacheGroup: Failed to allocate memory for %##s", rr->name->c); return(mDNSNULL); }
+	cg->next         = m->rrcache_hash[slot];
+	cg->namehash     = rr->namehash;
+	cg->members      = mDNSNULL;
+	cg->rrcache_tail = &cg->members;
+	cg->name         = (domainname*)cg->namestorage;
+	//LogMsg("GetCacheGroup: %-10s %d-byte cache name %##s",
+	//	(namelen > InlineCacheGroupNameSize) ? "Allocating" : "Inline", namelen, rr->name->c);
+	if (namelen > InlineCacheGroupNameSize) cg->name = mDNSPlatformMemAllocate(namelen);
+	if (!cg->name)
+		{
+		LogMsg("GetCacheGroup: Failed to allocate name storage for %##s", rr->name->c);
+		ReleaseCacheEntity(m, (CacheEntity*)cg);
+		return(mDNSNULL);
+		}
+	AssignDomainName(cg->name, rr->name);
+
+	if (CacheGroupForRecord(m, slot, rr)) LogMsg("GetCacheGroup: Already have CacheGroup for %##s", rr->name->c);
+	m->rrcache_hash[slot] = cg;
+	if (CacheGroupForRecord(m, slot, rr) != cg) LogMsg("GetCacheGroup: Not finding CacheGroup for %##s", rr->name->c);
+	
+	return(cg);
+	}
+
+mDNSexport void mDNS_PurgeCacheResourceRecord(mDNS *const m, CacheRecord *rr)
+	{
+	if (m->mDNS_busy != m->mDNS_reentrancy+1)
+		LogMsg("mDNS_PurgeCacheResourceRecord: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
+	// Make sure we mark this record as thoroughly expired -- we don't ever want to give
+	// a positive answer using an expired record (e.g. from an interface that has gone away).
+	// We don't want to clear CRActiveQuestion here, because that would leave the record subject to
+	// summary deletion without giving the proper callback to any questions that are monitoring it.
+	// By setting UnansweredQueries to MaxUnansweredQueries we ensure it won't trigger any further expiration queries.
+	rr->TimeRcvd          = m->timenow - mDNSPlatformOneSecond * 60;
+	rr->UnansweredQueries = MaxUnansweredQueries;
+	rr->resrec.rroriginalttl     = 0;
+	SetNextCacheCheckTimeForRecord(m, rr);
+	}
+
+mDNSexport mDNSs32 mDNS_TimeNow(const mDNS *const m)
+	{
+	mDNSs32 time;
+	mDNSPlatformLock(m);
+	if (m->mDNS_busy)
+		{
+		LogMsg("mDNS_TimeNow called while holding mDNS lock. This is incorrect. Code protected by lock should just use m->timenow.");
+		if (!m->timenow) LogMsg("mDNS_TimeNow: m->mDNS_busy is %ld but m->timenow not set", m->mDNS_busy);
+		}
+	
+	if (m->timenow) time = m->timenow;
+	else            time = mDNS_TimeNow_NoLock(m);
+	mDNSPlatformUnlock(m);
+	return(time);
+	}
+
+// To avoid pointless CPU thrash, we use SetSPSProxyListChanged(X) to record the last interface that
+// had its Sleep Proxy client list change, and defer to actual BPF reconfiguration to mDNS_Execute().
+// (GetNextScheduledEvent() returns "now" when m->SPSProxyListChanged is set)
+#define SetSPSProxyListChanged(X) do { \
+	if (m->SPSProxyListChanged && m->SPSProxyListChanged != (X)) mDNSPlatformUpdateProxyList(m, m->SPSProxyListChanged); \
+	m->SPSProxyListChanged = (X); } while(0)
+
+// Called from mDNS_Execute() to expire stale proxy records
+mDNSlocal void CheckProxyRecords(mDNS *const m, AuthRecord *list)
+	{
+	m->CurrentRecord = list;
+	while (m->CurrentRecord)
+		{
+		AuthRecord *rr = m->CurrentRecord;
+		if (rr->resrec.RecordType != kDNSRecordTypeDeregistering && rr->WakeUp.HMAC.l[0])
+			{
+			// If m->SPSSocket is NULL that means we're not acting as a sleep proxy any more,
+			// so we need to cease proxying for *all* records we may have, expired or not.
+			if (m->SPSSocket && m->timenow - rr->TimeExpire < 0)	// If proxy record not expired yet, update m->NextScheduledSPS
+				{
+				if (m->NextScheduledSPS - rr->TimeExpire > 0)
+					m->NextScheduledSPS = rr->TimeExpire;
+				}
+			else													// else proxy record expired, so remove it
+				{
+				LogSPS("CheckProxyRecords: Removing %d H-MAC %.6a I-MAC %.6a %d %s",
+					m->ProxyRecords, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, rr->WakeUp.seq, ARDisplayString(m, rr));
+				SetSPSProxyListChanged(rr->resrec.InterfaceID);
+				mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal);
+				// Don't touch rr after this -- memory may have been free'd
+				}
+			}
+		// Mustn't advance m->CurrentRecord until *after* mDNS_Deregister_internal, because
+		// new records could have been added to the end of the list as a result of that call.
+		if (m->CurrentRecord == rr) // If m->CurrentRecord was not advanced for us, do it now
+			m->CurrentRecord = rr->next;
+		}
+	}
+
+mDNSlocal void CheckRmvEventsForLocalRecords(mDNS *const m)
+	{
+	while (m->CurrentRecord)
+		{
+		AuthRecord *rr = m->CurrentRecord;
+		if (rr->AnsweredLocalQ && rr->resrec.RecordType == kDNSRecordTypeDeregistering)
+			{
+			debugf("CheckRmvEventsForLocalRecords: Generating local RMV events for %s", ARDisplayString(m, rr));
+			rr->resrec.RecordType = kDNSRecordTypeShared;
+			AnswerAllLocalQuestionsWithLocalAuthRecord(m, rr, mDNSfalse);
+			if (m->CurrentRecord == rr)	// If rr still exists in list, restore its state now
+				{
+				rr->resrec.RecordType = kDNSRecordTypeDeregistering;
+				rr->AnsweredLocalQ = mDNSfalse;
+				// SendResponses normally calls CompleteDeregistration after sending goodbyes.
+				// For LocalOnly records, we don't do that and hence we need to do that here.
+				if (RRLocalOnly(rr)) CompleteDeregistration(m, rr);
+				}
+			}
+		if (m->CurrentRecord == rr)		// If m->CurrentRecord was not auto-advanced, do it ourselves now
+			m->CurrentRecord = rr->next;
+		}
+	}
+
+mDNSlocal void TimeoutQuestions(mDNS *const m)
+	{
+	m->NextScheduledStopTime = m->timenow + 0x3FFFFFFF;
+	if (m->CurrentQuestion)
+		LogMsg("TimeoutQuestions ERROR m->CurrentQuestion already set: %##s (%s)", m->CurrentQuestion->qname.c,
+			DNSTypeName(m->CurrentQuestion->qtype));
+	m->CurrentQuestion = m->Questions;
+	while (m->CurrentQuestion)
+		{
+		DNSQuestion *const q = m->CurrentQuestion;
+		if (q->StopTime)
+			{
+			if (m->timenow - q->StopTime >= 0)
+				{
+				LogInfo("TimeoutQuestions: question %##s timed out, time %d", q->qname.c, m->timenow - q->StopTime);
+				GenerateNegativeResponse(m);
+				if (m->CurrentQuestion == q) q->StopTime = 0;
+				}
+			else
+				{
+				if (m->NextScheduledStopTime - q->StopTime > 0)
+					m->NextScheduledStopTime = q->StopTime;
+				}
+			}
+		// If m->CurrentQuestion wasn't modified out from under us, advance it now
+		// We can't do this at the start of the loop because GenerateNegativeResponse
+		// depends on having m->CurrentQuestion point to the right question
+		if (m->CurrentQuestion == q)
+			m->CurrentQuestion = q->next;
+		}
+	m->CurrentQuestion = mDNSNULL;
+	}
+
+mDNSexport mDNSs32 mDNS_Execute(mDNS *const m)
+	{
+	mDNS_Lock(m);	// Must grab lock before trying to read m->timenow
+
+	if (m->timenow - m->NextScheduledEvent >= 0)
+		{
+		int i;
+		AuthRecord *head, *tail;
+		mDNSu32 slot;
+		AuthGroup *ag;
+
+		verbosedebugf("mDNS_Execute");
+
+		if (m->CurrentQuestion)
+			LogMsg("mDNS_Execute: ERROR m->CurrentQuestion already set: %##s (%s)",
+				m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype));
+	
+		if (m->CurrentRecord)
+			LogMsg("mDNS_Execute: ERROR m->CurrentRecord already set: %s", ARDisplayString(m, m->CurrentRecord));
+	
+		// 1. If we're past the probe suppression time, we can clear it
+		if (m->SuppressProbes && m->timenow - m->SuppressProbes >= 0) m->SuppressProbes = 0;
+	
+		// 2. If it's been more than ten seconds since the last probe failure, we can clear the counter
+		if (m->NumFailedProbes && m->timenow - m->ProbeFailTime >= mDNSPlatformOneSecond * 10) m->NumFailedProbes = 0;
+		
+		// 3. Purge our cache of stale old records
+		if (m->rrcache_size && m->timenow - m->NextCacheCheck >= 0)
+			{
+			mDNSu32 numchecked = 0;
+			m->NextCacheCheck = m->timenow + 0x3FFFFFFF;
+			for (slot = 0; slot < CACHE_HASH_SLOTS; slot++)
+				{
+				if (m->timenow - m->rrcache_nextcheck[slot] >= 0)
+					{
+					CacheGroup **cp = &m->rrcache_hash[slot];
+					m->rrcache_nextcheck[slot] = m->timenow + 0x3FFFFFFF;
+					while (*cp)
+						{
+						debugf("m->NextCacheCheck %4d Slot %3d %##s", numchecked, slot, *cp ? (*cp)->name : (domainname*)"\x04NULL");
+						numchecked++;
+						CheckCacheExpiration(m, slot, *cp);
+						if ((*cp)->members) cp=&(*cp)->next;
+						else ReleaseCacheGroup(m, cp);
+						}
+					}
+				// Even if we didn't need to actually check this slot yet, still need to
+				// factor its nextcheck time into our overall NextCacheCheck value
+				if (m->NextCacheCheck - m->rrcache_nextcheck[slot] > 0)
+					m->NextCacheCheck = m->rrcache_nextcheck[slot];
+				}
+			debugf("m->NextCacheCheck %4d checked, next in %d", numchecked, m->NextCacheCheck - m->timenow);
+			}
+	
+		if (m->timenow - m->NextScheduledSPS >= 0)
+			{
+			m->NextScheduledSPS = m->timenow + 0x3FFFFFFF;
+			CheckProxyRecords(m, m->DuplicateRecords);	// Clear m->DuplicateRecords first, then m->ResourceRecords
+			CheckProxyRecords(m, m->ResourceRecords);
+			}
+
+		SetSPSProxyListChanged(mDNSNULL);		// Perform any deferred BPF reconfiguration now
+
+		// Clear AnnounceOwner if necessary. (Do this *before* SendQueries() and SendResponses().)
+		if (m->AnnounceOwner && m->timenow - m->AnnounceOwner >= 0) m->AnnounceOwner = 0;
+
+		if (m->DelaySleep && m->timenow - m->DelaySleep >= 0)
+			{
+			m->DelaySleep = 0;
+			if (m->SleepState == SleepState_Transferring)
+				{
+				LogSPS("Re-sleep delay passed; now checking for Sleep Proxy Servers");
+				BeginSleepProcessing(m);
+				}
+			}
+
+		// 4. See if we can answer any of our new local questions from the cache
+		for (i=0; m->NewQuestions && i<1000; i++)
+			{
+			if (m->NewQuestions->DelayAnswering && m->timenow - m->NewQuestions->DelayAnswering < 0) break;
+			AnswerNewQuestion(m);
+			}
+		if (i >= 1000) LogMsg("mDNS_Execute: AnswerNewQuestion exceeded loop limit");
+
+		// Make sure we deliver *all* local RMV events, and clear the corresponding rr->AnsweredLocalQ flags, *before*
+		// we begin generating *any* new ADD events in the m->NewLocalOnlyQuestions and m->NewLocalRecords loops below.
+		for (i=0; i<1000 && m->LocalRemoveEvents; i++)
+			{
+			m->LocalRemoveEvents = mDNSfalse;
+			m->CurrentRecord = m->ResourceRecords;
+			CheckRmvEventsForLocalRecords(m);
+			// Walk the LocalOnly records and deliver the RMV events
+			for (slot = 0; slot < AUTH_HASH_SLOTS; slot++)
+				for (ag = m->rrauth.rrauth_hash[slot]; ag; ag = ag->next)
+					{
+					m->CurrentRecord = ag->members;
+					if (m->CurrentRecord) CheckRmvEventsForLocalRecords(m);
+					}
+			}
+
+		if (i >= 1000) LogMsg("mDNS_Execute: m->LocalRemoveEvents exceeded loop limit");
+				
+		for (i=0; m->NewLocalOnlyQuestions && i<1000; i++) AnswerNewLocalOnlyQuestion(m);
+		if (i >= 1000) LogMsg("mDNS_Execute: AnswerNewLocalOnlyQuestion exceeded loop limit");
+
+		head = tail = mDNSNULL;
+		for (i=0; i<1000 && m->NewLocalRecords && m->NewLocalRecords != head; i++)
+			{
+			AuthRecord *rr = m->NewLocalRecords;
+			m->NewLocalRecords = m->NewLocalRecords->next;
+			if (LocalRecordReady(rr))
+				{
+				debugf("mDNS_Execute: Delivering Add event with LocalAuthRecord %s", ARDisplayString(m, rr));
+				AnswerAllLocalQuestionsWithLocalAuthRecord(m, rr, mDNStrue);
+				}
+			else if (!rr->next)
+				{
+				// If we have just one record that is not ready, we don't have to unlink and
+				// reinsert. As the NewLocalRecords will be NULL for this case, the loop will
+				// terminate and set the NewLocalRecords to rr.
+				debugf("mDNS_Execute: Just one LocalAuthRecord %s, breaking out of the loop early", ARDisplayString(m, rr));
+				if (head != mDNSNULL || m->NewLocalRecords != mDNSNULL)
+					LogMsg("mDNS_Execute: ERROR!!: head %p, NewLocalRecords %p", head, m->NewLocalRecords);
+					
+				head = rr;
+				}
+			else
+				{
+				AuthRecord **p = &m->ResourceRecords;	// Find this record in our list of active records
+				debugf("mDNS_Execute: Skipping LocalAuthRecord %s", ARDisplayString(m, rr));
+				// if this is the first record we are skipping, move to the end of the list.
+				// if we have already skipped records before, append it at the end.
+				while (*p && *p != rr) p=&(*p)->next;
+				if (*p) *p = rr->next;					// Cut this record from the list
+				else { LogMsg("mDNS_Execute: ERROR!! Cannot find record %s in ResourceRecords list", ARDisplayString(m, rr)); break; }
+				if (!head)
+					{
+					while (*p) p=&(*p)->next;
+					*p = rr;
+					head = tail = rr;
+					}
+				else
+					{
+					tail->next = rr;
+					tail = rr;
+					}
+				rr->next = mDNSNULL;
+				}
+			}
+		m->NewLocalRecords = head;
+		// debugf("mDNS_Execute: Setting NewLocalRecords to %s", (head ? ARDisplayString(m, head) : "NULL"));
+
+		if (i >= 1000) LogMsg("mDNS_Execute: m->NewLocalRecords exceeded loop limit");
+
+		// Check to see if we have any new LocalOnly/P2P records to examine for delivering
+		// to our local questions
+		if (m->NewLocalOnlyRecords)
+			{
+			m->NewLocalOnlyRecords = mDNSfalse;
+			for (slot = 0; slot < AUTH_HASH_SLOTS; slot++)
+				for (ag = m->rrauth.rrauth_hash[slot]; ag; ag = ag->next)
+					{
+					for (i=0; i<100 && ag->NewLocalOnlyRecords; i++)
+						{
+						AuthRecord *rr = ag->NewLocalOnlyRecords;
+						ag->NewLocalOnlyRecords = ag->NewLocalOnlyRecords->next;
+						// LocalOnly records should always be ready as they never probe
+						if (LocalRecordReady(rr))
+							{
+							debugf("mDNS_Execute: Delivering Add event with LocalAuthRecord %s", ARDisplayString(m, rr));
+							AnswerAllLocalQuestionsWithLocalAuthRecord(m, rr, mDNStrue);
+							}
+						else LogMsg("mDNS_Execute: LocalOnlyRecord %s not ready", ARDisplayString(m, rr));
+						}
+					// We limit about 100 per AuthGroup that can be serviced at a time
+					if (i >= 100) LogMsg("mDNS_Execute: ag->NewLocalOnlyRecords exceeded loop limit");
+					}
+			}
+
+		// 5. Some questions may have picked a new DNS server and the cache may answer these questions now.
+		AnswerQuestionsForDNSServerChanges(m);
+
+		// 6. See what packets we need to send
+		if (m->mDNSPlatformStatus != mStatus_NoError || (m->SleepState == SleepState_Sleeping))
+			DiscardDeregistrations(m);
+		if (m->mDNSPlatformStatus == mStatus_NoError && (m->SuppressSending == 0 || m->timenow - m->SuppressSending >= 0))
+			{
+			// If the platform code is ready, and we're not suppressing packet generation right now
+			// then send our responses, probes, and questions.
+			// We check the cache first, because there might be records close to expiring that trigger questions to refresh them.
+			// We send queries next, because there might be final-stage probes that complete their probing here, causing
+			// them to advance to announcing state, and we want those to be included in any announcements we send out.
+			// Finally, we send responses, including the previously mentioned records that just completed probing.
+			m->SuppressSending = 0;
+	
+			// 7. Send Query packets. This may cause some probing records to advance to announcing state
+			if (m->timenow - m->NextScheduledQuery >= 0 || m->timenow - m->NextScheduledProbe >= 0) SendQueries(m);
+			if (m->timenow - m->NextScheduledQuery >= 0)
+				{
+				DNSQuestion *q;
+				LogMsg("mDNS_Execute: SendQueries didn't send all its queries (%d - %d = %d) will try again in one second",
+					m->timenow, m->NextScheduledQuery, m->timenow - m->NextScheduledQuery);
+				m->NextScheduledQuery = m->timenow + mDNSPlatformOneSecond;
+				for (q = m->Questions; q && q != m->NewQuestions; q=q->next)
+					if (ActiveQuestion(q) && m->timenow - NextQSendTime(q) >= 0)
+						LogMsg("mDNS_Execute: SendQueries didn't send %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+				}
+			if (m->timenow - m->NextScheduledProbe >= 0)
+				{
+				LogMsg("mDNS_Execute: SendQueries didn't send all its probes (%d - %d = %d) will try again in one second",
+					m->timenow, m->NextScheduledProbe, m->timenow - m->NextScheduledProbe);
+				m->NextScheduledProbe = m->timenow + mDNSPlatformOneSecond;
+				}
+	
+			// 8. Send Response packets, including probing records just advanced to announcing state
+			if (m->timenow - m->NextScheduledResponse >= 0) SendResponses(m);
+			if (m->timenow - m->NextScheduledResponse >= 0)
+				{
+				LogMsg("mDNS_Execute: SendResponses didn't send all its responses; will try again in one second");
+				m->NextScheduledResponse = m->timenow + mDNSPlatformOneSecond;
+				}
+			}
+
+		// Clear RandomDelay values, ready to pick a new different value next time
+		m->RandomQueryDelay     = 0;
+		m->RandomReconfirmDelay = 0;
+
+		if (m->NextScheduledStopTime && m->timenow - m->NextScheduledStopTime >= 0) TimeoutQuestions(m);
+#ifndef UNICAST_DISABLED
+		if (m->NextSRVUpdate && m->timenow - m->NextSRVUpdate >= 0) UpdateAllSRVRecords(m);
+		if (m->timenow - m->NextScheduledNATOp >= 0) CheckNATMappings(m);
+		if (m->timenow - m->NextuDNSEvent >= 0) uDNS_Tasks(m);
+#endif
+		}
+
+	// Note about multi-threaded systems:
+	// On a multi-threaded system, some other thread could run right after the mDNS_Unlock(),
+	// performing mDNS API operations that change our next scheduled event time.
+	//
+	// On multi-threaded systems (like the current Windows implementation) that have a single main thread
+	// calling mDNS_Execute() (and other threads allowed to call mDNS API routines) it is the responsibility
+	// of the mDNSPlatformUnlock() routine to signal some kind of stateful condition variable that will
+	// signal whatever blocking primitive the main thread is using, so that it will wake up and execute one
+	// more iteration of its loop, and immediately call mDNS_Execute() again. The signal has to be stateful
+	// in the sense that if the main thread has not yet entered its blocking primitive, then as soon as it
+	// does, the state of the signal will be noticed, causing the blocking primitive to return immediately
+	// without blocking. This avoids the race condition between the signal from the other thread arriving
+	// just *before* or just *after* the main thread enters the blocking primitive.
+	//
+	// On multi-threaded systems (like the current Mac OS 9 implementation) that are entirely timer-driven,
+	// with no main mDNS_Execute() thread, it is the responsibility of the mDNSPlatformUnlock() routine to
+	// set the timer according to the m->NextScheduledEvent value, and then when the timer fires, the timer
+	// callback function should call mDNS_Execute() (and ignore the return value, which may already be stale
+	// by the time it gets to the timer callback function).
+
+	mDNS_Unlock(m);		// Calling mDNS_Unlock is what gives m->NextScheduledEvent its new value
+	return(m->NextScheduledEvent);
+	}
+
+mDNSlocal void SuspendLLQs(mDNS *m)
+	{
+	DNSQuestion *q;
+	for (q = m->Questions; q; q = q->next)
+		if (ActiveQuestion(q) && !mDNSOpaque16IsZero(q->TargetQID) && q->LongLived && q->state == LLQ_Established)
+			{ q->ReqLease = 0; sendLLQRefresh(m, q); }
+	}
+
+mDNSlocal mDNSBool QuestionHasLocalAnswers(mDNS *const m, DNSQuestion *q)
+	{
+	AuthRecord *rr;
+	mDNSu32 slot;
+	AuthGroup *ag;
+
+	slot = AuthHashSlot(&q->qname);
+	ag = AuthGroupForName(&m->rrauth, slot, q->qnamehash, &q->qname);
+	if (ag)
+		{
+		for (rr = ag->members; rr; rr=rr->next)
+			// Filter the /etc/hosts records - LocalOnly, Unique, A/AAAA/CNAME
+			if (LORecordAnswersAddressType(rr) && LocalOnlyRecordAnswersQuestion(rr, q))
+				{
+				LogInfo("QuestionHasLocalAnswers: Question %p %##s (%s) has local answer %s", q, q->qname.c, DNSTypeName(q->qtype), ARDisplayString(m, rr));
+				return mDNStrue;
+				}
+		}
+	return mDNSfalse;
+	}
+
+// ActivateUnicastQuery() is called from three places:
+// 1. When a new question is created
+// 2. On wake from sleep
+// 3. When the DNS configuration changes
+// In case 1 we don't want to mess with our established ThisQInterval and LastQTime (ScheduleImmediately is false)
+// In cases 2 and 3 we do want to cause the question to be resent immediately (ScheduleImmediately is true)
+mDNSlocal void ActivateUnicastQuery(mDNS *const m, DNSQuestion *const question, mDNSBool ScheduleImmediately)
+	{
+	// For now this AutoTunnel stuff is specific to Mac OS X.
+	// In the future, if there's demand, we may see if we can abstract it out cleanly into the platform layer
+#if APPLE_OSX_mDNSResponder
+	// Even though BTMM client tunnels are only useful for AAAA queries, we need to treat v4 and v6 queries equally.
+	// Otherwise we can get the situation where the A query completes really fast (with an NXDOMAIN result) and the
+	// caller then gives up waiting for the AAAA result while we're still in the process of setting up the tunnel.
+	// To level the playing field, we block both A and AAAA queries while tunnel setup is in progress, and then
+	// returns results for both at the same time. If we are looking for the _autotunnel6 record, then skip this logic
+	// as this would trigger looking up _autotunnel6._autotunnel6 and end up failing the original query.
+
+	if (RRTypeIsAddressType(question->qtype) && PrivateQuery(question) &&
+		!SameDomainLabel(question->qname.c, (const mDNSu8 *)"\x0c_autotunnel6")&& question->QuestionCallback != AutoTunnelCallback)
+		{
+		question->NoAnswer = NoAnswer_Suspended;
+		AddNewClientTunnel(m, question);
+		return;
+		}
+#endif // APPLE_OSX_mDNSResponder
+
+	if (!question->DuplicateOf)
+		{
+		debugf("ActivateUnicastQuery: %##s %s%s%s",
+			question->qname.c, DNSTypeName(question->qtype), PrivateQuery(question) ? " (Private)" : "", ScheduleImmediately ? " ScheduleImmediately" : "");
+		question->CNAMEReferrals = 0;
+		if (question->nta) { CancelGetZoneData(m, question->nta); question->nta = mDNSNULL; }
+		if (question->LongLived)
+			{
+			question->state = LLQ_InitialRequest;
+			question->id = zeroOpaque64;
+			question->servPort = zeroIPPort;
+			if (question->tcp) { DisposeTCPConn(question->tcp); question->tcp = mDNSNULL; }
+			}
+		// If the question has local answers, then we don't want answers from outside
+		if (ScheduleImmediately && !QuestionHasLocalAnswers(m, question))
+			{
+			question->ThisQInterval = InitialQuestionInterval;
+			question->LastQTime     = m->timenow - question->ThisQInterval;
+			SetNextQueryTime(m, question);
+			}
+		}
+	}
+
+// Caller should hold the lock
+mDNSexport void mDNSCoreRestartAddressQueries(mDNS *const m, mDNSBool SearchDomainsChanged, FlushCache flushCacheRecords,
+	CallbackBeforeStartQuery BeforeStartCallback, void *context)
+	{
+	DNSQuestion *q;
+	DNSQuestion *restart = mDNSNULL;
+
+	if (!m->mDNS_busy) LogMsg("mDNSCoreRestartAddressQueries: ERROR!! Lock not held");
+
+	// 1. Flush the cache records
+	if (flushCacheRecords) flushCacheRecords(m);
+
+	// 2. Even though we may have purged the cache records above, before it can generate RMV event
+	// we are going to stop the question. Hence we need to deliver the RMV event before we
+	// stop the question.
+	//
+	// CurrentQuestion is used by RmvEventsForQuestion below. While delivering RMV events, the
+	// application callback can potentially stop the current question (detected by CurrentQuestion) or
+	// *any* other question which could be the next one that we may process here. RestartQuestion
+	// points to the "next" question which will be automatically advanced in mDNS_StopQuery_internal
+	// if the "next" question is stopped while the CurrentQuestion is stopped
+
+	if (m->RestartQuestion)
+		LogMsg("mDNSCoreRestartAddressQueries: ERROR!! m->RestartQuestion already set: %##s (%s)",
+			m->RestartQuestion->qname.c, DNSTypeName(m->RestartQuestion->qtype));
+
+	m->RestartQuestion = m->Questions;
+	while (m->RestartQuestion)
+		{
+		q = m->RestartQuestion;
+		m->RestartQuestion = q->next;
+		// GetZoneData questions are referenced by other questions (original query that started the GetZoneData
+		// question)  through their "nta" pointer. Normally when the original query stops, it stops the
+		// GetZoneData question and also frees the memory (See CancelGetZoneData). If we stop the GetZoneData
+		// question followed by the original query that refers to this GetZoneData question, we will end up
+		// freeing the GetZoneData question and then start the "freed" question at the end. 
+
+		if (IsGetZoneDataQuestion(q))
+			{
+			DNSQuestion *refq = q->next;
+			LogInfo("mDNSCoreRestartAddressQueries: Skipping GetZoneDataQuestion %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype));
+			// debug stuff, we just try to find the referencing question and don't do much with it
+			while (refq)
+				{
+				if (q == &refq->nta->question)
+					{
+					LogInfo("mDNSCoreRestartAddressQueries: Question %p %##s (%s) referring to GetZoneDataQuestion %p, not stopping", refq, refq->qname.c, DNSTypeName(refq->qtype), q);
+					}
+				refq = refq->next;
+				}
+			continue;
+			}
+
+		// This function is called when /etc/hosts changes and that could affect A, AAAA and CNAME queries
+		if (q->qtype != kDNSType_A && q->qtype != kDNSType_AAAA && q->qtype != kDNSType_CNAME) continue;
+
+		// If the search domains did not change, then we restart all the queries. Otherwise, only
+		// for queries for which we "might" have appended search domains ("might" because we may
+		// find results before we apply search domains even though AppendSearchDomains is set to 1)
+		if (!SearchDomainsChanged || q->AppendSearchDomains)
+			{
+			// NOTE: CacheRecordRmvEventsForQuestion will not generate RMV events for queries that have non-zero
+			// LOAddressAnswers. Hence it is important that we call CacheRecordRmvEventsForQuestion before
+			// LocalRecordRmvEventsForQuestion (which decrements LOAddressAnswers). Let us say that
+			// /etc/hosts has an A Record for web.apple.com. Any queries for web.apple.com will be answered locally.
+			// But this can't prevent a CNAME/AAAA query to not to be sent on the wire. When it is sent on the wire,
+			// it could create cache entries. When we are restarting queries, we can't deliver the cache RMV events
+			// for the original query using these cache entries as ADDs were never delivered using these cache
+			// entries and hence this order is needed.
+
+			// If the query is suppressed, the RMV events won't be delivered
+			if (!CacheRecordRmvEventsForQuestion(m, q)) { LogInfo("mDNSCoreRestartAddressQueries: Question deleted while delivering Cache Record RMV events"); continue; }
+
+			// SuppressQuery status does not affect questions that are answered using local records
+			if (!LocalRecordRmvEventsForQuestion(m, q)) { LogInfo("mDNSCoreRestartAddressQueries: Question deleted while delivering Local Record RMV events"); continue; }
+
+			LogInfo("mDNSCoreRestartAddressQueries: Stop question %p %##s (%s), AppendSearchDomains %d, qnameOrig %p", q,
+				q->qname.c, DNSTypeName(q->qtype), q->AppendSearchDomains, q->qnameOrig);
+			mDNS_StopQuery_internal(m, q);
+			// Reset state so that it looks like it was in the beginning i.e it should look at /etc/hosts, cache
+			// and then search domains should be appended. At the beginning, qnameOrig was NULL.
+			if (q->qnameOrig)
+				{
+				LogInfo("mDNSCoreRestartAddressQueries: qnameOrig %##s", q->qnameOrig);
+				AssignDomainName(&q->qname, q->qnameOrig);
+				mDNSPlatformMemFree(q->qnameOrig);
+				q->qnameOrig = mDNSNULL;
+				q->RetryWithSearchDomains = ApplySearchDomainsFirst(q) ? 1 : 0;
+				}
+			q->SearchListIndex = 0;
+			q->next = restart;
+			restart = q;
+			}
+		}
+
+	// 3. Callback before we start the query
+	if (BeforeStartCallback) BeforeStartCallback(m, context);
+
+	// 4. Restart all the stopped queries
+	while (restart)
+		{
+		q = restart;
+		restart = restart->next;
+		q->next = mDNSNULL;
+		LogInfo("mDNSCoreRestartAddressQueries: Start question %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype));
+		mDNS_StartQuery_internal(m, q);
+		}
+	}
+
+mDNSexport void mDNSCoreRestartQueries(mDNS *const m)
+	{
+	DNSQuestion *q;
+
+#ifndef UNICAST_DISABLED
+	// Retrigger all our uDNS questions
+	if (m->CurrentQuestion)
+		LogMsg("mDNSCoreRestartQueries: ERROR m->CurrentQuestion already set: %##s (%s)",
+			m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype));
+	m->CurrentQuestion = m->Questions;
+	while (m->CurrentQuestion)
+		{
+		q = m->CurrentQuestion;
+		m->CurrentQuestion = m->CurrentQuestion->next;
+		if (!mDNSOpaque16IsZero(q->TargetQID) && ActiveQuestion(q)) ActivateUnicastQuery(m, q, mDNStrue);
+		}
+#endif
+
+	// Retrigger all our mDNS questions
+	for (q = m->Questions; q; q=q->next)				// Scan our list of questions
+		if (mDNSOpaque16IsZero(q->TargetQID) && ActiveQuestion(q))
+			{
+			q->ThisQInterval    = InitialQuestionInterval;	// MUST be > zero for an active question
+			q->RequestUnicast   = 2;						// Set to 2 because is decremented once *before* we check it
+			q->LastQTime        = m->timenow - q->ThisQInterval;
+			q->RecentAnswerPkts = 0;
+			ExpireDupSuppressInfo(q->DupSuppress, m->timenow);
+			m->NextScheduledQuery = m->timenow;
+			}
+	}
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - Power Management (Sleep/Wake)
+#endif
+
+mDNSexport void mDNS_UpdateAllowSleep(mDNS *const m)
+	{
+#ifndef IDLESLEEPCONTROL_DISABLED
+	mDNSBool allowSleep = mDNStrue;
+	char     reason[128];
+	
+	reason[0] = 0;
+	
+	if (m->SystemSleepOnlyIfWakeOnLAN)
+		{
+		// Don't sleep if we are a proxy for any services
+		if (m->ProxyRecords)
+			{
+			allowSleep = mDNSfalse;
+			mDNS_snprintf(reason, sizeof(reason), "sleep proxy for %d records", m->ProxyRecords);
+			LogInfo("Sleep disabled because we are proxying %d records", m->ProxyRecords);
+			}
+
+		if (allowSleep && mDNSCoreHaveAdvertisedMulticastServices(m))
+			{
+			// Scan the list of active interfaces
+			NetworkInterfaceInfo *intf;
+			for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next))
+				{
+				if (intf->McastTxRx && !intf->Loopback)
+					{
+					// Disallow sleep if this interface doesn't support NetWake
+					if (!intf->NetWake)
+						{
+						allowSleep = mDNSfalse;
+						mDNS_snprintf(reason, sizeof(reason), "%s does not support NetWake", intf->ifname);
+						LogInfo("Sleep disabled because %s does not support NetWake", intf->ifname);
+						break;
+						}
+
+					// Disallow sleep if there is no sleep proxy server
+					if (FindSPSInCache1(m, &intf->NetWakeBrowse, mDNSNULL, mDNSNULL) == mDNSNULL)
+						{
+						allowSleep = mDNSfalse;
+						mDNS_snprintf(reason, sizeof(reason), "%s does not support NetWake", intf->ifname);
+						LogInfo("Sleep disabled because %s has no sleep proxy", intf->ifname);
+						break;
+						}
+					}
+				}
+			}
+		}
+	
+	// Call the platform code to enable/disable sleep
+	mDNSPlatformSetAllowSleep(m, allowSleep, reason);
+#endif /* !defined(IDLESLEEPCONTROL_DISABLED) */
+	}
+
+mDNSlocal void SendSPSRegistrationForOwner(mDNS *const m, NetworkInterfaceInfo *const intf, const mDNSOpaque16 id, const OwnerOptData *const owner)
+	{
+	const int optspace = DNSOpt_Header_Space + DNSOpt_LeaseData_Space + DNSOpt_Owner_Space(&m->PrimaryMAC, &intf->MAC);
+	const int sps = intf->NextSPSAttempt / 3;
+	AuthRecord *rr;
+
+	if (!intf->SPSAddr[sps].type)
+		{
+		intf->NextSPSAttemptTime = m->timenow + mDNSPlatformOneSecond;
+		if (m->NextScheduledSPRetry - intf->NextSPSAttemptTime > 0)
+			m->NextScheduledSPRetry = intf->NextSPSAttemptTime;
+		LogSPS("SendSPSRegistration: %s SPS %d (%d) %##s not yet resolved", intf->ifname, intf->NextSPSAttempt, sps, intf->NetWakeResolve[sps].qname.c);
+		goto exit;
+		}
+
+	// Mark our mDNS records (not unicast records) for transfer to SPS
+	if (mDNSOpaque16IsZero(id))
+		for (rr = m->ResourceRecords; rr; rr=rr->next)
+			if (rr->resrec.RecordType > kDNSRecordTypeDeregistering)
+				if (rr->resrec.InterfaceID == intf->InterfaceID || (!rr->resrec.InterfaceID && (rr->ForceMCast || IsLocalDomain(rr->resrec.name))))
+					if (mDNSPlatformMemSame(owner, &rr->WakeUp, sizeof(*owner)))
+						rr->SendRNow = mDNSInterfaceMark;	// mark it now
+
+	while (1)
+		{
+		mDNSu8 *p = m->omsg.data;
+		// To comply with RFC 2782, PutResourceRecord suppresses name compression for SRV records in unicast updates.
+		// For now we follow that same logic for SPS registrations too.
+		// If we decide to compress SRV records in SPS registrations in the future, we can achieve that by creating our
+		// initial DNSMessage with h.flags set to zero, and then update it to UpdateReqFlags right before sending the packet.
+		InitializeDNSMessage(&m->omsg.h, mDNSOpaque16IsZero(id) ? mDNS_NewMessageID(m) : id, UpdateReqFlags);
+
+		for (rr = m->ResourceRecords; rr; rr=rr->next)
+			if (rr->SendRNow || (!mDNSOpaque16IsZero(id) && !AuthRecord_uDNS(rr) && mDNSSameOpaque16(rr->updateid, id) && m->timenow - (rr->LastAPTime + rr->ThisAPInterval) >= 0))
+				if (mDNSPlatformMemSame(owner, &rr->WakeUp, sizeof(*owner)))
+					{
+					mDNSu8 *newptr;
+					const mDNSu8 *const limit = m->omsg.data + (m->omsg.h.mDNS_numUpdates ? NormalMaxDNSMessageData : AbsoluteMaxDNSMessageData) - optspace;
+					if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask)
+						rr->resrec.rrclass |= kDNSClass_UniqueRRSet;	// Temporarily set the 'unique' bit so PutResourceRecord will set it
+					newptr = PutResourceRecordTTLWithLimit(&m->omsg, p, &m->omsg.h.mDNS_numUpdates, &rr->resrec, rr->resrec.rroriginalttl, limit);
+					rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet;		// Make sure to clear 'unique' bit back to normal state
+					if (!newptr)
+						LogSPS("SendSPSRegistration put %s FAILED %d/%d %s", intf->ifname, p - m->omsg.data, limit - m->omsg.data, ARDisplayString(m, rr));
+					else
+						{
+						LogSPS("SendSPSRegistration put %s %s", intf->ifname, ARDisplayString(m, rr));
+						rr->SendRNow       = mDNSNULL;
+						rr->ThisAPInterval = mDNSPlatformOneSecond;
+						rr->LastAPTime     = m->timenow;
+						rr->updateid       = m->omsg.h.id;
+						if (m->NextScheduledResponse - (rr->LastAPTime + rr->ThisAPInterval) >= 0)
+							m->NextScheduledResponse = (rr->LastAPTime + rr->ThisAPInterval);
+						p = newptr;
+						}
+					}
+
+		if (!m->omsg.h.mDNS_numUpdates) break;
+		else
+			{
+			AuthRecord opt;
+			mDNS_SetupResourceRecord(&opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
+			opt.resrec.rrclass    = NormalMaxDNSMessageData;
+			opt.resrec.rdlength   = sizeof(rdataOPT) * 2;	// Two options in this OPT record
+			opt.resrec.rdestimate = sizeof(rdataOPT) * 2;
+			opt.resrec.rdata->u.opt[0].opt           = kDNSOpt_Lease;
+			opt.resrec.rdata->u.opt[0].optlen        = DNSOpt_LeaseData_Space - 4;
+			opt.resrec.rdata->u.opt[0].u.updatelease = DEFAULT_UPDATE_LEASE;
+			if (!owner->HMAC.l[0])											// If no owner data,
+				SetupOwnerOpt(m, intf, &opt.resrec.rdata->u.opt[1]);		// use our own interface information
+			else															// otherwise, use the owner data we were given
+				{
+				opt.resrec.rdata->u.opt[1].u.owner = *owner;
+				opt.resrec.rdata->u.opt[1].opt     = kDNSOpt_Owner;
+				opt.resrec.rdata->u.opt[1].optlen  = DNSOpt_Owner_Space(&owner->HMAC, &owner->IMAC) - 4;
+				}
+			LogSPS("SendSPSRegistration put %s %s", intf->ifname, ARDisplayString(m, &opt));
+			p = PutResourceRecordTTLWithLimit(&m->omsg, p, &m->omsg.h.numAdditionals, &opt.resrec, opt.resrec.rroriginalttl, m->omsg.data + AbsoluteMaxDNSMessageData);
+			if (!p)
+				LogMsg("SendSPSRegistration: Failed to put OPT record (%d updates) %s", m->omsg.h.mDNS_numUpdates, ARDisplayString(m, &opt));
+			else
+				{
+				mStatus err;
+
+				LogSPS("SendSPSRegistration: Sending Update %s %d (%d) id %5d with %d records %d bytes to %#a:%d", intf->ifname, intf->NextSPSAttempt, sps,
+					mDNSVal16(m->omsg.h.id), m->omsg.h.mDNS_numUpdates, p - m->omsg.data, &intf->SPSAddr[sps], mDNSVal16(intf->SPSPort[sps]));
+				// if (intf->NextSPSAttempt < 5) m->omsg.h.flags = zeroID;	// For simulating packet loss
+				err = mDNSSendDNSMessage(m, &m->omsg, p, intf->InterfaceID, mDNSNULL, &intf->SPSAddr[sps], intf->SPSPort[sps], mDNSNULL, mDNSNULL);
+				if (err) LogSPS("SendSPSRegistration: mDNSSendDNSMessage err %d", err);
+				if (err && intf->SPSAddr[sps].type == mDNSAddrType_IPv6 && intf->NetWakeResolve[sps].ThisQInterval == -1)
+					{
+					LogSPS("SendSPSRegistration %d %##s failed to send to IPv6 address; will try IPv4 instead", sps, intf->NetWakeResolve[sps].qname.c);
+					intf->NetWakeResolve[sps].qtype = kDNSType_A;
+					mDNS_StartQuery_internal(m, &intf->NetWakeResolve[sps]);
+					return;
+					}
+				}
+			}
+		}
+
+	intf->NextSPSAttemptTime = m->timenow + mDNSPlatformOneSecond * 10;		// If successful, update NextSPSAttemptTime
+
+exit:
+	if (mDNSOpaque16IsZero(id) && intf->NextSPSAttempt < 8) intf->NextSPSAttempt++;
+	}
+
+mDNSlocal mDNSBool RecordIsFirstOccurrenceOfOwner(mDNS *const m, const AuthRecord *const rr)
+	{
+	AuthRecord *ar;
+	for (ar = m->ResourceRecords; ar && ar != rr; ar=ar->next)
+		if (mDNSPlatformMemSame(&rr->WakeUp, &ar->WakeUp, sizeof(rr->WakeUp))) return mDNSfalse;
+	return mDNStrue;
+	}
+
+mDNSlocal void SendSPSRegistration(mDNS *const m, NetworkInterfaceInfo *const intf, const mDNSOpaque16 id)
+	{
+	AuthRecord *ar;
+	OwnerOptData owner = zeroOwner;
+
+	SendSPSRegistrationForOwner(m, intf, id, &owner);
+
+	for (ar = m->ResourceRecords; ar; ar=ar->next)
+		{
+		if (!mDNSPlatformMemSame(&owner, &ar->WakeUp, sizeof(owner)) && RecordIsFirstOccurrenceOfOwner(m, ar))
+			{
+			owner = ar->WakeUp;
+			SendSPSRegistrationForOwner(m, intf, id, &owner);
+			}
+		}
+	}
+
+// RetrySPSRegistrations is called from SendResponses, with the lock held
+mDNSlocal void RetrySPSRegistrations(mDNS *const m)
+	{
+	AuthRecord *rr;
+	NetworkInterfaceInfo *intf;
+
+	// First make sure none of our interfaces' NextSPSAttemptTimes are inadvertently set to m->timenow + mDNSPlatformOneSecond * 10
+	for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next))
+		if (intf->NextSPSAttempt && intf->NextSPSAttemptTime == m->timenow + mDNSPlatformOneSecond * 10)
+			intf->NextSPSAttemptTime++;
+
+	// Retry any record registrations that are due
+	for (rr = m->ResourceRecords; rr; rr=rr->next)
+		if (!AuthRecord_uDNS(rr) && !mDNSOpaque16IsZero(rr->updateid) && m->timenow - (rr->LastAPTime + rr->ThisAPInterval) >= 0)
+			for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next))
+				if (!rr->resrec.InterfaceID || rr->resrec.InterfaceID == intf->InterfaceID)
+					{
+					LogSPS("RetrySPSRegistrations: %s", ARDisplayString(m, rr));
+					SendSPSRegistration(m, intf, rr->updateid);
+					}
+
+	// For interfaces where we did an SPS registration attempt, increment intf->NextSPSAttempt
+	for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next))
+		if (intf->NextSPSAttempt && intf->NextSPSAttemptTime == m->timenow + mDNSPlatformOneSecond * 10 && intf->NextSPSAttempt < 8)
+			intf->NextSPSAttempt++;
+	}
+
+mDNSlocal void NetWakeResolve(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
+	{
+	NetworkInterfaceInfo *intf = (NetworkInterfaceInfo *)question->QuestionContext;
+	int sps = (int)(question - intf->NetWakeResolve);
+	(void)m;			// Unused
+	LogSPS("NetWakeResolve: SPS: %d Add: %d %s", sps, AddRecord, RRDisplayString(m, answer));
+
+	if (!AddRecord) return;												// Don't care about REMOVE events
+	if (answer->rrtype != question->qtype) return;						// Don't care about CNAMEs
+
+	// if (answer->rrtype == kDNSType_AAAA && sps == 0) return;	// To test failing to resolve sleep proxy's address
+
+	if (answer->rrtype == kDNSType_SRV)
+		{
+		// 1. Got the SRV record; now look up the target host's IPv6 link-local address
+		mDNS_StopQuery(m, question);
+		intf->SPSPort[sps] = answer->rdata->u.srv.port;
+		AssignDomainName(&question->qname, &answer->rdata->u.srv.target);
+		question->qtype = kDNSType_AAAA;
+		mDNS_StartQuery(m, question);
+		}
+	else if (answer->rrtype == kDNSType_AAAA && answer->rdlength == sizeof(mDNSv6Addr) && mDNSv6AddressIsLinkLocal(&answer->rdata->u.ipv6))
+		{
+		// 2. Got the target host's IPv6 link-local address; record address and initiate an SPS registration if appropriate
+		mDNS_StopQuery(m, question);
+		question->ThisQInterval = -1;
+		intf->SPSAddr[sps].type = mDNSAddrType_IPv6;
+		intf->SPSAddr[sps].ip.v6 = answer->rdata->u.ipv6;
+		mDNS_Lock(m);
+		if (sps == intf->NextSPSAttempt/3) SendSPSRegistration(m, intf, zeroID);	// If we're ready for this result, use it now
+		mDNS_Unlock(m);
+		}
+	else if (answer->rrtype == kDNSType_AAAA && answer->rdlength == 0)
+		{
+		// 3. Got negative response -- target host apparently has IPv6 disabled -- so try looking up the target host's IPv4 address(es) instead
+		mDNS_StopQuery(m, question);
+		LogSPS("NetWakeResolve: SPS %d %##s has no IPv6 address, will try IPv4 instead", sps, question->qname.c);
+		question->qtype = kDNSType_A;
+		mDNS_StartQuery(m, question);
+		}
+	else if (answer->rrtype == kDNSType_A && answer->rdlength == sizeof(mDNSv4Addr))
+		{
+		// 4. Got an IPv4 address for the target host; record address and initiate an SPS registration if appropriate
+		mDNS_StopQuery(m, question);
+		question->ThisQInterval = -1;
+		intf->SPSAddr[sps].type = mDNSAddrType_IPv4;
+		intf->SPSAddr[sps].ip.v4 = answer->rdata->u.ipv4;
+		mDNS_Lock(m);
+		if (sps == intf->NextSPSAttempt/3) SendSPSRegistration(m, intf, zeroID);	// If we're ready for this result, use it now
+		mDNS_Unlock(m);
+		}
+	}
+
+mDNSexport mDNSBool mDNSCoreHaveAdvertisedMulticastServices(mDNS *const m)
+	{
+	AuthRecord *rr;
+	for (rr = m->ResourceRecords; rr; rr=rr->next)
+		if (rr->resrec.rrtype == kDNSType_SRV && !AuthRecord_uDNS(rr) && !mDNSSameIPPort(rr->resrec.rdata->u.srv.port, DiscardPort))
+			return mDNStrue;
+	return mDNSfalse;
+	}
+
+mDNSlocal void SendSleepGoodbyes(mDNS *const m)
+	{
+	AuthRecord *rr;
+	m->SleepState = SleepState_Sleeping;
+
+#ifndef UNICAST_DISABLED
+	SleepRecordRegistrations(m);	// If we have no SPS, need to deregister our uDNS records
+#endif /* UNICAST_DISABLED */
+
+	// Mark all the records we need to deregister and send them
+	for (rr = m->ResourceRecords; rr; rr=rr->next)
+		if (rr->resrec.RecordType == kDNSRecordTypeShared && rr->RequireGoodbye)
+			rr->ImmedAnswer = mDNSInterfaceMark;
+	SendResponses(m);
+	}
+
+// BeginSleepProcessing is called, with the lock held, from either mDNS_Execute or mDNSCoreMachineSleep
+mDNSlocal void BeginSleepProcessing(mDNS *const m)
+	{
+	mDNSBool SendGoodbyes = mDNStrue;
+	const CacheRecord *sps[3] = { mDNSNULL };
+
+	m->NextScheduledSPRetry = m->timenow;
+
+	if      (!m->SystemWakeOnLANEnabled)                  LogSPS("BeginSleepProcessing: m->SystemWakeOnLANEnabled is false");
+	else if (!mDNSCoreHaveAdvertisedMulticastServices(m)) LogSPS("BeginSleepProcessing: No advertised services");
+	else	// If we have at least one advertised service
+		{
+		NetworkInterfaceInfo *intf;
+		for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next))
+			{
+			if (!intf->NetWake) LogSPS("BeginSleepProcessing: %-6s not capable of magic packet wakeup", intf->ifname);
+#if APPLE_OSX_mDNSResponder
+			else if (ActivateLocalProxy(m, intf->ifname) == mStatus_NoError)
+				{
+				SendGoodbyes = mDNSfalse;
+				LogSPS("BeginSleepProcessing: %-6s using local proxy", intf->ifname);
+				// This will leave m->SleepState set to SleepState_Transferring,
+				// which is okay because with no outstanding resolves, or updates in flight,
+				// mDNSCoreReadyForSleep() will conclude correctly that all the updates have already completed
+				}
+#endif // APPLE_OSX_mDNSResponder
+			else
+				{
+				FindSPSInCache(m, &intf->NetWakeBrowse, sps);
+				if (!sps[0]) LogSPS("BeginSleepProcessing: %-6s %#a No Sleep Proxy Server found (Next Browse Q in %d, interval %d)",
+					intf->ifname, &intf->ip, NextQSendTime(&intf->NetWakeBrowse) - m->timenow, intf->NetWakeBrowse.ThisQInterval);
+				else
+					{
+					int i;
+					SendGoodbyes = mDNSfalse;
+					intf->NextSPSAttempt = 0;
+					intf->NextSPSAttemptTime = m->timenow + mDNSPlatformOneSecond;
+					// Don't need to set m->NextScheduledSPRetry here because we already set "m->NextScheduledSPRetry = m->timenow" above
+					for (i=0; i<3; i++)
+						{
+#if ForceAlerts
+						if (intf->SPSAddr[i].type)
+							{ LogMsg("BeginSleepProcessing: %s %d intf->SPSAddr[i].type %d", intf->ifname, i, intf->SPSAddr[i].type); *(long*)0 = 0; }
+						if (intf->NetWakeResolve[i].ThisQInterval >= 0)
+							{ LogMsg("BeginSleepProcessing: %s %d intf->NetWakeResolve[i].ThisQInterval %d", intf->ifname, i, intf->NetWakeResolve[i].ThisQInterval); *(long*)0 = 0; }
+#endif
+						intf->SPSAddr[i].type = mDNSAddrType_None;
+						if (intf->NetWakeResolve[i].ThisQInterval >= 0) mDNS_StopQuery(m, &intf->NetWakeResolve[i]);
+						intf->NetWakeResolve[i].ThisQInterval = -1;
+						if (sps[i])
+							{
+							LogSPS("BeginSleepProcessing: %-6s Found Sleep Proxy Server %d TTL %d %s", intf->ifname, i, sps[i]->resrec.rroriginalttl, CRDisplayString(m, sps[i]));
+							mDNS_SetupQuestion(&intf->NetWakeResolve[i], intf->InterfaceID, &sps[i]->resrec.rdata->u.name, kDNSType_SRV, NetWakeResolve, intf);
+							intf->NetWakeResolve[i].ReturnIntermed = mDNStrue;
+							mDNS_StartQuery_internal(m, &intf->NetWakeResolve[i]);
+							}
+						}
+					}
+				}
+			}
+		}
+
+	if (SendGoodbyes)	// If we didn't find even one Sleep Proxy
+		{
+		LogSPS("BeginSleepProcessing: Not registering with Sleep Proxy Server");
+		SendSleepGoodbyes(m);
+		}
+	}
+
+// Call mDNSCoreMachineSleep(m, mDNStrue) when the machine is about to go to sleep.
+// Call mDNSCoreMachineSleep(m, mDNSfalse) when the machine is has just woken up.
+// Normally, the platform support layer below mDNSCore should call this, not the client layer above.
+mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleep)
+	{
+	AuthRecord *rr;
+
+	LogSPS("%s (old state %d) at %ld", sleep ? "Sleeping" : "Waking", m->SleepState, m->timenow);
+
+	if (sleep && !m->SleepState)		// Going to sleep
+		{
+		mDNS_Lock(m);
+		// If we're going to sleep, need to stop advertising that we're a Sleep Proxy Server
+		if (m->SPSSocket)
+			{
+			mDNSu8 oldstate = m->SPSState;
+			mDNS_DropLockBeforeCallback();		// mDNS_DeregisterService expects to be called without the lock held, so we emulate that here
+			m->SPSState = 2;
+			if (oldstate == 1) mDNS_DeregisterService(m, &m->SPSRecords);
+			mDNS_ReclaimLockAfterCallback();
+			}
+
+		m->SleepState = SleepState_Transferring;
+		if (m->SystemWakeOnLANEnabled && m->DelaySleep)
+			{
+			// If we just woke up moments ago, allow ten seconds for networking to stabilize before going back to sleep
+			LogSPS("mDNSCoreMachineSleep: Re-sleeping immediately after waking; will delay for %d ticks", m->DelaySleep - m->timenow);
+			m->SleepLimit = NonZeroTime(m->DelaySleep + mDNSPlatformOneSecond * 10);
+			}
+		else
+			{
+			m->DelaySleep = 0;
+			m->SleepLimit = NonZeroTime(m->timenow + mDNSPlatformOneSecond * 10);
+			BeginSleepProcessing(m);
+			}
+
+#ifndef UNICAST_DISABLED
+		SuspendLLQs(m);
+#endif
+		mDNS_Unlock(m);
+		// RemoveAutoTunnel6Record needs to be called outside the lock, as it grabs the lock also.
+#if APPLE_OSX_mDNSResponder
+		RemoveAutoTunnel6Record(m);
+#endif
+		LogSPS("mDNSCoreMachineSleep: m->SleepState %d (%s) seq %d", m->SleepState,
+			m->SleepState == SleepState_Transferring ? "Transferring" :
+			m->SleepState == SleepState_Sleeping     ? "Sleeping"     : "?", m->SleepSeqNum);
+		}
+	else if (!sleep)		// Waking up
+		{
+		mDNSu32 slot;
+		CacheGroup *cg;
+		CacheRecord *cr;
+		NetworkInterfaceInfo *intf;
+
+		mDNS_Lock(m);
+		// Reset SleepLimit back to 0 now that we're awake again.
+		m->SleepLimit = 0;
+
+		// If we were previously sleeping, but now we're not, increment m->SleepSeqNum to indicate that we're entering a new period of wakefulness
+		if (m->SleepState != SleepState_Awake)
+			{
+			m->SleepState = SleepState_Awake;
+			m->SleepSeqNum++;
+			// If the machine wakes and then immediately tries to sleep again (e.g. a maintenance wake)
+			// then we enforce a minimum delay of 16 seconds before we begin sleep processing.
+			// This is to allow time for the Ethernet link to come up, DHCP to get an address, mDNS to issue queries, etc.,
+			// before we make our determination of whether there's a Sleep Proxy out there we should register with.
+			m->DelaySleep = NonZeroTime(m->timenow + mDNSPlatformOneSecond * 16);
+			}
+
+		if (m->SPSState == 3)
+			{
+			m->SPSState = 0;
+			mDNSCoreBeSleepProxyServer_internal(m, m->SPSType, m->SPSPortability, m->SPSMarginalPower, m->SPSTotalPower);
+			}
+
+		// In case we gave up waiting and went to sleep before we got an ack from the Sleep Proxy,
+		// on wake we go through our record list and clear updateid back to zero
+		for (rr = m->ResourceRecords; rr; rr=rr->next) rr->updateid = zeroID;
+
+		// ... and the same for NextSPSAttempt
+		for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next)) intf->NextSPSAttempt = -1;
+
+		// Restart unicast and multicast queries
+		mDNSCoreRestartQueries(m);
+
+		// and reactivtate service registrations
+		m->NextSRVUpdate = NonZeroTime(m->timenow + mDNSPlatformOneSecond);
+		LogInfo("mDNSCoreMachineSleep waking: NextSRVUpdate in %d %d", m->NextSRVUpdate - m->timenow, m->timenow);
+
+		// 2. Re-validate our cache records
+		FORALL_CACHERECORDS(slot, cg, cr)
+			mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForWake);
+
+		// 3. Retrigger probing and announcing for all our authoritative records
+		for (rr = m->ResourceRecords; rr; rr=rr->next)
+			if (AuthRecord_uDNS(rr))
+				{
+				ActivateUnicastRegistration(m, rr);
+				}
+			else
+				{
+				if (rr->resrec.RecordType == kDNSRecordTypeVerified && !rr->DependentOn) rr->resrec.RecordType = kDNSRecordTypeUnique;
+				rr->ProbeCount     = DefaultProbeCountForRecordType(rr->resrec.RecordType);
+				rr->AnnounceCount  = InitialAnnounceCount;
+				rr->SendNSECNow    = mDNSNULL;
+				InitializeLastAPTime(m, rr);
+				}
+
+		// 4. Refresh NAT mappings
+		// We don't want to have to assume that all hardware can necessarily keep accurate
+		// track of passage of time while asleep, so on wake we refresh our NAT mappings
+		// We typically wake up with no interfaces active, so there's no need to rush to try to find our external address.
+		// When we get a network configuration change, mDNSMacOSXNetworkChanged calls uDNS_SetupDNSConfig, which calls
+		// mDNS_SetPrimaryInterfaceInfo, which then sets m->retryGetAddr to immediately request our external address from the NAT gateway.
+		m->retryIntervalGetAddr = NATMAP_INIT_RETRY;
+		m->retryGetAddr         = m->timenow + mDNSPlatformOneSecond * 5;
+		LogInfo("mDNSCoreMachineSleep: retryGetAddr in %d %d", m->retryGetAddr - m->timenow, m->timenow);
+		RecreateNATMappings(m);
+		mDNS_Unlock(m);
+		}
+	}
+
+mDNSexport mDNSBool mDNSCoreReadyForSleep(mDNS *m, mDNSs32 now)
+	{
+	DNSQuestion *q;
+	AuthRecord *rr;
+	NetworkInterfaceInfo *intf;
+
+	mDNS_Lock(m);
+
+	if (m->DelaySleep) goto notready;
+
+	// If we've not hit the sleep limit time, and it's not time for our next retry, we can skip these checks
+	if (m->SleepLimit - now > 0 && m->NextScheduledSPRetry - now > 0) goto notready;
+
+	m->NextScheduledSPRetry = now + 0x40000000UL;
+
+	// See if we might need to retransmit any lost Sleep Proxy Registrations
+	for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next))
+		if (intf->NextSPSAttempt >= 0)
+			{
+			if (now - intf->NextSPSAttemptTime >= 0)
+				{
+				LogSPS("mDNSCoreReadyForSleep: retrying for %s SPS %d try %d",
+					intf->ifname, intf->NextSPSAttempt/3, intf->NextSPSAttempt);
+				SendSPSRegistration(m, intf, zeroID);
+				// Don't need to "goto notready" here, because if we do still have record registrations
+				// that have not been acknowledged yet, we'll catch that in the record list scan below.
+				}
+			else
+				if (m->NextScheduledSPRetry - intf->NextSPSAttemptTime > 0)
+					m->NextScheduledSPRetry = intf->NextSPSAttemptTime;
+			}
+
+	// Scan list of interfaces, and see if we're still waiting for any sleep proxy resolves to complete
+	for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next))
+		{
+		int sps = (intf->NextSPSAttempt == 0) ? 0 : (intf->NextSPSAttempt-1)/3;
+		if (intf->NetWakeResolve[sps].ThisQInterval >= 0)
+			{
+			LogSPS("mDNSCoreReadyForSleep: waiting for SPS Resolve %s %##s (%s)",
+				intf->ifname, intf->NetWakeResolve[sps].qname.c, DNSTypeName(intf->NetWakeResolve[sps].qtype));
+			goto spsnotready;
+			}
+		}
+
+	// Scan list of registered records
+	for (rr = m->ResourceRecords; rr; rr = rr->next)
+		if (!AuthRecord_uDNS(rr))
+			if (!mDNSOpaque16IsZero(rr->updateid))
+				{ LogSPS("mDNSCoreReadyForSleep: waiting for SPS Update ID %d %s", mDNSVal16(rr->updateid), ARDisplayString(m,rr)); goto spsnotready; }
+
+	// Scan list of private LLQs, and make sure they've all completed their handshake with the server
+	for (q = m->Questions; q; q = q->next)
+		if (!mDNSOpaque16IsZero(q->TargetQID) && q->LongLived && q->ReqLease == 0 && q->tcp)
+			{
+			LogSPS("mDNSCoreReadyForSleep: waiting for LLQ %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+			goto notready;
+			}
+
+	// Scan list of registered records
+	for (rr = m->ResourceRecords; rr; rr = rr->next)
+		if (AuthRecord_uDNS(rr))
+			{
+			if (rr->state == regState_Refresh && rr->tcp)
+				{ LogSPS("mDNSCoreReadyForSleep: waiting for Record Update ID %d %s", mDNSVal16(rr->updateid), ARDisplayString(m,rr)); goto notready; }
+			#if APPLE_OSX_mDNSResponder
+			if (!RecordReadyForSleep(m, rr)) { LogSPS("mDNSCoreReadyForSleep: waiting for %s", ARDisplayString(m, rr)); goto notready; }
+			#endif
+			}
+
+	mDNS_Unlock(m);
+	return mDNStrue;
+
+spsnotready:
+
+	// If we failed to complete sleep proxy registration within ten seconds, we give up on that
+	// and allow up to ten seconds more to complete wide-area deregistration instead
+	if (now - m->SleepLimit >= 0)
+		{
+		LogMsg("Failed to register with SPS, now sending goodbyes");
+
+		for (intf = GetFirstActiveInterface(m->HostInterfaces); intf; intf = GetFirstActiveInterface(intf->next))
+			if (intf->NetWakeBrowse.ThisQInterval >= 0)
+				{
+				LogSPS("ReadyForSleep mDNS_DeactivateNetWake %s %##s (%s)",
+					intf->ifname, intf->NetWakeResolve[0].qname.c, DNSTypeName(intf->NetWakeResolve[0].qtype));
+				mDNS_DeactivateNetWake_internal(m, intf);
+				}
+
+		for (rr = m->ResourceRecords; rr; rr = rr->next)
+			if (!AuthRecord_uDNS(rr))
+				if (!mDNSOpaque16IsZero(rr->updateid))
+					{
+					LogSPS("ReadyForSleep clearing updateid for %s", ARDisplayString(m, rr));
+					rr->updateid = zeroID;
+					}
+
+		// We'd really like to allow up to ten seconds more here,
+		// but if we don't respond to the sleep notification within 30 seconds
+		// we'll be put back to sleep forcibly without the chance to schedule the next maintenance wake.
+		// Right now we wait 16 sec after wake for all the interfaces to come up, then we wait up to 10 seconds
+		// more for SPS resolves and record registrations to complete, which puts us at 26 seconds.
+		// If we allow just one more second to send our goodbyes, that puts us at 27 seconds.
+		m->SleepLimit = now + mDNSPlatformOneSecond * 1;
+
+		SendSleepGoodbyes(m);
+		}
+
+notready:
+	mDNS_Unlock(m);
+	return mDNSfalse;
+	}
+
+mDNSexport mDNSs32 mDNSCoreIntervalToNextWake(mDNS *const m, mDNSs32 now)
+	{
+	AuthRecord *ar;
+
+	// Even when we have no wake-on-LAN-capable interfaces, or we failed to find a sleep proxy, or we have other
+	// failure scenarios, we still want to wake up in at most 120 minutes, to see if the network environment has changed.
+	// E.g. we might wake up and find no wireless network because the base station got rebooted just at that moment,
+	// and if that happens we don't want to just give up and go back to sleep and never try again.
+	mDNSs32 e = now + (120 * 60 * mDNSPlatformOneSecond);		// Sleep for at most 120 minutes
+
+	NATTraversalInfo *nat;
+	for (nat = m->NATTraversals; nat; nat=nat->next)
+		if (nat->Protocol && nat->ExpiryTime && nat->ExpiryTime - now > mDNSPlatformOneSecond*4)
+			{
+			mDNSs32 t = nat->ExpiryTime - (nat->ExpiryTime - now) / 10;		// Wake up when 90% of the way to the expiry time
+			if (e - t > 0) e = t;
+			LogSPS("ComputeWakeTime: %p %s Int %5d Ext %5d Err %d Retry %5d Interval %5d Expire %5d Wake %5d",
+				nat, nat->Protocol == NATOp_MapTCP ? "TCP" : "UDP",
+				mDNSVal16(nat->IntPort), mDNSVal16(nat->ExternalPort), nat->Result,
+				nat->retryPortMap ? (nat->retryPortMap - now) / mDNSPlatformOneSecond : 0,
+				nat->retryInterval / mDNSPlatformOneSecond,
+				nat->ExpiryTime ? (nat->ExpiryTime - now) / mDNSPlatformOneSecond : 0,
+				(t - now) / mDNSPlatformOneSecond);
+			}
+
+	// This loop checks both the time we need to renew wide-area registrations,
+	// and the time we need to renew Sleep Proxy registrations
+	for (ar = m->ResourceRecords; ar; ar = ar->next)
+		if (ar->expire && ar->expire - now > mDNSPlatformOneSecond*4)
+			{
+			mDNSs32 t = ar->expire - (ar->expire - now) / 10;		// Wake up when 90% of the way to the expiry time
+			if (e - t > 0) e = t;
+			LogSPS("ComputeWakeTime: %p Int %7d Next %7d Expire %7d Wake %7d %s",
+				ar, ar->ThisAPInterval / mDNSPlatformOneSecond,
+				(ar->LastAPTime + ar->ThisAPInterval - now) / mDNSPlatformOneSecond,
+				ar->expire ? (ar->expire - now) / mDNSPlatformOneSecond : 0,
+				(t - now) / mDNSPlatformOneSecond, ARDisplayString(m, ar));
+			}
+
+	return(e - now);
+	}
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - Packet Reception Functions
+#endif
+
+#define MustSendRecord(RR) ((RR)->NR_AnswerTo || (RR)->NR_AdditionalTo)
+
+mDNSlocal mDNSu8 *GenerateUnicastResponse(const DNSMessage *const query, const mDNSu8 *const end,
+	const mDNSInterfaceID InterfaceID, mDNSBool LegacyQuery, DNSMessage *const response, AuthRecord *ResponseRecords)
+	{
+	mDNSu8          *responseptr     = response->data;
+	const mDNSu8    *const limit     = response->data + sizeof(response->data);
+	const mDNSu8    *ptr             = query->data;
+	AuthRecord  *rr;
+	mDNSu32          maxttl = 0x70000000;
+	int i;
+
+	// Initialize the response fields so we can answer the questions
+	InitializeDNSMessage(&response->h, query->h.id, ResponseFlags);
+
+	// ***
+	// *** 1. Write out the list of questions we are actually going to answer with this packet
+	// ***
+	if (LegacyQuery)
+		{
+		maxttl = kStaticCacheTTL;
+		for (i=0; i<query->h.numQuestions; i++)						// For each question...
+			{
+			DNSQuestion q;
+			ptr = getQuestion(query, ptr, end, InterfaceID, &q);	// get the question...
+			if (!ptr) return(mDNSNULL);
+	
+			for (rr=ResponseRecords; rr; rr=rr->NextResponse)		// and search our list of proposed answers
+				{
+				if (rr->NR_AnswerTo == ptr)							// If we're going to generate a record answering this question
+					{												// then put the question in the question section
+					responseptr = putQuestion(response, responseptr, limit, &q.qname, q.qtype, q.qclass);
+					if (!responseptr) { debugf("GenerateUnicastResponse: Ran out of space for questions!"); return(mDNSNULL); }
+					break;		// break out of the ResponseRecords loop, and go on to the next question
+					}
+				}
+			}
+	
+		if (response->h.numQuestions == 0) { LogMsg("GenerateUnicastResponse: ERROR! Why no questions?"); return(mDNSNULL); }
+		}
+
+	// ***
+	// *** 2. Write Answers
+	// ***
+	for (rr=ResponseRecords; rr; rr=rr->NextResponse)
+		if (rr->NR_AnswerTo)
+			{
+			mDNSu8 *p = PutResourceRecordTTL(response, responseptr, &response->h.numAnswers, &rr->resrec,
+				maxttl < rr->resrec.rroriginalttl ? maxttl : rr->resrec.rroriginalttl);
+			if (p) responseptr = p;
+			else { debugf("GenerateUnicastResponse: Ran out of space for answers!"); response->h.flags.b[0] |= kDNSFlag0_TC; }
+			}
+
+	// ***
+	// *** 3. Write Additionals
+	// ***
+	for (rr=ResponseRecords; rr; rr=rr->NextResponse)
+		if (rr->NR_AdditionalTo && !rr->NR_AnswerTo)
+			{
+			mDNSu8 *p = PutResourceRecordTTL(response, responseptr, &response->h.numAdditionals, &rr->resrec,
+				maxttl < rr->resrec.rroriginalttl ? maxttl : rr->resrec.rroriginalttl);
+			if (p) responseptr = p;
+			else debugf("GenerateUnicastResponse: No more space for additionals");
+			}
+
+	return(responseptr);
+	}
+
+// AuthRecord *our is our Resource Record
+// CacheRecord *pkt is the Resource Record from the response packet we've witnessed on the network
+// Returns 0 if there is no conflict
+// Returns +1 if there was a conflict and we won
+// Returns -1 if there was a conflict and we lost and have to rename
+mDNSlocal int CompareRData(const AuthRecord *const our, const CacheRecord *const pkt)
+	{
+	mDNSu8 ourdata[256], *ourptr = ourdata, *ourend;
+	mDNSu8 pktdata[256], *pktptr = pktdata, *pktend;
+	if (!our) { LogMsg("CompareRData ERROR: our is NULL"); return(+1); }
+	if (!pkt) { LogMsg("CompareRData ERROR: pkt is NULL"); return(+1); }
+
+	ourend = putRData(mDNSNULL, ourdata, ourdata + sizeof(ourdata), &our->resrec);
+	pktend = putRData(mDNSNULL, pktdata, pktdata + sizeof(pktdata), &pkt->resrec);
+	while (ourptr < ourend && pktptr < pktend && *ourptr == *pktptr) { ourptr++; pktptr++; }
+	if (ourptr >= ourend && pktptr >= pktend) return(0);			// If data identical, not a conflict
+
+	if (ourptr >= ourend) return(-1);								// Our data ran out first; We lost
+	if (pktptr >= pktend) return(+1);								// Packet data ran out first; We won
+	if (*pktptr > *ourptr) return(-1);								// Our data is numerically lower; We lost
+	if (*pktptr < *ourptr) return(+1);								// Packet data is numerically lower; We won
+	
+	LogMsg("CompareRData ERROR: Invalid state");
+	return(-1);
+	}
+
+// See if we have an authoritative record that's identical to this packet record,
+// whose canonical DependentOn record is the specified master record.
+// The DependentOn pointer is typically used for the TXT record of service registrations
+// It indicates that there is no inherent conflict detection for the TXT record
+// -- it depends on the SRV record to resolve name conflicts
+// If we find any identical ResourceRecords in our authoritative list, then follow their DependentOn
+// pointer chain (if any) to make sure we reach the canonical DependentOn record
+// If the record has no DependentOn, then just return that record's pointer
+// Returns NULL if we don't have any local RRs that are identical to the one from the packet
+mDNSlocal mDNSBool MatchDependentOn(const mDNS *const m, const CacheRecord *const pktrr, const AuthRecord *const master)
+	{
+	const AuthRecord *r1;
+	for (r1 = m->ResourceRecords; r1; r1=r1->next)
+		{
+		if (IdenticalResourceRecord(&r1->resrec, &pktrr->resrec))
+			{
+			const AuthRecord *r2 = r1;
+			while (r2->DependentOn) r2 = r2->DependentOn;
+			if (r2 == master) return(mDNStrue);
+			}
+		}
+	for (r1 = m->DuplicateRecords; r1; r1=r1->next)
+		{
+		if (IdenticalResourceRecord(&r1->resrec, &pktrr->resrec))
+			{
+			const AuthRecord *r2 = r1;
+			while (r2->DependentOn) r2 = r2->DependentOn;
+			if (r2 == master) return(mDNStrue);
+			}
+		}
+	return(mDNSfalse);
+	}
+
+// Find the canonical RRSet pointer for this RR received in a packet.
+// If we find any identical AuthRecord in our authoritative list, then follow its RRSet
+// pointers (if any) to make sure we return the canonical member of this name/type/class
+// Returns NULL if we don't have any local RRs that are identical to the one from the packet
+mDNSlocal const AuthRecord *FindRRSet(const mDNS *const m, const CacheRecord *const pktrr)
+	{
+	const AuthRecord *rr;
+	for (rr = m->ResourceRecords; rr; rr=rr->next)
+		{
+		if (IdenticalResourceRecord(&rr->resrec, &pktrr->resrec))
+			{
+			while (rr->RRSet && rr != rr->RRSet) rr = rr->RRSet;
+			return(rr);
+			}
+		}
+	return(mDNSNULL);
+	}
+
+// PacketRRConflict is called when we've received an RR (pktrr) which has the same name
+// as one of our records (our) but different rdata.
+// 1. If our record is not a type that's supposed to be unique, we don't care.
+// 2a. If our record is marked as dependent on some other record for conflict detection, ignore this one.
+// 2b. If the packet rr exactly matches one of our other RRs, and *that* record's DependentOn pointer
+//     points to our record, ignore this conflict (e.g. the packet record matches one of our
+//     TXT records, and that record is marked as dependent on 'our', its SRV record).
+// 3. If we have some *other* RR that exactly matches the one from the packet, and that record and our record
+//    are members of the same RRSet, then this is not a conflict.
+mDNSlocal mDNSBool PacketRRConflict(const mDNS *const m, const AuthRecord *const our, const CacheRecord *const pktrr)
+	{
+	// If not supposed to be unique, not a conflict
+	if (!(our->resrec.RecordType & kDNSRecordTypeUniqueMask)) return(mDNSfalse);
+
+	// If a dependent record, not a conflict
+	if (our->DependentOn || MatchDependentOn(m, pktrr, our)) return(mDNSfalse);
+	else
+		{
+		// If the pktrr matches a member of ourset, not a conflict
+		const AuthRecord *ourset = our->RRSet ? our->RRSet : our;
+		const AuthRecord *pktset = FindRRSet(m, pktrr);
+		if (pktset == ourset) return(mDNSfalse);
+
+		// For records we're proxying, where we don't know the full
+		// relationship between the records, having any matching record
+		// in our AuthRecords list is sufficient evidence of non-conflict
+		if (our->WakeUp.HMAC.l[0] && pktset) return(mDNSfalse);
+		}
+
+	// Okay, this is a conflict
+	return(mDNStrue);
+	}
+
+// Note: ResolveSimultaneousProbe calls mDNS_Deregister_internal which can call a user callback, which may change
+// the record list and/or question list.
+// Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
+mDNSlocal void ResolveSimultaneousProbe(mDNS *const m, const DNSMessage *const query, const mDNSu8 *const end,
+	DNSQuestion *q, AuthRecord *our)
+	{
+	int i;
+	const mDNSu8 *ptr = LocateAuthorities(query, end);
+	mDNSBool FoundUpdate = mDNSfalse;
+
+	for (i = 0; i < query->h.numAuthorities; i++)
+		{
+		ptr = GetLargeResourceRecord(m, query, ptr, end, q->InterfaceID, kDNSRecordTypePacketAuth, &m->rec);
+		if (!ptr) break;
+		if (m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative && ResourceRecordAnswersQuestion(&m->rec.r.resrec, q))
+			{
+			FoundUpdate = mDNStrue;
+			if (PacketRRConflict(m, our, &m->rec.r))
+				{
+				int result          = (int)our->resrec.rrclass - (int)m->rec.r.resrec.rrclass;
+				if (!result) result = (int)our->resrec.rrtype  - (int)m->rec.r.resrec.rrtype;
+				if (!result) result = CompareRData(our, &m->rec.r);
+				if (result)
+					{
+					const char *const msg = (result < 0) ? "lost:" : (result > 0) ? "won: " : "tie: ";
+					LogMsg("ResolveSimultaneousProbe: %p Pkt Record:        %08lX %s", q->InterfaceID, m->rec.r.resrec.rdatahash, CRDisplayString(m, &m->rec.r));
+					LogMsg("ResolveSimultaneousProbe: %p Our Record %d %s %08lX %s", our->resrec.InterfaceID, our->ProbeCount, msg, our->resrec.rdatahash, ARDisplayString(m, our));
+					}
+				// If we lost the tie-break for simultaneous probes, we don't immediately give up, because we might be seeing stale packets on the network.
+				// Instead we pause for one second, to give the other host (if real) a chance to establish its name, and then try probing again.
+				// If there really is another live host out there with the same name, it will answer our probes and we'll then rename.
+				if (result < 0)
+					{
+					m->SuppressProbes   = NonZeroTime(m->timenow + mDNSPlatformOneSecond);
+					our->ProbeCount     = DefaultProbeCountForTypeUnique;
+					our->AnnounceCount  = InitialAnnounceCount;
+					InitializeLastAPTime(m, our);
+					goto exit;
+					}
+				}
+#if 0
+			else
+				{
+				LogMsg("ResolveSimultaneousProbe: %p Pkt Record:        %08lX %s", q->InterfaceID, m->rec.r.resrec.rdatahash, CRDisplayString(m, &m->rec.r));
+				LogMsg("ResolveSimultaneousProbe: %p Our Record %d ign:  %08lX %s", our->resrec.InterfaceID, our->ProbeCount, our->resrec.rdatahash, ARDisplayString(m, our));
+				}
+#endif
+			}
+		m->rec.r.resrec.RecordType = 0;		// Clear RecordType to show we're not still using it
+		}
+	if (!FoundUpdate)
+		LogInfo("ResolveSimultaneousProbe: %##s (%s): No Update Record found", our->resrec.name->c, DNSTypeName(our->resrec.rrtype));
+exit:
+	m->rec.r.resrec.RecordType = 0;		// Clear RecordType to show we're not still using it
+	}
+
+mDNSlocal CacheRecord *FindIdenticalRecordInCache(const mDNS *const m, const ResourceRecord *const pktrr)
+	{
+	mDNSu32 slot = HashSlot(pktrr->name);
+	CacheGroup *cg = CacheGroupForRecord(m, slot, pktrr);
+	CacheRecord *rr;
+	mDNSBool match;
+	for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
+		{
+		match = !pktrr->InterfaceID ? pktrr->rDNSServer == rr->resrec.rDNSServer : pktrr->InterfaceID == rr->resrec.InterfaceID;
+		if (match && IdenticalSameNameRecord(pktrr, &rr->resrec)) break;
+		}
+	return(rr);
+	}
+
+// Called from mDNSCoreReceiveUpdate when we get a sleep proxy registration request,
+// to check our lists and discard any stale duplicates of this record we already have
+mDNSlocal void ClearIdenticalProxyRecords(mDNS *const m, const OwnerOptData *const owner, AuthRecord *const thelist)
+	{
+	if (m->CurrentRecord)
+		LogMsg("ClearIdenticalProxyRecords ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord));
+	m->CurrentRecord = thelist;
+	while (m->CurrentRecord)
+		{
+		AuthRecord *const rr = m->CurrentRecord;
+		if (m->rec.r.resrec.InterfaceID == rr->resrec.InterfaceID && mDNSSameEthAddress(&owner->HMAC, &rr->WakeUp.HMAC))
+			if (IdenticalResourceRecord(&rr->resrec, &m->rec.r.resrec))
+				{
+				LogSPS("ClearIdenticalProxyRecords: Removing %3d H-MAC %.6a I-MAC %.6a %d %d %s",
+					m->ProxyRecords, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, rr->WakeUp.seq, owner->seq, ARDisplayString(m, rr));
+				rr->WakeUp.HMAC = zeroEthAddr;	// Clear HMAC so that mDNS_Deregister_internal doesn't waste packets trying to wake this host
+				rr->RequireGoodbye = mDNSfalse;	// and we don't want to send goodbye for it
+				mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal);
+				SetSPSProxyListChanged(m->rec.r.resrec.InterfaceID);
+				}
+		// Mustn't advance m->CurrentRecord until *after* mDNS_Deregister_internal, because
+		// new records could have been added to the end of the list as a result of that call.
+		if (m->CurrentRecord == rr) // If m->CurrentRecord was not advanced for us, do it now
+			m->CurrentRecord = rr->next;
+		}
+	}
+
+// Called from ProcessQuery when we get an mDNS packet with an owner record in it
+mDNSlocal void ClearProxyRecords(mDNS *const m, const OwnerOptData *const owner, AuthRecord *const thelist)
+	{
+	if (m->CurrentRecord)
+		LogMsg("ClearProxyRecords ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord));
+	m->CurrentRecord = thelist;
+	while (m->CurrentRecord)
+		{
+		AuthRecord *const rr = m->CurrentRecord;
+		if (m->rec.r.resrec.InterfaceID == rr->resrec.InterfaceID && mDNSSameEthAddress(&owner->HMAC, &rr->WakeUp.HMAC))
+			if (owner->seq != rr->WakeUp.seq || m->timenow - rr->TimeRcvd > mDNSPlatformOneSecond * 60)
+				{
+				if (rr->AddressProxy.type == mDNSAddrType_IPv6)
+					{
+					// We don't do this here because we know that the host is waking up at this point, so we don't send
+					// Unsolicited Neighbor Advertisements -- even Neighbor Advertisements agreeing with what the host should be
+					// saying itself -- because it can cause some IPv6 stacks to falsely conclude that there's an address conflict.
+					#if MDNS_USE_Unsolicited_Neighbor_Advertisements
+					LogSPS("NDP Announcement -- Releasing traffic for H-MAC %.6a I-MAC %.6a %s",
+						&rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m,rr));
+					SendNDP(m, NDP_Adv, NDP_Override, rr, &rr->AddressProxy.ip.v6, &rr->WakeUp.IMAC, &AllHosts_v6, &AllHosts_v6_Eth);
+					#endif
+					}
+				LogSPS("ClearProxyRecords: Removing %3d AC %2d %02X H-MAC %.6a I-MAC %.6a %d %d %s",
+					m->ProxyRecords, rr->AnnounceCount, rr->resrec.RecordType,
+					&rr->WakeUp.HMAC, &rr->WakeUp.IMAC, rr->WakeUp.seq, owner->seq, ARDisplayString(m, rr));
+				if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) rr->resrec.RecordType = kDNSRecordTypeShared;
+				rr->WakeUp.HMAC = zeroEthAddr;	// Clear HMAC so that mDNS_Deregister_internal doesn't waste packets trying to wake this host
+				rr->RequireGoodbye = mDNSfalse;	// and we don't want to send goodbye for it, since real host is now back and functional
+				mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal);
+				SetSPSProxyListChanged(m->rec.r.resrec.InterfaceID);
+				}
+		// Mustn't advance m->CurrentRecord until *after* mDNS_Deregister_internal, because
+		// new records could have been added to the end of the list as a result of that call.
+		if (m->CurrentRecord == rr) // If m->CurrentRecord was not advanced for us, do it now
+			m->CurrentRecord = rr->next;
+		}
+	}
+
+// ProcessQuery examines a received query to see if we have any answers to give
+mDNSlocal mDNSu8 *ProcessQuery(mDNS *const m, const DNSMessage *const query, const mDNSu8 *const end,
+	const mDNSAddr *srcaddr, const mDNSInterfaceID InterfaceID, mDNSBool LegacyQuery, mDNSBool QueryWasMulticast,
+	mDNSBool QueryWasLocalUnicast, DNSMessage *const response)
+	{
+	mDNSBool      FromLocalSubnet    = srcaddr && mDNS_AddressIsLocalSubnet(m, InterfaceID, srcaddr);
+	AuthRecord   *ResponseRecords    = mDNSNULL;
+	AuthRecord  **nrp                = &ResponseRecords;
+	CacheRecord  *ExpectedAnswers    = mDNSNULL;			// Records in our cache we expect to see updated
+	CacheRecord **eap                = &ExpectedAnswers;
+	DNSQuestion  *DupQuestions       = mDNSNULL;			// Our questions that are identical to questions in this packet
+	DNSQuestion **dqp                = &DupQuestions;
+	mDNSs32       delayresponse      = 0;
+	mDNSBool      SendLegacyResponse = mDNSfalse;
+	const mDNSu8 *ptr;
+	mDNSu8       *responseptr        = mDNSNULL;
+	AuthRecord   *rr;
+	int i;
+
+	// ***
+	// *** 1. Look in Additional Section for an OPT record
+	// ***
+	ptr = LocateOptRR(query, end, DNSOpt_OwnerData_ID_Space);
+	if (ptr)
+		{
+		ptr = GetLargeResourceRecord(m, query, ptr, end, InterfaceID, kDNSRecordTypePacketAdd, &m->rec);
+		if (ptr && m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative && m->rec.r.resrec.rrtype == kDNSType_OPT)
+			{
+			const rdataOPT *opt;
+			const rdataOPT *const e = (const rdataOPT *)&m->rec.r.resrec.rdata->u.data[m->rec.r.resrec.rdlength];
+			// Find owner sub-option(s). We verify that the MAC is non-zero, otherwise we could inadvertently
+			// delete all our own AuthRecords (which are identified by having zero MAC tags on them).
+			for (opt = &m->rec.r.resrec.rdata->u.opt[0]; opt < e; opt++)
+				if (opt->opt == kDNSOpt_Owner && opt->u.owner.vers == 0 && opt->u.owner.HMAC.l[0])
+					{
+					ClearProxyRecords(m, &opt->u.owner, m->DuplicateRecords);
+					ClearProxyRecords(m, &opt->u.owner, m->ResourceRecords);
+					}
+			}
+		m->rec.r.resrec.RecordType = 0;		// Clear RecordType to show we're not still using it
+		}
+
+	// ***
+	// *** 2. Parse Question Section and mark potential answers
+	// ***
+	ptr = query->data;
+	for (i=0; i<query->h.numQuestions; i++)						// For each question...
+		{
+		mDNSBool QuestionNeedsMulticastResponse;
+		int NumAnswersForThisQuestion = 0;
+		AuthRecord *NSECAnswer = mDNSNULL;
+		DNSQuestion pktq, *q;
+		ptr = getQuestion(query, ptr, end, InterfaceID, &pktq);	// get the question...
+		if (!ptr) goto exit;
+
+		// The only queries that *need* a multicast response are:
+		// * Queries sent via multicast
+		// * from port 5353
+		// * that don't have the kDNSQClass_UnicastResponse bit set
+		// These queries need multicast responses because other clients will:
+		// * suppress their own identical questions when they see these questions, and
+		// * expire their cache records if they don't see the expected responses
+		// For other queries, we may still choose to send the occasional multicast response anyway,
+		// to keep our neighbours caches warm, and for ongoing conflict detection.
+		QuestionNeedsMulticastResponse = QueryWasMulticast && !LegacyQuery && !(pktq.qclass & kDNSQClass_UnicastResponse);
+		// Clear the UnicastResponse flag -- don't want to confuse the rest of the code that follows later
+		pktq.qclass &= ~kDNSQClass_UnicastResponse;
+		
+		// Note: We use the m->CurrentRecord mechanism here because calling ResolveSimultaneousProbe
+		// can result in user callbacks which may change the record list and/or question list.
+		// Also note: we just mark potential answer records here, without trying to build the
+		// "ResponseRecords" list, because we don't want to risk user callbacks deleting records
+		// from that list while we're in the middle of trying to build it.
+		if (m->CurrentRecord)
+			LogMsg("ProcessQuery ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord));
+		m->CurrentRecord = m->ResourceRecords;
+		while (m->CurrentRecord)
+			{
+			rr = m->CurrentRecord;
+			m->CurrentRecord = rr->next;
+			if (AnyTypeRecordAnswersQuestion(&rr->resrec, &pktq) && (QueryWasMulticast || QueryWasLocalUnicast || rr->AllowRemoteQuery))
+				{
+				if (RRTypeAnswersQuestionType(&rr->resrec, pktq.qtype))
+					{
+					if (rr->resrec.RecordType == kDNSRecordTypeUnique)
+						ResolveSimultaneousProbe(m, query, end, &pktq, rr);
+					else if (ResourceRecordIsValidAnswer(rr))
+						{
+						NumAnswersForThisQuestion++;
+						// Note: We should check here if this is a probe-type query, and if so, generate an immediate
+						// unicast answer back to the source, because timeliness in answering probes is important.
+	
+						// Notes:
+						// NR_AnswerTo pointing into query packet means "answer via immediate legacy unicast" (may *also* choose to multicast)
+						// NR_AnswerTo == (mDNSu8*)~1             means "answer via delayed unicast" (to modern querier; may promote to multicast instead)
+						// NR_AnswerTo == (mDNSu8*)~0             means "definitely answer via multicast" (can't downgrade to unicast later)
+						// If we're not multicasting this record because the kDNSQClass_UnicastResponse bit was set,
+						// but the multicast querier is not on a matching subnet (e.g. because of overlaid subnets on one link)
+						// then we'll multicast it anyway (if we unicast, the receiver will ignore it because it has an apparently non-local source)
+						if (QuestionNeedsMulticastResponse || (!FromLocalSubnet && QueryWasMulticast && !LegacyQuery))
+							{
+							// We only mark this question for sending if it is at least one second since the last time we multicast it
+							// on this interface. If it is more than a second, or LastMCInterface is different, then we may multicast it.
+							// This is to guard against the case where someone blasts us with queries as fast as they can.
+							if (m->timenow - (rr->LastMCTime + mDNSPlatformOneSecond) >= 0 ||
+								(rr->LastMCInterface != mDNSInterfaceMark && rr->LastMCInterface != InterfaceID))
+								rr->NR_AnswerTo = (mDNSu8*)~0;
+							}
+						else if (!rr->NR_AnswerTo) rr->NR_AnswerTo = LegacyQuery ? ptr : (mDNSu8*)~1;
+						}
+					}
+				else if ((rr->resrec.RecordType & kDNSRecordTypeActiveUniqueMask) && ResourceRecordIsValidAnswer(rr))
+					{
+					// If we don't have any answers for this question, but we do own another record with the same name,
+					// then we'll want to mark it to generate an NSEC record on this interface
+					if (!NSECAnswer) NSECAnswer = rr;
+					}
+				}
+			}
+
+		if (NumAnswersForThisQuestion == 0 && NSECAnswer)
+			{
+			NumAnswersForThisQuestion++;
+			NSECAnswer->SendNSECNow = InterfaceID;
+			m->NextScheduledResponse = m->timenow;
+			}
+
+		// If we couldn't answer this question, someone else might be able to,
+		// so use random delay on response to reduce collisions
+		if (NumAnswersForThisQuestion == 0) delayresponse = mDNSPlatformOneSecond;	// Divided by 50 = 20ms
+
+#if ENABLE_MULTI_PACKET_QUERY_SNOOPING
+		if (QuestionNeedsMulticastResponse)
+#else
+		// We only do the following accelerated cache expiration and duplicate question suppression processing
+		// for non-truncated multicast queries with multicast responses.
+		// For any query generating a unicast response we don't do this because we can't assume we will see the response.
+		// For truncated queries we don't do this because a response we're expecting might be suppressed by a subsequent
+		// known-answer packet, and when there's packet loss we can't safely assume we'll receive *all* known-answer packets.
+		if (QuestionNeedsMulticastResponse && !(query->h.flags.b[0] & kDNSFlag0_TC))
+#endif
+			{
+			const mDNSu32 slot = HashSlot(&pktq.qname);
+			CacheGroup *cg = CacheGroupForName(m, slot, pktq.qnamehash, &pktq.qname);
+			CacheRecord *cr;
+
+			// Make a list indicating which of our own cache records we expect to see updated as a result of this query
+			// Note: Records larger than 1K are not habitually multicast, so don't expect those to be updated
+#if ENABLE_MULTI_PACKET_QUERY_SNOOPING
+			if (!(query->h.flags.b[0] & kDNSFlag0_TC))
+#endif
+				for (cr = cg ? cg->members : mDNSNULL; cr; cr=cr->next)
+					if (SameNameRecordAnswersQuestion(&cr->resrec, &pktq) && cr->resrec.rdlength <= SmallRecordLimit)
+						if (!cr->NextInKAList && eap != &cr->NextInKAList)
+							{
+							*eap = cr;
+							eap = &cr->NextInKAList;
+#if ENABLE_MULTI_PACKET_QUERY_SNOOPING
+							if (cr->MPUnansweredQ == 0 || m->timenow - cr->MPLastUnansweredQT >= mDNSPlatformOneSecond)
+								{
+								// Although MPUnansweredQ is only really used for multi-packet query processing,
+								// we increment it for both single-packet and multi-packet queries, so that it stays in sync
+								// with the MPUnansweredKA value, which by necessity is incremented for both query types.
+								cr->MPUnansweredQ++;
+								cr->MPLastUnansweredQT = m->timenow;
+								cr->MPExpectingKA = mDNStrue;
+								}
+#endif
+							}
+	
+			// Check if this question is the same as any of mine.
+			// We only do this for non-truncated queries. Right now it would be too complicated to try
+			// to keep track of duplicate suppression state between multiple packets, especially when we
+			// can't guarantee to receive all of the Known Answer packets that go with a particular query.
+#if ENABLE_MULTI_PACKET_QUERY_SNOOPING
+			if (!(query->h.flags.b[0] & kDNSFlag0_TC))
+#endif
+				for (q = m->Questions; q; q=q->next)
+					if (!q->Target.type && ActiveQuestion(q) && m->timenow - q->LastQTxTime > mDNSPlatformOneSecond / 4)
+						if (!q->InterfaceID || q->InterfaceID == InterfaceID)
+							if (q->NextInDQList == mDNSNULL && dqp != &q->NextInDQList)
+								if (q->qtype == pktq.qtype &&
+									q->qclass == pktq.qclass &&
+									q->qnamehash == pktq.qnamehash && SameDomainName(&q->qname, &pktq.qname))
+									{ *dqp = q; dqp = &q->NextInDQList; }
+			}
+		}
+
+	// ***
+	// *** 3. Now we can safely build the list of marked answers
+	// ***
+	for (rr = m->ResourceRecords; rr; rr=rr->next)				// Now build our list of potential answers
+		if (rr->NR_AnswerTo)									// If we marked the record...
+			AddRecordToResponseList(&nrp, rr, mDNSNULL);		// ... add it to the list
+
+	// ***
+	// *** 4. Add additional records
+	// ***
+	AddAdditionalsToResponseList(m, ResponseRecords, &nrp, InterfaceID);
+
+	// ***
+	// *** 5. Parse Answer Section and cancel any records disallowed by Known-Answer list
+	// ***
+	for (i=0; i<query->h.numAnswers; i++)						// For each record in the query's answer section...
+		{
+		// Get the record...
+		CacheRecord *ourcacherr;
+		ptr = GetLargeResourceRecord(m, query, ptr, end, InterfaceID, kDNSRecordTypePacketAns, &m->rec);
+		if (!ptr) goto exit;
+		if (m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative)
+			{
+			// See if this Known-Answer suppresses any of our currently planned answers
+			for (rr=ResponseRecords; rr; rr=rr->NextResponse)
+				if (MustSendRecord(rr) && ShouldSuppressKnownAnswer(&m->rec.r, rr))
+					{ rr->NR_AnswerTo = mDNSNULL; rr->NR_AdditionalTo = mDNSNULL; }
+	
+			// See if this Known-Answer suppresses any previously scheduled answers (for multi-packet KA suppression)
+			for (rr=m->ResourceRecords; rr; rr=rr->next)
+				{
+				// If we're planning to send this answer on this interface, and only on this interface, then allow KA suppression
+				if (rr->ImmedAnswer == InterfaceID && ShouldSuppressKnownAnswer(&m->rec.r, rr))
+					{
+					if (srcaddr->type == mDNSAddrType_IPv4)
+						{
+						if (mDNSSameIPv4Address(rr->v4Requester, srcaddr->ip.v4)) rr->v4Requester = zerov4Addr;
+						}
+					else if (srcaddr->type == mDNSAddrType_IPv6)
+						{
+						if (mDNSSameIPv6Address(rr->v6Requester, srcaddr->ip.v6)) rr->v6Requester = zerov6Addr;
+						}
+					if (mDNSIPv4AddressIsZero(rr->v4Requester) && mDNSIPv6AddressIsZero(rr->v6Requester))
+						{
+						rr->ImmedAnswer  = mDNSNULL;
+						rr->ImmedUnicast = mDNSfalse;
+	#if MDNS_LOG_ANSWER_SUPPRESSION_TIMES
+						LogMsg("Suppressed after%4d: %s", m->timenow - rr->ImmedAnswerMarkTime, ARDisplayString(m, rr));
+	#endif
+						}
+					}
+				}
+	
+			ourcacherr = FindIdenticalRecordInCache(m, &m->rec.r.resrec);
+	
+	#if ENABLE_MULTI_PACKET_QUERY_SNOOPING
+			// See if this Known-Answer suppresses any answers we were expecting for our cache records. We do this always,
+			// even if the TC bit is not set (the TC bit will *not* be set in the *last* packet of a multi-packet KA list).
+			if (ourcacherr && ourcacherr->MPExpectingKA && m->timenow - ourcacherr->MPLastUnansweredQT < mDNSPlatformOneSecond)
+				{
+				ourcacherr->MPUnansweredKA++;
+				ourcacherr->MPExpectingKA = mDNSfalse;
+				}
+	#endif
+	
+			// Having built our ExpectedAnswers list from the questions in this packet, we then remove
+			// any records that are suppressed by the Known Answer list in this packet.
+			eap = &ExpectedAnswers;
+			while (*eap)
+				{
+				CacheRecord *cr = *eap;
+				if (cr->resrec.InterfaceID == InterfaceID && IdenticalResourceRecord(&m->rec.r.resrec, &cr->resrec))
+					{ *eap = cr->NextInKAList; cr->NextInKAList = mDNSNULL; }
+				else eap = &cr->NextInKAList;
+				}
+			
+			// See if this Known-Answer is a surprise to us. If so, we shouldn't suppress our own query.
+			if (!ourcacherr)
+				{
+				dqp = &DupQuestions;
+				while (*dqp)
+					{
+					DNSQuestion *q = *dqp;
+					if (ResourceRecordAnswersQuestion(&m->rec.r.resrec, q))
+						{ *dqp = q->NextInDQList; q->NextInDQList = mDNSNULL; }
+					else dqp = &q->NextInDQList;
+					}
+				}
+			}
+		m->rec.r.resrec.RecordType = 0;		// Clear RecordType to show we're not still using it
+		}
+
+	// ***
+	// *** 6. Cancel any additionals that were added because of now-deleted records
+	// ***
+	for (rr=ResponseRecords; rr; rr=rr->NextResponse)
+		if (rr->NR_AdditionalTo && !MustSendRecord(rr->NR_AdditionalTo))
+			{ rr->NR_AnswerTo = mDNSNULL; rr->NR_AdditionalTo = mDNSNULL; }
+
+	// ***
+	// *** 7. Mark the send flags on the records we plan to send
+	// ***
+	for (rr=ResponseRecords; rr; rr=rr->NextResponse)
+		{
+		if (rr->NR_AnswerTo)
+			{
+			mDNSBool SendMulticastResponse = mDNSfalse;		// Send modern multicast response
+			mDNSBool SendUnicastResponse   = mDNSfalse;		// Send modern unicast response (not legacy unicast response)
+			
+			// If it's been a while since we multicast this, then send a multicast response for conflict detection, etc.
+			if (m->timenow - (rr->LastMCTime + TicksTTL(rr)/4) >= 0)
+				{
+				SendMulticastResponse = mDNStrue;
+				// If this record was marked for modern (delayed) unicast response, then mark it as promoted to
+				// multicast response instead (don't want to end up ALSO setting SendUnicastResponse in the check below).
+				// If this record was marked for legacy unicast response, then we mustn't change the NR_AnswerTo value.
+				if (rr->NR_AnswerTo == (mDNSu8*)~1) rr->NR_AnswerTo = (mDNSu8*)~0;
+				}
+			
+			// If the client insists on a multicast response, then we'd better send one
+			if      (rr->NR_AnswerTo == (mDNSu8*)~0) SendMulticastResponse = mDNStrue;
+			else if (rr->NR_AnswerTo == (mDNSu8*)~1) SendUnicastResponse   = mDNStrue;
+			else if (rr->NR_AnswerTo)                SendLegacyResponse    = mDNStrue;
+	
+			if (SendMulticastResponse || SendUnicastResponse)
+				{
+#if MDNS_LOG_ANSWER_SUPPRESSION_TIMES
+				rr->ImmedAnswerMarkTime = m->timenow;
+#endif
+				m->NextScheduledResponse = m->timenow;
+				// If we're already planning to send this on another interface, just send it on all interfaces
+				if (rr->ImmedAnswer && rr->ImmedAnswer != InterfaceID)
+					rr->ImmedAnswer = mDNSInterfaceMark;
+				else
+					{
+					rr->ImmedAnswer = InterfaceID;			// Record interface to send it on
+					if (SendUnicastResponse) rr->ImmedUnicast = mDNStrue;
+					if (srcaddr->type == mDNSAddrType_IPv4)
+						{
+						if      (mDNSIPv4AddressIsZero(rr->v4Requester))                rr->v4Requester = srcaddr->ip.v4;
+						else if (!mDNSSameIPv4Address(rr->v4Requester, srcaddr->ip.v4)) rr->v4Requester = onesIPv4Addr;
+						}
+					else if (srcaddr->type == mDNSAddrType_IPv6)
+						{
+						if      (mDNSIPv6AddressIsZero(rr->v6Requester))                rr->v6Requester = srcaddr->ip.v6;
+						else if (!mDNSSameIPv6Address(rr->v6Requester, srcaddr->ip.v6)) rr->v6Requester = onesIPv6Addr;
+						}
+					}
+				}
+			// If TC flag is set, it means we should expect that additional known answers may be coming in another packet,
+			// so we allow roughly half a second before deciding to reply (we've observed inter-packet delays of 100-200ms on 802.11)
+			// else, if record is a shared one, spread responses over 100ms to avoid implosion of simultaneous responses
+			// else, for a simple unique record reply, we can reply immediately; no need for delay
+			if      (query->h.flags.b[0] & kDNSFlag0_TC)            delayresponse = mDNSPlatformOneSecond * 20;	// Divided by 50 = 400ms
+			else if (rr->resrec.RecordType == kDNSRecordTypeShared) delayresponse = mDNSPlatformOneSecond;		// Divided by 50 = 20ms
+			}
+		else if (rr->NR_AdditionalTo && rr->NR_AdditionalTo->NR_AnswerTo == (mDNSu8*)~0)
+			{
+			// Since additional records are an optimization anyway, we only ever send them on one interface at a time
+			// If two clients on different interfaces do queries that invoke the same optional additional answer,
+			// then the earlier client is out of luck
+			rr->ImmedAdditional = InterfaceID;
+			// No need to set m->NextScheduledResponse here
+			// We'll send these additional records when we send them, or not, as the case may be
+			}
+		}
+
+	// ***
+	// *** 8. If we think other machines are likely to answer these questions, set our packet suppression timer
+	// ***
+	if (delayresponse && (!m->SuppressSending || (m->SuppressSending - m->timenow) < (delayresponse + 49) / 50))
+		{
+#if MDNS_LOG_ANSWER_SUPPRESSION_TIMES
+		mDNSs32 oldss = m->SuppressSending;
+		if (oldss && delayresponse)
+			LogMsg("Current SuppressSending delay%5ld; require%5ld", m->SuppressSending - m->timenow, (delayresponse + 49) / 50);
+#endif
+		// Pick a random delay:
+		// We start with the base delay chosen above (typically either 1 second or 20 seconds),
+		// and add a random value in the range 0-5 seconds (making 1-6 seconds or 20-25 seconds).
+		// This is an integer value, with resolution determined by the platform clock rate.
+		// We then divide that by 50 to get the delay value in ticks. We defer the division until last
+		// to get better results on platforms with coarse clock granularity (e.g. ten ticks per second).
+		// The +49 before dividing is to ensure we round up, not down, to ensure that even
+		// on platforms where the native clock rate is less than fifty ticks per second,
+		// we still guarantee that the final calculated delay is at least one platform tick.
+		// We want to make sure we don't ever allow the delay to be zero ticks,
+		// because if that happens we'll fail the Bonjour Conformance Test.
+		// Our final computed delay is 20-120ms for normal delayed replies,
+		// or 400-500ms in the case of multi-packet known-answer lists.
+		m->SuppressSending = m->timenow + (delayresponse + (mDNSs32)mDNSRandom((mDNSu32)mDNSPlatformOneSecond*5) + 49) / 50;
+		if (m->SuppressSending == 0) m->SuppressSending = 1;
+#if MDNS_LOG_ANSWER_SUPPRESSION_TIMES
+		if (oldss && delayresponse)
+			LogMsg("Set     SuppressSending to   %5ld", m->SuppressSending - m->timenow);
+#endif
+		}
+
+	// ***
+	// *** 9. If query is from a legacy client, or from a new client requesting a unicast reply, then generate a unicast response too
+	// ***
+	if (SendLegacyResponse)
+		responseptr = GenerateUnicastResponse(query, end, InterfaceID, LegacyQuery, response, ResponseRecords);
+
+exit:
+	m->rec.r.resrec.RecordType = 0;		// Clear RecordType to show we're not still using it
+	
+	// ***
+	// *** 10. Finally, clear our link chains ready for use next time
+	// ***
+	while (ResponseRecords)
+		{
+		rr = ResponseRecords;
+		ResponseRecords = rr->NextResponse;
+		rr->NextResponse    = mDNSNULL;
+		rr->NR_AnswerTo     = mDNSNULL;
+		rr->NR_AdditionalTo = mDNSNULL;
+		}
+	
+	while (ExpectedAnswers)
+		{
+		CacheRecord *cr = ExpectedAnswers;
+		ExpectedAnswers = cr->NextInKAList;
+		cr->NextInKAList = mDNSNULL;
+		
+		// For non-truncated queries, we can definitively say that we should expect
+		// to be seeing a response for any records still left in the ExpectedAnswers list
+		if (!(query->h.flags.b[0] & kDNSFlag0_TC))
+			if (cr->UnansweredQueries == 0 || m->timenow - cr->LastUnansweredTime >= mDNSPlatformOneSecond)
+				{
+				cr->UnansweredQueries++;
+				cr->LastUnansweredTime = m->timenow;
+#if ENABLE_MULTI_PACKET_QUERY_SNOOPING
+				if (cr->UnansweredQueries > 1)
+					debugf("ProcessQuery: (!TC) UAQ %lu MPQ %lu MPKA %lu %s",
+						cr->UnansweredQueries, cr->MPUnansweredQ, cr->MPUnansweredKA, CRDisplayString(m, cr));
+#endif
+				SetNextCacheCheckTimeForRecord(m, cr);
+				}
+
+		// If we've seen multiple unanswered queries for this record,
+		// then mark it to expire in five seconds if we don't get a response by then.
+		if (cr->UnansweredQueries >= MaxUnansweredQueries)
+			{
+#if ENABLE_MULTI_PACKET_QUERY_SNOOPING
+			// Only show debugging message if this record was not about to expire anyway
+			if (RRExpireTime(cr) - m->timenow > 4 * mDNSPlatformOneSecond)
+				debugf("ProcessQuery: (Max) UAQ %lu MPQ %lu MPKA %lu mDNS_Reconfirm() for %s",
+					cr->UnansweredQueries, cr->MPUnansweredQ, cr->MPUnansweredKA, CRDisplayString(m, cr));
+#endif
+			mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForNoAnswer);
+			}
+#if ENABLE_MULTI_PACKET_QUERY_SNOOPING
+		// Make a guess, based on the multi-packet query / known answer counts, whether we think we
+		// should have seen an answer for this. (We multiply MPQ by 4 and MPKA by 5, to allow for
+		// possible packet loss of up to 20% of the additional KA packets.)
+		else if (cr->MPUnansweredQ * 4 > cr->MPUnansweredKA * 5 + 8)
+			{
+			// We want to do this conservatively.
+			// If there are so many machines on the network that they have to use multi-packet known-answer lists,
+			// then we don't want them to all hit the network simultaneously with their final expiration queries.
+			// By setting the record to expire in four minutes, we achieve two things:
+			// (a) the 90-95% final expiration queries will be less bunched together
+			// (b) we allow some time for us to witness enough other failed queries that we don't have to do our own
+			mDNSu32 remain = (mDNSu32)(RRExpireTime(cr) - m->timenow) / 4;
+			if (remain > 240 * (mDNSu32)mDNSPlatformOneSecond)
+				remain = 240 * (mDNSu32)mDNSPlatformOneSecond;
+			
+			// Only show debugging message if this record was not about to expire anyway
+			if (RRExpireTime(cr) - m->timenow > 4 * mDNSPlatformOneSecond)
+				debugf("ProcessQuery: (MPQ) UAQ %lu MPQ %lu MPKA %lu mDNS_Reconfirm() for %s",
+					cr->UnansweredQueries, cr->MPUnansweredQ, cr->MPUnansweredKA, CRDisplayString(m, cr));
+
+			if (remain <= 60 * (mDNSu32)mDNSPlatformOneSecond)
+				cr->UnansweredQueries++;	// Treat this as equivalent to one definite unanswered query
+			cr->MPUnansweredQ  = 0;			// Clear MPQ/MPKA statistics
+			cr->MPUnansweredKA = 0;
+			cr->MPExpectingKA  = mDNSfalse;
+			
+			if (remain < kDefaultReconfirmTimeForNoAnswer)
+				remain = kDefaultReconfirmTimeForNoAnswer;
+			mDNS_Reconfirm_internal(m, cr, remain);
+			}
+#endif
+		}
+	
+	while (DupQuestions)
+		{
+		DNSQuestion *q = DupQuestions;
+		DupQuestions = q->NextInDQList;
+		q->NextInDQList = mDNSNULL;
+		i = RecordDupSuppressInfo(q->DupSuppress, m->timenow, InterfaceID, srcaddr->type);
+		debugf("ProcessQuery: Recorded DSI for %##s (%s) on %p/%s %d", q->qname.c, DNSTypeName(q->qtype), InterfaceID,
+			srcaddr->type == mDNSAddrType_IPv4 ? "v4" : "v6", i);
+		}
+	
+	return(responseptr);
+	}
+
+mDNSlocal void mDNSCoreReceiveQuery(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end,
+	const mDNSAddr *srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport,
+	const mDNSInterfaceID InterfaceID)
+	{
+	mDNSu8    *responseend = mDNSNULL;
+	mDNSBool   QueryWasLocalUnicast = srcaddr && dstaddr &&
+		!mDNSAddrIsDNSMulticast(dstaddr) && mDNS_AddressIsLocalSubnet(m, InterfaceID, srcaddr);
+	
+	if (!InterfaceID && dstaddr && mDNSAddrIsDNSMulticast(dstaddr))
+		{
+		LogMsg("Ignoring Query from %#-15a:%-5d to %#-15a:%-5d on 0x%p with "
+			"%2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s %d bytes (Multicast, but no InterfaceID)",
+			srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), InterfaceID,
+			msg->h.numQuestions,   msg->h.numQuestions   == 1 ? ", "   : "s,",
+			msg->h.numAnswers,     msg->h.numAnswers     == 1 ? ", "   : "s,",
+			msg->h.numAuthorities, msg->h.numAuthorities == 1 ? "y,  " : "ies,",
+			msg->h.numAdditionals, msg->h.numAdditionals == 1 ? " "    : "s", end - msg->data);
+		return;
+		}
+
+	verbosedebugf("Received Query from %#-15a:%-5d to %#-15a:%-5d on 0x%p with "
+		"%2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s %d bytes",
+		srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), InterfaceID,
+		msg->h.numQuestions,   msg->h.numQuestions   == 1 ? ", "   : "s,",
+		msg->h.numAnswers,     msg->h.numAnswers     == 1 ? ", "   : "s,",
+		msg->h.numAuthorities, msg->h.numAuthorities == 1 ? "y,  " : "ies,",
+		msg->h.numAdditionals, msg->h.numAdditionals == 1 ? " "    : "s", end - msg->data);
+	
+	responseend = ProcessQuery(m, msg, end, srcaddr, InterfaceID,
+		!mDNSSameIPPort(srcport, MulticastDNSPort), mDNSAddrIsDNSMulticast(dstaddr), QueryWasLocalUnicast, &m->omsg);
+
+	if (responseend)	// If responseend is non-null, that means we built a unicast response packet
+		{
+		debugf("Unicast Response: %d Question%s, %d Answer%s, %d Additional%s to %#-15a:%d on %p/%ld",
+			m->omsg.h.numQuestions,   m->omsg.h.numQuestions   == 1 ? "" : "s",
+			m->omsg.h.numAnswers,     m->omsg.h.numAnswers     == 1 ? "" : "s",
+			m->omsg.h.numAdditionals, m->omsg.h.numAdditionals == 1 ? "" : "s",
+			srcaddr, mDNSVal16(srcport), InterfaceID, srcaddr->type);
+		mDNSSendDNSMessage(m, &m->omsg, responseend, InterfaceID, mDNSNULL, srcaddr, srcport, mDNSNULL, mDNSNULL);
+		}
+	}
+
+#if 0
+mDNSlocal mDNSBool TrustedSource(const mDNS *const m, const mDNSAddr *const srcaddr)
+	{
+	DNSServer *s;
+	(void)m; // Unused
+	(void)srcaddr; // Unused
+	for (s = m->DNSServers; s; s = s->next)
+		if (mDNSSameAddress(srcaddr, &s->addr)) return(mDNStrue);
+	return(mDNSfalse);
+	}
+#endif
+
+struct UDPSocket_struct
+	{
+	mDNSIPPort port; // MUST BE FIRST FIELD -- mDNSCoreReceive expects every UDPSocket_struct to begin with mDNSIPPort port
+	};
+
+mDNSlocal DNSQuestion *ExpectingUnicastResponseForQuestion(const mDNS *const m, const mDNSIPPort port, const mDNSOpaque16 id, const DNSQuestion *const question, mDNSBool tcp)
+	{
+	DNSQuestion *q;
+	for (q = m->Questions; q; q=q->next)
+		{
+		if (!tcp && !q->LocalSocket) continue;
+		if (mDNSSameIPPort(tcp ? q->tcpSrcPort : q->LocalSocket->port, port)     &&
+			mDNSSameOpaque16(q->TargetQID,         id)       &&
+			q->qtype                  == question->qtype     &&
+			q->qclass                 == question->qclass    &&
+			q->qnamehash              == question->qnamehash &&
+			SameDomainName(&q->qname, &question->qname))
+			return(q);
+		}
+	return(mDNSNULL);
+	}
+
+// This function is called when we receive a unicast response. This could be the case of a unicast response from the
+// DNS server or a response to the QU query. Hence, the cache record's InterfaceId can be both NULL or non-NULL (QU case)
+mDNSlocal DNSQuestion *ExpectingUnicastResponseForRecord(mDNS *const m,
+	const mDNSAddr *const srcaddr, const mDNSBool SrcLocal, const mDNSIPPort port, const mDNSOpaque16 id, const CacheRecord *const rr, mDNSBool tcp)
+	{
+	DNSQuestion *q;
+	(void)id;
+	(void)srcaddr;
+
+	for (q = m->Questions; q; q=q->next)
+		{
+		if (!q->DuplicateOf && ResourceRecordAnswersUnicastResponse(&rr->resrec, q))
+			{
+			if (!mDNSOpaque16IsZero(q->TargetQID))
+				{
+				debugf("ExpectingUnicastResponseForRecord msg->h.id %d q->TargetQID %d for %s", mDNSVal16(id), mDNSVal16(q->TargetQID), CRDisplayString(m, rr));
+
+				if (mDNSSameOpaque16(q->TargetQID, id))
+					{
+					mDNSIPPort srcp;
+					if (!tcp)
+						{
+						srcp = q->LocalSocket ? q->LocalSocket->port : zeroIPPort;
+						}
+					else
+						{
+						srcp = q->tcpSrcPort;
+						}
+					if (mDNSSameIPPort(srcp, port)) return(q);
+					
+				//	if (mDNSSameAddress(srcaddr, &q->Target))                   return(mDNStrue);
+				//	if (q->LongLived && mDNSSameAddress(srcaddr, &q->servAddr)) return(mDNStrue); Shouldn't need this now that we have LLQType checking
+				//	if (TrustedSource(m, srcaddr))                              return(mDNStrue);
+					LogInfo("WARNING: Ignoring suspect uDNS response for %##s (%s) [q->Target %#a:%d] from %#a:%d %s",
+						q->qname.c, DNSTypeName(q->qtype), &q->Target, mDNSVal16(srcp), srcaddr, mDNSVal16(port), CRDisplayString(m, rr));
+					return(mDNSNULL);
+					}
+				}
+			else
+				{
+				if (SrcLocal && q->ExpectUnicastResp && (mDNSu32)(m->timenow - q->ExpectUnicastResp) < (mDNSu32)(mDNSPlatformOneSecond*2))
+					return(q);
+				}
+			}
+		}
+	return(mDNSNULL);
+	}
+
+// Certain data types need more space for in-memory storage than their in-packet rdlength would imply
+// Currently this applies only to rdata types containing more than one domainname,
+// or types where the domainname is not the last item in the structure.
+// In addition, NSEC currently requires less space for in-memory storage than its in-packet representation.
+mDNSlocal mDNSu16 GetRDLengthMem(const ResourceRecord *const rr)
+	{
+	switch (rr->rrtype)
+		{
+		case kDNSType_SOA: return sizeof(rdataSOA);
+		case kDNSType_RP:  return sizeof(rdataRP);
+		case kDNSType_PX:  return sizeof(rdataPX);
+		case kDNSType_NSEC:return sizeof(rdataNSEC);
+		default:           return rr->rdlength;
+		}
+	}
+
+mDNSexport CacheRecord *CreateNewCacheEntry(mDNS *const m, const mDNSu32 slot, CacheGroup *cg, mDNSs32 delay)
+	{
+	CacheRecord *rr = mDNSNULL;
+	mDNSu16 RDLength = GetRDLengthMem(&m->rec.r.resrec);
+
+	if (!m->rec.r.resrec.InterfaceID) debugf("CreateNewCacheEntry %s", CRDisplayString(m, &m->rec.r));
+
+	//if (RDLength > InlineCacheRDSize)
+	//	LogInfo("Rdata len %4d > InlineCacheRDSize %d %s", RDLength, InlineCacheRDSize, CRDisplayString(m, &m->rec.r));
+
+	if (!cg) cg = GetCacheGroup(m, slot, &m->rec.r.resrec);	// If we don't have a CacheGroup for this name, make one now
+	if (cg)  rr = GetCacheRecord(m, cg, RDLength);	// Make a cache record, being careful not to recycle cg
+	if (!rr) NoCacheAnswer(m, &m->rec.r);
+	else
+		{
+		RData *saveptr = rr->resrec.rdata;		// Save the rr->resrec.rdata pointer
+		*rr = m->rec.r;							// Block copy the CacheRecord object
+		rr->resrec.rdata  = saveptr;				// Restore rr->resrec.rdata after the structure assignment
+		rr->resrec.name   = cg->name;			// And set rr->resrec.name to point into our CacheGroup header
+		rr->DelayDelivery = delay;
+
+		// If this is an oversized record with external storage allocated, copy rdata to external storage
+		if      (rr->resrec.rdata == (RData*)&rr->smallrdatastorage && RDLength > InlineCacheRDSize)
+			LogMsg("rr->resrec.rdata == &rr->rdatastorage but length > InlineCacheRDSize %##s", m->rec.r.resrec.name->c);
+		else if (rr->resrec.rdata != (RData*)&rr->smallrdatastorage && RDLength <= InlineCacheRDSize)
+			LogMsg("rr->resrec.rdata != &rr->rdatastorage but length <= InlineCacheRDSize %##s", m->rec.r.resrec.name->c);
+		if (RDLength > InlineCacheRDSize)
+			mDNSPlatformMemCopy(rr->resrec.rdata, m->rec.r.resrec.rdata, sizeofRDataHeader + RDLength);
+
+		rr->next = mDNSNULL;					// Clear 'next' pointer
+		*(cg->rrcache_tail) = rr;				// Append this record to tail of cache slot list
+		cg->rrcache_tail = &(rr->next);			// Advance tail pointer
+
+		CacheRecordAdd(m, rr);	// CacheRecordAdd calls SetNextCacheCheckTimeForRecord(m, rr); for us
+		}
+	return(rr);
+	}
+
+mDNSlocal void RefreshCacheRecord(mDNS *const m, CacheRecord *rr, mDNSu32 ttl)
+	{
+	rr->TimeRcvd             = m->timenow;
+	rr->resrec.rroriginalttl = ttl;
+	rr->UnansweredQueries = 0;
+#if ENABLE_MULTI_PACKET_QUERY_SNOOPING
+	rr->MPUnansweredQ     = 0;
+	rr->MPUnansweredKA    = 0;
+	rr->MPExpectingKA     = mDNSfalse;
+#endif
+	SetNextCacheCheckTimeForRecord(m, rr);
+	}
+
+mDNSexport void GrantCacheExtensions(mDNS *const m, DNSQuestion *q, mDNSu32 lease)
+	{
+	CacheRecord *rr;
+	const mDNSu32 slot = HashSlot(&q->qname);
+	CacheGroup *cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname);
+	for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
+		if (rr->CRActiveQuestion == q)
+			{
+			//LogInfo("GrantCacheExtensions: new lease %d / %s", lease, CRDisplayString(m, rr));
+			RefreshCacheRecord(m, rr, lease);
+			}
+	}
+
+mDNSlocal mDNSu32 GetEffectiveTTL(const uDNS_LLQType LLQType, mDNSu32 ttl)		// TTL in seconds
+	{
+	if      (LLQType == uDNS_LLQ_Entire) ttl = kLLQ_DefLease;
+	else if (LLQType == uDNS_LLQ_Events)
+		{
+		// If the TTL is -1 for uDNS LLQ event packet, that means "remove"
+		if (ttl == 0xFFFFFFFF) ttl = 0;
+		else                   ttl = kLLQ_DefLease;
+		}
+	else	// else not LLQ (standard uDNS response)
+		{
+		// The TTL is already capped to a maximum value in GetLargeResourceRecord, but just to be extra safe we
+		// also do this check here to make sure we can't get overflow below when we add a quarter to the TTL
+		if (ttl > 0x60000000UL / mDNSPlatformOneSecond) ttl = 0x60000000UL / mDNSPlatformOneSecond;
+
+		// Adjustment factor to avoid race condition:
+		// Suppose real record as TTL of 3600, and our local caching server has held it for 3500 seconds, so it returns an aged TTL of 100.
+		// If we do our normal refresh at 80% of the TTL, our local caching server will return 20 seconds, so we'll do another
+		// 80% refresh after 16 seconds, and then the server will return 4 seconds, and so on, in the fashion of Zeno's paradox.
+		// To avoid this, we extend the record's effective TTL to give it a little extra grace period.
+		// We adjust the 100 second TTL to 126. This means that when we do our 80% query at 101 seconds,
+		// the cached copy at our local caching server will already have expired, so the server will be forced
+		// to fetch a fresh copy from the authoritative server, and then return a fresh record with the full TTL of 3600 seconds.
+		ttl += ttl/4 + 2;
+
+		// For mDNS, TTL zero means "delete this record"
+		// For uDNS, TTL zero means: this data is true at this moment, but don't cache it.
+		// For the sake of network efficiency, we impose a minimum effective TTL of 15 seconds.
+		// This means that we'll do our 80, 85, 90, 95% queries at 12.00, 12.75, 13.50, 14.25 seconds
+		// respectively, and then if we get no response, delete the record from the cache at 15 seconds.
+		// This gives the server up to three seconds to respond between when we send our 80% query at 12 seconds
+		// and when we delete the record at 15 seconds. Allowing cache lifetimes less than 15 seconds would
+		// (with the current code) result in the server having even less than three seconds to respond
+		// before we deleted the record and reported a "remove" event to any active questions.
+		// Furthermore, with the current code, if we were to allow a TTL of less than 2 seconds
+		// then things really break (e.g. we end up making a negative cache entry).
+		// In the future we may want to revisit this and consider properly supporting non-cached (TTL=0) uDNS answers.
+		if (ttl < 15) ttl = 15;
+		}
+	
+	return ttl;
+	}
+
+// Note: mDNSCoreReceiveResponse calls mDNS_Deregister_internal which can call a user callback, which may change
+// the record list and/or question list.
+// Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
+// InterfaceID non-NULL tells us the interface this multicast response was received on
+// InterfaceID NULL tells us this was a unicast response
+// dstaddr NULL tells us we received this over an outgoing TCP connection we made
+mDNSlocal void mDNSCoreReceiveResponse(mDNS *const m,
+	const DNSMessage *const response, const mDNSu8 *end,
+	const mDNSAddr *srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport,
+	const mDNSInterfaceID InterfaceID)
+	{
+	int i;
+	mDNSBool ResponseMCast    = dstaddr && mDNSAddrIsDNSMulticast(dstaddr);
+	mDNSBool ResponseSrcLocal = !srcaddr || mDNS_AddressIsLocalSubnet(m, InterfaceID, srcaddr);
+	DNSQuestion *llqMatch = mDNSNULL;
+	uDNS_LLQType LLQType      = uDNS_recvLLQResponse(m, response, end, srcaddr, srcport, &llqMatch);
+
+	// "(CacheRecord*)1" is a special (non-zero) end-of-list marker
+	// We use this non-zero marker so that records in our CacheFlushRecords list will always have NextInCFList
+	// set non-zero, and that tells GetCacheEntity() that they're not, at this moment, eligible for recycling.
+	CacheRecord *CacheFlushRecords = (CacheRecord*)1;
+	CacheRecord **cfp = &CacheFlushRecords;
+
+	// All records in a DNS response packet are treated as equally valid statements of truth. If we want
+	// to guard against spoof responses, then the only credible protection against that is cryptographic
+	// security, e.g. DNSSEC., not worring about which section in the spoof packet contained the record
+	int firstauthority  =                   response->h.numAnswers;
+	int firstadditional = firstauthority  + response->h.numAuthorities;
+	int totalrecords    = firstadditional + response->h.numAdditionals;
+	const mDNSu8 *ptr   = response->data;
+	DNSServer *uDNSServer = mDNSNULL;
+
+	debugf("Received Response from %#-15a addressed to %#-15a on %p with "
+		"%2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s %d bytes LLQType %d",
+		srcaddr, dstaddr, InterfaceID,
+		response->h.numQuestions,   response->h.numQuestions   == 1 ? ", "   : "s,",
+		response->h.numAnswers,     response->h.numAnswers     == 1 ? ", "   : "s,",
+		response->h.numAuthorities, response->h.numAuthorities == 1 ? "y,  " : "ies,",
+		response->h.numAdditionals, response->h.numAdditionals == 1 ? " "    : "s", end - response->data, LLQType);
+
+	// According to RFC 2181 <http://www.ietf.org/rfc/rfc2181.txt>
+	//    When a DNS client receives a reply with TC
+	//    set, it should ignore that response, and query again, using a
+	//    mechanism, such as a TCP connection, that will permit larger replies.
+	// It feels wrong to be throwing away data after the network went to all the trouble of delivering it to us, but
+	// delivering some records of the RRSet first and then the remainder a couple of milliseconds later was causing
+	// failures in our Microsoft Active Directory client, which expects to get the entire set of answers at once.
+	// <rdar://problem/6690034> Can't bind to Active Directory
+	// In addition, if the client immediately canceled its query after getting the initial partial response, then we'll
+	// abort our TCP connection, and not complete the operation, and end up with an incomplete RRSet in our cache.
+	// Next time there's a query for this RRSet we'll see answers in our cache, and assume we have the whole RRSet already,
+	// and not even do the TCP query.
+	// Accordingly, if we get a uDNS reply with kDNSFlag0_TC set, we bail out and wait for the TCP response containing the entire RRSet.
+	if (!InterfaceID && (response->h.flags.b[0] & kDNSFlag0_TC)) return;
+
+	if (LLQType == uDNS_LLQ_Ignore) return;
+
+	// 1. We ignore questions (if any) in mDNS response packets
+	// 2. If this is an LLQ response, we handle it much the same
+	// 3. If we get a uDNS UDP response with the TC (truncated) bit set, then we can't treat this
+	//    answer as being the authoritative complete RRSet, and respond by deleting all other
+	//    matching cache records that don't appear in this packet.
+	// Otherwise, this is a authoritative uDNS answer, so arrange for any stale records to be purged
+	if (ResponseMCast || LLQType == uDNS_LLQ_Events || (response->h.flags.b[0] & kDNSFlag0_TC))
+		ptr = LocateAnswers(response, end);
+	// Otherwise, for one-shot queries, any answers in our cache that are not also contained
+	// in this response packet are immediately deemed to be invalid.
+	else
+		{
+		mDNSu8 rcode = (mDNSu8)(response->h.flags.b[1] & kDNSFlag1_RC_Mask);
+		mDNSBool failure = !(rcode == kDNSFlag1_RC_NoErr || rcode == kDNSFlag1_RC_NXDomain || rcode == kDNSFlag1_RC_NotAuth);
+		mDNSBool returnEarly = mDNSfalse;
+		// We could possibly combine this with the similar loop at the end of this function --
+		// instead of tagging cache records here and then rescuing them if we find them in the answer section,
+		// we could instead use the "m->PktNum" mechanism to tag each cache record with the packet number in
+		// which it was received (or refreshed), and then at the end if we find any cache records which
+		// answer questions in this packet's question section, but which aren't tagged with this packet's
+		// packet number, then we deduce they are old and delete them
+		for (i = 0; i < response->h.numQuestions && ptr && ptr < end; i++)
+			{
+			DNSQuestion q, *qptr = mDNSNULL;
+			ptr = getQuestion(response, ptr, end, InterfaceID, &q);
+			if (ptr && (qptr = ExpectingUnicastResponseForQuestion(m, dstport, response->h.id, &q, !dstaddr)))
+				{
+				if (!failure)
+					{
+					CacheRecord *rr;
+					const mDNSu32 slot = HashSlot(&q.qname);
+					CacheGroup *cg = CacheGroupForName(m, slot, q.qnamehash, &q.qname);
+					for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
+						if (SameNameRecordAnswersQuestion(&rr->resrec, qptr))
+							{
+							debugf("uDNS marking %p %##s (%s) %p %s", q.InterfaceID, q.qname.c, DNSTypeName(q.qtype),
+								rr->resrec.InterfaceID, CRDisplayString(m, rr));
+							// Don't want to disturb rroriginalttl here, because code below might need it for the exponential backoff doubling algorithm
+							rr->TimeRcvd          = m->timenow - TicksTTL(rr) - 1;
+							rr->UnansweredQueries = MaxUnansweredQueries;
+							}
+					}
+				else
+					{
+					if (qptr)
+						{
+						LogInfo("mDNSCoreReceiveResponse: Server %p responded with code %d to query %##s (%s)", qptr->qDNSServer, rcode, q.qname.c, DNSTypeName(q.qtype));
+						PenalizeDNSServer(m, qptr);
+						}
+					returnEarly = mDNStrue;
+					}
+				}
+			}
+		if (returnEarly)
+			{
+			LogInfo("Ignoring %2d Answer%s %2d Authorit%s %2d Additional%s",
+				response->h.numAnswers,     response->h.numAnswers     == 1 ? ", " : "s,",
+				response->h.numAuthorities, response->h.numAuthorities == 1 ? "y,  " : "ies,",
+				response->h.numAdditionals, response->h.numAdditionals == 1 ? "" : "s");
+			// not goto exit because we won't have any CacheFlushRecords and we do not want to
+			// generate negative cache entries (we want to query the next server)
+			return;
+			}
+		}
+
+	for (i = 0; i < totalrecords && ptr && ptr < end; i++)
+		{
+		// All responses sent via LL multicast are acceptable for caching
+		// All responses received over our outbound TCP connections are acceptable for caching
+		mDNSBool AcceptableResponse = ResponseMCast || !dstaddr || LLQType;
+		// (Note that just because we are willing to cache something, that doesn't necessarily make it a trustworthy answer
+		// to any specific question -- any code reading records from the cache needs to make that determination for itself.)
+
+		const mDNSu8 RecordType =
+			(i < firstauthority ) ? (mDNSu8)kDNSRecordTypePacketAns  :
+			(i < firstadditional) ? (mDNSu8)kDNSRecordTypePacketAuth : (mDNSu8)kDNSRecordTypePacketAdd;
+		ptr = GetLargeResourceRecord(m, response, ptr, end, InterfaceID, RecordType, &m->rec);
+		if (!ptr) goto exit;		// Break out of the loop and clean up our CacheFlushRecords list before exiting
+		if (m->rec.r.resrec.RecordType == kDNSRecordTypePacketNegative) { m->rec.r.resrec.RecordType = 0; continue; }
+
+		// Don't want to cache OPT or TSIG pseudo-RRs
+		if (m->rec.r.resrec.rrtype == kDNSType_TSIG) { m->rec.r.resrec.RecordType = 0; continue; }
+		if (m->rec.r.resrec.rrtype == kDNSType_OPT)
+			{
+			const rdataOPT *opt;
+			const rdataOPT *const e = (const rdataOPT *)&m->rec.r.resrec.rdata->u.data[m->rec.r.resrec.rdlength];
+			// Find owner sub-option(s). We verify that the MAC is non-zero, otherwise we could inadvertently
+			// delete all our own AuthRecords (which are identified by having zero MAC tags on them).
+			for (opt = &m->rec.r.resrec.rdata->u.opt[0]; opt < e; opt++)
+				if (opt->opt == kDNSOpt_Owner && opt->u.owner.vers == 0 && opt->u.owner.HMAC.l[0])
+					{
+					ClearProxyRecords(m, &opt->u.owner, m->DuplicateRecords);
+					ClearProxyRecords(m, &opt->u.owner, m->ResourceRecords);
+					}
+			m->rec.r.resrec.RecordType = 0;
+			continue;
+			}
+
+		// if a CNAME record points to itself, then don't add it to the cache
+		if ((m->rec.r.resrec.rrtype == kDNSType_CNAME) && SameDomainName(m->rec.r.resrec.name, &m->rec.r.resrec.rdata->u.name))
+			{
+			LogInfo("mDNSCoreReceiveResponse: CNAME loop domain name %##s", m->rec.r.resrec.name->c);
+			m->rec.r.resrec.RecordType = 0;
+			continue;
+			}
+
+		// When we receive uDNS LLQ responses, we assume a long cache lifetime --
+		// In the case of active LLQs, we'll get remove events when the records actually do go away
+		// In the case of polling LLQs, we assume the record remains valid until the next poll
+		if (!mDNSOpaque16IsZero(response->h.id))
+			m->rec.r.resrec.rroriginalttl = GetEffectiveTTL(LLQType, m->rec.r.resrec.rroriginalttl);
+
+		// If response was not sent via LL multicast,
+		// then see if it answers a recent query of ours, which would also make it acceptable for caching.
+		if (!ResponseMCast)
+			{
+			if (LLQType)
+				{
+				// For Long Lived queries that are both sent over UDP and Private TCP, LLQType is set.
+				// Even though it is AcceptableResponse, we need a matching DNSServer pointer for the
+				// queries to get ADD/RMV events. To lookup the question, we can't use
+				// ExpectingUnicastResponseForRecord as the port numbers don't match. uDNS_recvLLQRespose
+				// has already matched the question using the 64 bit Id in the packet and we use that here.
+
+				if (llqMatch != mDNSNULL) m->rec.r.resrec.rDNSServer = uDNSServer = llqMatch->qDNSServer;
+				}
+			else if (!AcceptableResponse || !dstaddr)
+				{
+				// For responses that come over TCP (Responses that can't fit within UDP) or TLS (Private queries
+				// that are not long lived e.g., AAAA lookup in a Private domain), it is indicated by !dstaddr.
+				// Even though it is AcceptableResponse, we still need a DNSServer pointer for the resource records that
+				// we create.
+
+				DNSQuestion *q = ExpectingUnicastResponseForRecord(m, srcaddr, ResponseSrcLocal, dstport, response->h.id, &m->rec.r, !dstaddr);
+
+				// Intialize the DNS server on the resource record which will now filter what questions we answer with
+				// this record.
+				//
+				// We could potentially lookup the DNS server based on the source address, but that may not work always
+				// and that's why ExpectingUnicastResponseForRecord does not try to verify whether the response came
+				// from the DNS server that queried. We follow the same logic here. If we can find a matching quetion based
+				// on the "id" and "source port", then this response answers the question and assume the response
+				// came from the same DNS server that we sent the query to.
+
+				if (q != mDNSNULL)
+					{
+					AcceptableResponse = mDNStrue;
+					if (!InterfaceID)
+						{
+						debugf("mDNSCoreReceiveResponse: InterfaceID %p %##s (%s)", q->InterfaceID, q->qname.c, DNSTypeName(q->qtype));
+						m->rec.r.resrec.rDNSServer = uDNSServer = q->qDNSServer;
+						}
+					}
+				else
+					{
+					// If we can't find a matching question, we need to see whether we have seen records earlier that matched
+					// the question. The code below does that. So, make this record unacceptable for now
+					if (!InterfaceID)
+						{
+						debugf("mDNSCoreReceiveResponse: Can't find question for record name %##s", m->rec.r.resrec.name->c);
+						AcceptableResponse = mDNSfalse;
+						}
+					}
+				}
+			}
+
+		// 1. Check that this packet resource record does not conflict with any of ours
+		if (mDNSOpaque16IsZero(response->h.id) && m->rec.r.resrec.rrtype != kDNSType_NSEC)
+			{
+			if (m->CurrentRecord)
+				LogMsg("mDNSCoreReceiveResponse ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord));
+			m->CurrentRecord = m->ResourceRecords;
+			while (m->CurrentRecord)
+				{
+				AuthRecord *rr = m->CurrentRecord;
+				m->CurrentRecord = rr->next;
+				// We accept all multicast responses, and unicast responses resulting from queries we issued
+				// For other unicast responses, this code accepts them only for responses with an
+				// (apparently) local source address that pertain to a record of our own that's in probing state
+				if (!AcceptableResponse && !(ResponseSrcLocal && rr->resrec.RecordType == kDNSRecordTypeUnique)) continue;
+
+				if (PacketRRMatchesSignature(&m->rec.r, rr))		// If interface, name, type (if shared record) and class match...
+					{
+					// ... check to see if type and rdata are identical
+					if (IdenticalSameNameRecord(&m->rec.r.resrec, &rr->resrec))
+						{
+						// If the RR in the packet is identical to ours, just check they're not trying to lower the TTL on us
+						if (m->rec.r.resrec.rroriginalttl >= rr->resrec.rroriginalttl/2 || m->SleepState)
+							{
+							// If we were planning to send on this -- and only this -- interface, then we don't need to any more
+							if      (rr->ImmedAnswer == InterfaceID) { rr->ImmedAnswer = mDNSNULL; rr->ImmedUnicast = mDNSfalse; }
+							}
+						else
+							{
+							if      (rr->ImmedAnswer == mDNSNULL)    { rr->ImmedAnswer = InterfaceID;       m->NextScheduledResponse = m->timenow; }
+							else if (rr->ImmedAnswer != InterfaceID) { rr->ImmedAnswer = mDNSInterfaceMark; m->NextScheduledResponse = m->timenow; }
+							}
+						}
+					// else, the packet RR has different type or different rdata -- check to see if this is a conflict
+					else if (m->rec.r.resrec.rroriginalttl > 0 && PacketRRConflict(m, rr, &m->rec.r))
+						{
+						LogInfo("mDNSCoreReceiveResponse: Pkt Record: %08lX %s", m->rec.r.resrec.rdatahash, CRDisplayString(m, &m->rec.r));
+						LogInfo("mDNSCoreReceiveResponse: Our Record: %08lX %s", rr->     resrec.rdatahash, ARDisplayString(m, rr));
+	
+						// If this record is marked DependentOn another record for conflict detection purposes,
+						// then *that* record has to be bumped back to probing state to resolve the conflict
+						if (rr->DependentOn)
+							{
+							while (rr->DependentOn) rr = rr->DependentOn;
+							LogInfo("mDNSCoreReceiveResponse: Dep Record: %08lX %s", rr->     resrec.rdatahash, ARDisplayString(m, rr));
+							}
+	
+						// If we've just whacked this record's ProbeCount, don't need to do it again
+						if (rr->ProbeCount > DefaultProbeCountForTypeUnique)
+							LogInfo("mDNSCoreReceiveResponse: Already reset to Probing: %s", ARDisplayString(m, rr));
+						else if (rr->ProbeCount == DefaultProbeCountForTypeUnique)
+							LogMsg("mDNSCoreReceiveResponse: Ignoring response received before we even began probing: %s", ARDisplayString(m, rr));
+						else
+							{
+							LogMsg("mDNSCoreReceiveResponse: Received from %#a:%d %s", srcaddr, mDNSVal16(srcport), CRDisplayString(m, &m->rec.r));
+							// If we'd previously verified this record, put it back to probing state and try again
+							if (rr->resrec.RecordType == kDNSRecordTypeVerified)
+								{
+								LogMsg("mDNSCoreReceiveResponse: Resetting to Probing: %s", ARDisplayString(m, rr));
+								rr->resrec.RecordType     = kDNSRecordTypeUnique;
+								// We set ProbeCount to one more than the usual value so we know we've already touched this record.
+								// This is because our single probe for "example-name.local" could yield a response with (say) two A records and
+								// three AAAA records in it, and we don't want to call RecordProbeFailure() five times and count that as five conflicts.
+								// This special value is recognised and reset to DefaultProbeCountForTypeUnique in SendQueries().
+								rr->ProbeCount     = DefaultProbeCountForTypeUnique + 1;
+								rr->AnnounceCount  = InitialAnnounceCount;
+								InitializeLastAPTime(m, rr);
+								RecordProbeFailure(m, rr);	// Repeated late conflicts also cause us to back off to the slower probing rate
+								}
+							// If we're probing for this record, we just failed
+							else if (rr->resrec.RecordType == kDNSRecordTypeUnique)
+								{
+								LogMsg("mDNSCoreReceiveResponse: ProbeCount %d; will deregister %s", rr->ProbeCount, ARDisplayString(m, rr));
+								mDNS_Deregister_internal(m, rr, mDNS_Dereg_conflict);
+								}
+							// We assumed this record must be unique, but we were wrong. (e.g. There are two mDNSResponders on the
+							// same machine giving different answers for the reverse mapping record, or there are two machines on the
+							// network using the same IP address.) This is simply a misconfiguration, and there's nothing we can do
+							// to fix it -- e.g. it's not our job to be trying to change the machine's IP address. We just discard our
+							// record to avoid continued conflicts (as we do for a conflict on our Unique records) and get on with life.
+							else if (rr->resrec.RecordType == kDNSRecordTypeKnownUnique)
+								{
+								LogMsg("mDNSCoreReceiveResponse: Unexpected conflict discarding %s", ARDisplayString(m, rr));
+								mDNS_Deregister_internal(m, rr, mDNS_Dereg_conflict);
+								}
+							else
+								LogMsg("mDNSCoreReceiveResponse: Unexpected record type %X %s", rr->resrec.RecordType, ARDisplayString(m, rr));
+							}
+						}
+					// Else, matching signature, different type or rdata, but not a considered a conflict.
+					// If the packet record has the cache-flush bit set, then we check to see if we
+					// have any record(s) of the same type that we should re-assert to rescue them
+					// (see note about "multi-homing and bridged networks" at the end of this function).
+					else if (m->rec.r.resrec.rrtype == rr->resrec.rrtype)
+						if ((m->rec.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) && m->timenow - rr->LastMCTime > mDNSPlatformOneSecond/2)
+							{ rr->ImmedAnswer = mDNSInterfaceMark; m->NextScheduledResponse = m->timenow; }
+					}
+				}
+			}
+
+		if (!AcceptableResponse)
+			{
+			const CacheRecord *cr;
+			for (cr = CacheFlushRecords; cr != (CacheRecord*)1; cr = cr->NextInCFList)
+				{
+				domainname *target = GetRRDomainNameTarget(&cr->resrec);
+				// When we issue a query for A record, the response might contain both a CNAME and A records. Only the CNAME would
+				// match the question and we already created a cache entry in the previous pass of this loop. Now when we process
+				// the A record, it does not match the question because the record name here is the CNAME. Hence we try to
+				// match with the previous records to make it an AcceptableResponse. We have to be careful about setting the
+				// DNSServer value that we got in the previous pass. This can happen for other record types like SRV also.
+
+				if (target && cr->resrec.rdatahash == m->rec.r.resrec.namehash && SameDomainName(target, m->rec.r.resrec.name))
+					{
+					debugf("mDNSCoreReceiveResponse: Found a matching entry for %##s in the CacheFlushRecords", m->rec.r.resrec.name->c);
+					AcceptableResponse = mDNStrue;
+					m->rec.r.resrec.rDNSServer = uDNSServer;
+					break;
+					}
+				}
+			}
+
+		// 2. See if we want to add this packet resource record to our cache
+		// We only try to cache answers if we have a cache to put them in
+		// Also, we ignore any apparent attempts at cache poisoning unicast to us that do not answer any outstanding active query
+		if (!AcceptableResponse) LogInfo("mDNSCoreReceiveResponse ignoring %s", CRDisplayString(m, &m->rec.r));
+		if (m->rrcache_size && AcceptableResponse)
+			{
+			const mDNSu32 slot = HashSlot(m->rec.r.resrec.name);
+			CacheGroup *cg = CacheGroupForRecord(m, slot, &m->rec.r.resrec);
+			CacheRecord *rr;
+
+			// 2a. Check if this packet resource record is already in our cache
+			for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
+				{
+				mDNSBool match = !InterfaceID ? m->rec.r.resrec.rDNSServer == rr->resrec.rDNSServer : rr->resrec.InterfaceID == InterfaceID;
+				// If we found this exact resource record, refresh its TTL
+				if (match && IdenticalSameNameRecord(&m->rec.r.resrec, &rr->resrec))
+					{
+					if (m->rec.r.resrec.rdlength > InlineCacheRDSize)
+						verbosedebugf("Found record size %5d interface %p already in cache: %s",
+							m->rec.r.resrec.rdlength, InterfaceID, CRDisplayString(m, &m->rec.r));
+					
+					if (m->rec.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask)
+						{
+						// If this packet record has the kDNSClass_UniqueRRSet flag set, then add it to our cache flushing list
+						if (rr->NextInCFList == mDNSNULL && cfp != &rr->NextInCFList && LLQType != uDNS_LLQ_Events)
+							{ *cfp = rr; cfp = &rr->NextInCFList; *cfp = (CacheRecord*)1; }
+
+						// If this packet record is marked unique, and our previous cached copy was not, then fix it
+						if (!(rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask))
+							{
+							DNSQuestion *q;
+							for (q = m->Questions; q; q=q->next) if (ResourceRecordAnswersQuestion(&rr->resrec, q)) q->UniqueAnswers++;
+							rr->resrec.RecordType = m->rec.r.resrec.RecordType;
+							}
+						}
+
+					if (!SameRDataBody(&m->rec.r.resrec, &rr->resrec.rdata->u, SameDomainNameCS))
+						{
+						// If the rdata of the packet record differs in name capitalization from the record in our cache
+						// then mDNSPlatformMemSame will detect this. In this case, throw the old record away, so that clients get
+						// a 'remove' event for the record with the old capitalization, and then an 'add' event for the new one.
+						// <rdar://problem/4015377> mDNS -F returns the same domain multiple times with different casing
+						rr->resrec.rroriginalttl = 0;
+						rr->TimeRcvd = m->timenow;
+						rr->UnansweredQueries = MaxUnansweredQueries;
+						SetNextCacheCheckTimeForRecord(m, rr);
+						LogInfo("Discarding due to domainname case change old: %s", CRDisplayString(m,rr));
+						LogInfo("Discarding due to domainname case change new: %s", CRDisplayString(m,&m->rec.r));
+						LogInfo("Discarding due to domainname case change in %d slot %3d in %d %d",
+							NextCacheCheckEvent(rr) - m->timenow, slot, m->rrcache_nextcheck[slot] - m->timenow, m->NextCacheCheck - m->timenow);
+						// DO NOT break out here -- we want to continue as if we never found it
+						}
+					else if (m->rec.r.resrec.rroriginalttl > 0)
+						{
+						DNSQuestion *q;
+						//if (rr->resrec.rroriginalttl == 0) LogMsg("uDNS rescuing %s", CRDisplayString(m, rr));
+						RefreshCacheRecord(m, rr, m->rec.r.resrec.rroriginalttl);
+
+						// We have to reset the question interval to MaxQuestionInterval so that we don't keep
+						// polling the network once we get a valid response back. For the first time when a new
+						// cache entry is created, AnswerCurrentQuestionWithResourceRecord does that.
+						// Subsequently, if we reissue questions from within the mDNSResponder e.g., DNS server
+						// configuration changed, without flushing the cache, we reset the question interval here.
+						// Currently, we do this for for both multicast and unicast questions as long as the record
+						// type is unique. For unicast, resource record is always unique and for multicast it is
+						// true for records like A etc. but not for PTR.
+						if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask)
+							{
+							for (q = m->Questions; q; q=q->next)
+								{
+								if (!q->DuplicateOf && !q->LongLived &&
+									ActiveQuestion(q) && ResourceRecordAnswersQuestion(&rr->resrec, q))
+									{
+									q->LastQTime        = m->timenow;
+									q->LastQTxTime      = m->timenow;
+									q->RecentAnswerPkts = 0;
+									q->ThisQInterval    = MaxQuestionInterval;
+									q->RequestUnicast   = mDNSfalse;
+									q->unansweredQueries = 0;
+									debugf("mDNSCoreReceiveResponse: Set MaxQuestionInterval for %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype));
+									break;		// Why break here? Aren't there other questions we might want to look at?-- SC July 2010
+									}
+								}
+							}
+						break;
+						}
+					else
+						{
+						// If the packet TTL is zero, that means we're deleting this record.
+						// To give other hosts on the network a chance to protest, we push the deletion
+						// out one second into the future. Also, we set UnansweredQueries to MaxUnansweredQueries.
+						// Otherwise, we'll do final queries for this record at 80% and 90% of its apparent
+						// lifetime (800ms and 900ms from now) which is a pointless waste of network bandwidth.
+						// If record's current expiry time is more than a second from now, we set it to expire in one second.
+						// If the record is already going to expire in less than one second anyway, we leave it alone --
+						// we don't want to let the goodbye packet *extend* the record's lifetime in our cache.
+						debugf("DE for %s", CRDisplayString(m, rr));
+						if (RRExpireTime(rr) - m->timenow > mDNSPlatformOneSecond)
+							{
+							rr->resrec.rroriginalttl = 1;
+							rr->TimeRcvd = m->timenow;
+							rr->UnansweredQueries = MaxUnansweredQueries;
+							SetNextCacheCheckTimeForRecord(m, rr);
+							}
+						break;
+						}
+					}
+				}
+
+			// If packet resource record not in our cache, add it now
+			// (unless it is just a deletion of a record we never had, in which case we don't care)
+			if (!rr && m->rec.r.resrec.rroriginalttl > 0)
+				{
+				const mDNSBool AddToCFList = (m->rec.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) && (LLQType != uDNS_LLQ_Events);
+				const mDNSs32 delay = AddToCFList ? NonZeroTime(m->timenow + mDNSPlatformOneSecond) :
+					CheckForSoonToExpireRecords(m, m->rec.r.resrec.name, m->rec.r.resrec.namehash, slot);
+				// If unique, assume we may have to delay delivery of this 'add' event.
+				// Below, where we walk the CacheFlushRecords list, we either call CacheRecordDeferredAdd()
+				// to immediately to generate answer callbacks, or we call ScheduleNextCacheCheckTime()
+				// to schedule an mDNS_Execute task at the appropriate time.
+				rr = CreateNewCacheEntry(m, slot, cg, delay);
+				if (rr)
+					{
+					if (AddToCFList) { *cfp = rr; cfp = &rr->NextInCFList; *cfp = (CacheRecord*)1; }
+					else if (rr->DelayDelivery) ScheduleNextCacheCheckTime(m, slot, rr->DelayDelivery);
+					}
+				}
+			}
+		m->rec.r.resrec.RecordType = 0;		// Clear RecordType to show we're not still using it
+		}
+
+exit:
+	m->rec.r.resrec.RecordType = 0;		// Clear RecordType to show we're not still using it
+
+	// If we've just received one or more records with their cache flush bits set,
+	// then scan that cache slot to see if there are any old stale records we need to flush
+	while (CacheFlushRecords != (CacheRecord*)1)
+		{
+		CacheRecord *r1 = CacheFlushRecords, *r2;
+		const mDNSu32 slot = HashSlot(r1->resrec.name);
+		const CacheGroup *cg = CacheGroupForRecord(m, slot, &r1->resrec);
+		CacheFlushRecords = CacheFlushRecords->NextInCFList;
+		r1->NextInCFList = mDNSNULL;
+		
+		// Look for records in the cache with the same signature as this new one with the cache flush
+		// bit set, and either (a) if they're fresh, just make sure the whole RRSet has the same TTL
+		// (as required by DNS semantics) or (b) if they're old, mark them for deletion in one second.
+		// We make these TTL adjustments *only* for records that still have *more* than one second
+		// remaining to live. Otherwise, a record that we tagged for deletion half a second ago
+		// (and now has half a second remaining) could inadvertently get its life extended, by either
+		// (a) if we got an explicit goodbye packet half a second ago, the record would be considered
+		// "fresh" and would be incorrectly resurrected back to the same TTL as the rest of the RRSet,
+		// or (b) otherwise, the record would not be fully resurrected, but would be reset to expire
+		// in one second, thereby inadvertently delaying its actual expiration, instead of hastening it.
+		// If this were to happen repeatedly, the record's expiration could be deferred indefinitely.
+		// To avoid this, we need to ensure that the cache flushing operation will only act to
+		// *decrease* a record's remaining lifetime, never *increase* it.
+		for (r2 = cg ? cg->members : mDNSNULL; r2; r2=r2->next)
+			// For Unicast (null InterfaceID) the DNSservers should also match
+			if ((r1->resrec.InterfaceID == r2->resrec.InterfaceID) &&
+				(r1->resrec.InterfaceID || (r1->resrec.rDNSServer == r2->resrec.rDNSServer)) &&
+				r1->resrec.rrtype      == r2->resrec.rrtype &&
+				r1->resrec.rrclass     == r2->resrec.rrclass)
+				{
+				// If record is recent, just ensure the whole RRSet has the same TTL (as required by DNS semantics)
+				// else, if record is old, mark it to be flushed
+				if (m->timenow - r2->TimeRcvd < mDNSPlatformOneSecond && RRExpireTime(r2) - m->timenow > mDNSPlatformOneSecond)
+					{
+					// If we find mismatched TTLs in an RRSet, correct them.
+					// We only do this for records with a TTL of 2 or higher. It's possible to have a
+					// goodbye announcement with the cache flush bit set (or a case-change on record rdata,
+					// which we treat as a goodbye followed by an addition) and in that case it would be
+					// inappropriate to synchronize all the other records to a TTL of 0 (or 1).
+					// We suppress the message for the specific case of correcting from 240 to 60 for type TXT,
+					// because certain early Bonjour devices are known to have this specific mismatch, and
+					// there's no point filling syslog with messages about something we already know about.
+					// We also don't log this for uDNS responses, since a caching name server is obliged
+					// to give us an aged TTL to correct for how long it has held the record,
+					// so our received TTLs are expected to vary in that case
+					if (r2->resrec.rroriginalttl != r1->resrec.rroriginalttl && r1->resrec.rroriginalttl > 1)
+						{
+						if (!(r2->resrec.rroriginalttl == 240 && r1->resrec.rroriginalttl == 60 && r2->resrec.rrtype == kDNSType_TXT) &&
+							mDNSOpaque16IsZero(response->h.id))
+							LogInfo("Correcting TTL from %4d to %4d for %s",
+								r2->resrec.rroriginalttl, r1->resrec.rroriginalttl, CRDisplayString(m, r2));
+						r2->resrec.rroriginalttl = r1->resrec.rroriginalttl;
+						}
+					r2->TimeRcvd = m->timenow;
+					}
+				else				// else, if record is old, mark it to be flushed
+					{
+					verbosedebugf("Cache flush new %p age %d expire in %d %s", r1, m->timenow - r1->TimeRcvd, RRExpireTime(r1) - m->timenow, CRDisplayString(m, r1));
+					verbosedebugf("Cache flush old %p age %d expire in %d %s", r2, m->timenow - r2->TimeRcvd, RRExpireTime(r2) - m->timenow, CRDisplayString(m, r2));
+					// We set stale records to expire in one second.
+					// This gives the owner a chance to rescue it if necessary.
+					// This is important in the case of multi-homing and bridged networks:
+					//   Suppose host X is on Ethernet. X then connects to an AirPort base station, which happens to be
+					//   bridged onto the same Ethernet. When X announces its AirPort IP address with the cache-flush bit
+					//   set, the AirPort packet will be bridged onto the Ethernet, and all other hosts on the Ethernet
+					//   will promptly delete their cached copies of the (still valid) Ethernet IP address record.
+					//   By delaying the deletion by one second, we give X a change to notice that this bridging has
+					//   happened, and re-announce its Ethernet IP address to rescue it from deletion from all our caches.
+
+					// We set UnansweredQueries to MaxUnansweredQueries to avoid expensive and unnecessary
+					// final expiration queries for this record.
+
+					// If a record is deleted twice, first with an explicit DE record, then a second time by virtue of the cache
+					// flush bit on the new record replacing it, then we allow the record to be deleted immediately, without the usual
+					// one-second grace period. This improves responsiveness for mDNS_Update(), as used for things like iChat status updates.
+					// <rdar://problem/5636422> Updating TXT records is too slow
+					// We check for "rroriginalttl == 1" because we want to include records tagged by the "packet TTL is zero" check above,
+					// which sets rroriginalttl to 1, but not records tagged by the rdata case-change check, which sets rroriginalttl to 0.
+					if (r2->TimeRcvd == m->timenow && r2->resrec.rroriginalttl == 1 && r2->UnansweredQueries == MaxUnansweredQueries)
+						{
+						LogInfo("Cache flush for DE record %s", CRDisplayString(m, r2));
+						r2->resrec.rroriginalttl = 0;
+						}
+					else if (RRExpireTime(r2) - m->timenow > mDNSPlatformOneSecond)
+						{
+						// We only set a record to expire in one second if it currently has *more* than a second to live
+						// If it's already due to expire in a second or less, we just leave it alone
+						r2->resrec.rroriginalttl = 1;
+						r2->UnansweredQueries = MaxUnansweredQueries;
+						r2->TimeRcvd = m->timenow - 1;
+						// We use (m->timenow - 1) instead of m->timenow, because we use that to identify records
+						// that we marked for deletion via an explicit DE record
+						}
+					}
+				SetNextCacheCheckTimeForRecord(m, r2);
+				}
+
+		if (r1->DelayDelivery)	// If we were planning to delay delivery of this record, see if we still need to
+			{
+			r1->DelayDelivery = CheckForSoonToExpireRecords(m, r1->resrec.name, r1->resrec.namehash, slot);
+			// If no longer delaying, deliver answer now, else schedule delivery for the appropriate time
+			if (!r1->DelayDelivery) CacheRecordDeferredAdd(m, r1);
+			else ScheduleNextCacheCheckTime(m, slot, r1->DelayDelivery);
+			}
+		}
+
+	// See if we need to generate negative cache entries for unanswered unicast questions
+	ptr = response->data;
+	for (i = 0; i < response->h.numQuestions && ptr && ptr < end; i++)
+		{
+		DNSQuestion q;
+		DNSQuestion *qptr = mDNSNULL;
+		ptr = getQuestion(response, ptr, end, InterfaceID, &q);
+		if (ptr && (qptr = ExpectingUnicastResponseForQuestion(m, dstport, response->h.id, &q, !dstaddr)))
+			{
+			CacheRecord *rr, *neg = mDNSNULL;
+			mDNSu32 slot = HashSlot(&q.qname);
+			CacheGroup *cg = CacheGroupForName(m, slot, q.qnamehash, &q.qname);
+			for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
+				if (SameNameRecordAnswersQuestion(&rr->resrec, qptr))
+					{
+					// 1. If we got a fresh answer to this query, then don't need to generate a negative entry
+					if (RRExpireTime(rr) - m->timenow > 0) break;
+					// 2. If we already had a negative entry, keep track of it so we can resurrect it instead of creating a new one
+					if (rr->resrec.RecordType == kDNSRecordTypePacketNegative) neg = rr;
+					}
+			// When we're doing parallel unicast and multicast queries for dot-local names (for supporting Microsoft
+			// Active Directory sites) we don't want to waste memory making negative cache entries for all the unicast answers.
+			// Otherwise we just fill up our cache with negative entries for just about every single multicast name we ever look up
+			// (since the Microsoft Active Directory server is going to assert that pretty much every single multicast name doesn't exist).
+			// This is not only a waste of memory, but there's also the problem of those negative entries confusing us later -- e.g. we
+			// suppress sending our mDNS query packet because we think we already have a valid (negative) answer to that query in our cache.
+			// The one exception is that we *DO* want to make a negative cache entry for "local. SOA", for the (common) case where we're
+			// *not* on a Microsoft Active Directory network, and there is no authoritative server for "local". Note that this is not
+			// in conflict with the mDNS spec, because that spec says, "Multicast DNS Zones have no SOA record," so it's okay to cache
+			// negative answers for "local. SOA" from a uDNS server, because the mDNS spec already says that such records do not exist :-)
+			if (!InterfaceID && q.qtype != kDNSType_SOA && IsLocalDomain(&q.qname))
+				{
+				// If we did not find a positive answer and we can append search domains to this question,
+				// generate a negative response (without creating a cache entry) to append search domains.
+				if (qptr->AppendSearchDomains && !rr)
+					{
+					LogInfo("mDNSCoreReceiveResponse: Generate negative response for %##s (%s)", q.qname.c, DNSTypeName(q.qtype));
+					m->CurrentQuestion = qptr;
+					GenerateNegativeResponse(m);
+					m->CurrentQuestion = mDNSNULL;
+					}
+				else LogInfo("mDNSCoreReceiveResponse: Skipping check to see if we need to generate a negative cache entry for %##s (%s)", q.qname.c, DNSTypeName(q.qtype));
+				}
+			else
+				{
+				if (!rr)
+					{
+					// We start off assuming a negative caching TTL of 60 seconds
+					// but then look to see if we can find an SOA authority record to tell us a better value we should be using
+					mDNSu32 negttl = 60;
+					int repeat = 0;
+					const domainname *name = &q.qname;
+					mDNSu32           hash = q.qnamehash;
+	
+					// Special case for our special Microsoft Active Directory "local SOA" check.
+					// Some cheap home gateways don't include an SOA record in the authority section when
+					// they send negative responses, so we don't know how long to cache the negative result.
+					// Because we don't want to keep hitting the root name servers with our query to find
+					// if we're on a network using Microsoft Active Directory using "local" as a private
+					// internal top-level domain, we make sure to cache the negative result for at least one day.
+					if (q.qtype == kDNSType_SOA && SameDomainName(&q.qname, &localdomain)) negttl = 60 * 60 * 24;
+	
+					// If we're going to make (or update) a negative entry, then look for the appropriate TTL from the SOA record
+					if (response->h.numAuthorities && (ptr = LocateAuthorities(response, end)) != mDNSNULL)
+						{
+						ptr = GetLargeResourceRecord(m, response, ptr, end, InterfaceID, kDNSRecordTypePacketAuth, &m->rec);
+						if (ptr && m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative && m->rec.r.resrec.rrtype == kDNSType_SOA)
+							{
+							const rdataSOA *const soa = (const rdataSOA *)m->rec.r.resrec.rdata->u.data;
+							mDNSu32 ttl_s = soa->min;
+							// We use the lesser of the SOA.MIN field and the SOA record's TTL, *except*
+							// for the SOA record for ".", where the record is reported as non-cacheable
+							// (TTL zero) for some reason, so in this case we just take the SOA record's TTL as-is
+							if (ttl_s > m->rec.r.resrec.rroriginalttl && m->rec.r.resrec.name->c[0])
+								ttl_s = m->rec.r.resrec.rroriginalttl;
+							if (negttl < ttl_s) negttl = ttl_s;
+		
+							// Special check for SOA queries: If we queried for a.b.c.d.com, and got no answer,
+							// with an Authority Section SOA record for d.com, then this is a hint that the authority
+							// is d.com, and consequently SOA records b.c.d.com and c.d.com don't exist either.
+							// To do this we set the repeat count so the while loop below will make a series of negative cache entries for us
+							if (q.qtype == kDNSType_SOA)
+								{
+								int qcount = CountLabels(&q.qname);
+								int scount = CountLabels(m->rec.r.resrec.name);
+								if (qcount - 1 > scount)
+									if (SameDomainName(SkipLeadingLabels(&q.qname, qcount - scount), m->rec.r.resrec.name))
+										repeat = qcount - 1 - scount;
+								}
+							}
+						m->rec.r.resrec.RecordType = 0;		// Clear RecordType to show we're not still using it
+						}
+	
+					// If we already had a negative entry in the cache, then we double our existing negative TTL. This is to avoid
+					// the case where the record doesn't exist (e.g. particularly for things like our lb._dns-sd._udp.<domain> query),
+					// and the server returns no SOA record (or an SOA record with a small MIN TTL) so we assume a TTL
+					// of 60 seconds, and we end up polling the server every minute for a record that doesn't exist.
+					// With this fix in place, when this happens, we double the effective TTL each time (up to one hour),
+					// so that we back off our polling rate and don't keep hitting the server continually.
+					if (neg)
+						{
+						if (negttl < neg->resrec.rroriginalttl * 2)
+							negttl = neg->resrec.rroriginalttl * 2;
+						if (negttl > 3600)
+							negttl = 3600;
+						}
+	
+					negttl = GetEffectiveTTL(LLQType, negttl);	// Add 25% grace period if necessary
+	
+					// If we already had a negative cache entry just update it, else make one or more new negative cache entries
+					if (neg)
+						{
+						debugf("Renewing negative TTL from %d to %d %s", neg->resrec.rroriginalttl, negttl, CRDisplayString(m, neg));
+						RefreshCacheRecord(m, neg, negttl);
+						}
+					else while (1)
+						{
+						debugf("mDNSCoreReceiveResponse making negative cache entry TTL %d for %##s (%s)", negttl, name->c, DNSTypeName(q.qtype));
+						MakeNegativeCacheRecord(m, &m->rec.r, name, hash, q.qtype, q.qclass, negttl, mDNSInterface_Any, qptr->qDNSServer);
+						CreateNewCacheEntry(m, slot, cg, 0);	// We never need any delivery delay for these generated negative cache records
+						m->rec.r.resrec.RecordType = 0;		// Clear RecordType to show we're not still using it
+						if (!repeat) break;
+						repeat--;
+						name = (const domainname *)(name->c + 1 + name->c[0]);
+						hash = DomainNameHashValue(name);
+						slot = HashSlot(name);
+						cg   = CacheGroupForName(m, slot, hash, name);
+						}
+					}
+				}
+			}
+		}
+	}
+
+// ScheduleWakeup causes all proxy records with WakeUp.HMAC matching mDNSEthAddr 'e' to be deregistered, causing
+// multiple wakeup magic packets to be sent if appropriate, and all records to be ultimately freed after a few seconds.
+// ScheduleWakeup is called on mDNS record conflicts, ARP conflicts, NDP conflicts, or reception of trigger traffic
+// that warrants waking the sleeping host.
+// ScheduleWakeup must be called with the lock held (ScheduleWakeupForList uses mDNS_Deregister_internal)
+
+mDNSlocal void ScheduleWakeupForList(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSEthAddr *e, AuthRecord *const thelist)
+	{
+	// We don't need to use the m->CurrentRecord mechanism here because the target HMAC is nonzero,
+	// so all we're doing is marking the record to generate a few wakeup packets
+	AuthRecord *rr;
+	if (!e->l[0]) { LogMsg("ScheduleWakeupForList ERROR: Target HMAC is zero"); return; }
+	for (rr = thelist; rr; rr = rr->next) 
+		if (rr->resrec.InterfaceID == InterfaceID && rr->resrec.RecordType != kDNSRecordTypeDeregistering && mDNSSameEthAddress(&rr->WakeUp.HMAC, e))
+			{
+			LogInfo("ScheduleWakeupForList: Scheduling wakeup packets for %s", ARDisplayString(m, rr));
+			mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal);
+			}
+	}
+
+mDNSlocal void ScheduleWakeup(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSEthAddr *e)
+	{
+	if (!e->l[0]) { LogMsg("ScheduleWakeup ERROR: Target HMAC is zero"); return; }
+	ScheduleWakeupForList(m, InterfaceID, e, m->DuplicateRecords);
+	ScheduleWakeupForList(m, InterfaceID, e, m->ResourceRecords);
+	}
+
+mDNSlocal void SPSRecordCallback(mDNS *const m, AuthRecord *const ar, mStatus result)
+	{
+	if (result && result != mStatus_MemFree)
+		LogInfo("SPS Callback %d %s", result, ARDisplayString(m, ar));
+
+	if (result == mStatus_NameConflict)
+		{
+		mDNS_Lock(m);
+		LogMsg("%-7s Conflicting mDNS -- waking %.6a %s", InterfaceNameForID(m, ar->resrec.InterfaceID), &ar->WakeUp.HMAC, ARDisplayString(m, ar));
+		if (ar->WakeUp.HMAC.l[0])
+			{
+			SendWakeup(m, ar->resrec.InterfaceID, &ar->WakeUp.IMAC, &ar->WakeUp.password);	// Send one wakeup magic packet
+			ScheduleWakeup(m, ar->resrec.InterfaceID, &ar->WakeUp.HMAC);					// Schedule all other records with the same owner to be woken
+			}
+		mDNS_Unlock(m);
+		}
+
+	if (result == mStatus_NameConflict || result == mStatus_MemFree)
+		{
+		m->ProxyRecords--;
+		mDNSPlatformMemFree(ar);
+		mDNS_UpdateAllowSleep(m);
+		}
+	}
+
+mDNSlocal void mDNSCoreReceiveUpdate(mDNS *const m,
+	const DNSMessage *const msg, const mDNSu8 *end,
+	const mDNSAddr *srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport,
+	const mDNSInterfaceID InterfaceID)
+	{
+	int i;
+	AuthRecord opt;
+	mDNSu8 *p = m->omsg.data;
+	OwnerOptData owner = zeroOwner;		// Need to zero this, so we'll know if this Update packet was missing its Owner option
+	mDNSu32 updatelease = 0;
+	const mDNSu8 *ptr;
+
+	LogSPS("Received Update from %#-15a:%-5d to %#-15a:%-5d on 0x%p with "
+		"%2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s %d bytes",
+		srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), InterfaceID,
+		msg->h.numQuestions,   msg->h.numQuestions   == 1 ? ", "   : "s,",
+		msg->h.numAnswers,     msg->h.numAnswers     == 1 ? ", "   : "s,",
+		msg->h.numAuthorities, msg->h.numAuthorities == 1 ? "y,  " : "ies,",
+		msg->h.numAdditionals, msg->h.numAdditionals == 1 ? " "    : "s", end - msg->data);
+
+	if (!InterfaceID || !m->SPSSocket || !mDNSSameIPPort(dstport, m->SPSSocket->port)) return;
+
+	if (mDNS_PacketLoggingEnabled)
+		DumpPacket(m, mStatus_NoError, mDNSfalse, "UDP", srcaddr, srcport, dstaddr, dstport, msg, end);
+
+	ptr = LocateOptRR(msg, end, DNSOpt_LeaseData_Space + DNSOpt_OwnerData_ID_Space);
+	if (ptr)
+		{
+		ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &m->rec);
+		if (ptr && m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative && m->rec.r.resrec.rrtype == kDNSType_OPT)
+			{
+			const rdataOPT *o;
+			const rdataOPT *const e = (const rdataOPT *)&m->rec.r.resrec.rdata->u.data[m->rec.r.resrec.rdlength];
+			for (o = &m->rec.r.resrec.rdata->u.opt[0]; o < e; o++)
+				{
+				if      (o->opt == kDNSOpt_Lease)                         updatelease = o->u.updatelease;
+				else if (o->opt == kDNSOpt_Owner && o->u.owner.vers == 0) owner       = o->u.owner;
+				}
+			}
+		m->rec.r.resrec.RecordType = 0;		// Clear RecordType to show we're not still using it
+		}
+
+	InitializeDNSMessage(&m->omsg.h, msg->h.id, UpdateRespFlags);
+
+	if (!updatelease || !owner.HMAC.l[0])
+		{
+		static int msgs = 0;
+		if (msgs < 100)
+			{
+			msgs++;
+			LogMsg("Refusing sleep proxy registration from %#a:%d:%s%s", srcaddr, mDNSVal16(srcport),
+				!updatelease ? " No lease" : "", !owner.HMAC.l[0] ? " No owner" : "");
+			}
+		m->omsg.h.flags.b[1] |= kDNSFlag1_RC_FormErr;
+		}
+	else if (m->ProxyRecords + msg->h.mDNS_numUpdates > MAX_PROXY_RECORDS)
+		{
+		static int msgs = 0;
+		if (msgs < 100)
+			{
+			msgs++;
+			LogMsg("Refusing sleep proxy registration from %#a:%d: Too many records %d + %d = %d > %d", srcaddr, mDNSVal16(srcport),
+				m->ProxyRecords, msg->h.mDNS_numUpdates, m->ProxyRecords + msg->h.mDNS_numUpdates, MAX_PROXY_RECORDS);
+			}
+		m->omsg.h.flags.b[1] |= kDNSFlag1_RC_Refused;
+		}
+	else
+		{
+		LogSPS("Received Update for H-MAC %.6a I-MAC %.6a Password %.6a seq %d", &owner.HMAC, &owner.IMAC, &owner.password, owner.seq);
+	
+		if (updatelease > 24 * 60 * 60)
+			updatelease = 24 * 60 * 60;
+	
+		if (updatelease > 0x40000000UL / mDNSPlatformOneSecond)
+			updatelease = 0x40000000UL / mDNSPlatformOneSecond;
+	
+		ptr = LocateAuthorities(msg, end);
+		for (i = 0; i < msg->h.mDNS_numUpdates && ptr && ptr < end; i++)
+			{
+			ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAuth, &m->rec);
+			if (ptr && m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative)
+				{
+				mDNSu16 RDLengthMem = GetRDLengthMem(&m->rec.r.resrec);
+				AuthRecord *ar = mDNSPlatformMemAllocate(sizeof(AuthRecord) - sizeof(RDataBody) + RDLengthMem);
+				if (!ar) { m->omsg.h.flags.b[1] |= kDNSFlag1_RC_Refused; break; }
+				else
+					{
+					mDNSu8 RecordType = m->rec.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask ? kDNSRecordTypeUnique : kDNSRecordTypeShared;
+					m->rec.r.resrec.rrclass &= ~kDNSClass_UniqueRRSet;
+					ClearIdenticalProxyRecords(m, &owner, m->DuplicateRecords);	// Make sure we don't have any old stale duplicates of this record
+					ClearIdenticalProxyRecords(m, &owner, m->ResourceRecords);
+					mDNS_SetupResourceRecord(ar, mDNSNULL, InterfaceID, m->rec.r.resrec.rrtype, m->rec.r.resrec.rroriginalttl, RecordType, AuthRecordAny, SPSRecordCallback, ar);
+					AssignDomainName(&ar->namestorage, m->rec.r.resrec.name);
+					ar->resrec.rdlength = GetRDLength(&m->rec.r.resrec, mDNSfalse);
+					ar->resrec.rdata->MaxRDLength = RDLengthMem;
+					mDNSPlatformMemCopy(ar->resrec.rdata->u.data, m->rec.r.resrec.rdata->u.data, RDLengthMem);
+					ar->ForceMCast = mDNStrue;
+					ar->WakeUp     = owner;
+					if (m->rec.r.resrec.rrtype == kDNSType_PTR)
+						{
+						mDNSs32 t = ReverseMapDomainType(m->rec.r.resrec.name);
+						if      (t == mDNSAddrType_IPv4) GetIPv4FromName(&ar->AddressProxy, m->rec.r.resrec.name);
+						else if (t == mDNSAddrType_IPv6) GetIPv6FromName(&ar->AddressProxy, m->rec.r.resrec.name);
+						debugf("mDNSCoreReceiveUpdate: PTR %d %d %#a %s", t, ar->AddressProxy.type, &ar->AddressProxy, ARDisplayString(m, ar));
+						if (ar->AddressProxy.type) SetSPSProxyListChanged(InterfaceID);
+						}
+					ar->TimeRcvd   = m->timenow;
+					ar->TimeExpire = m->timenow + updatelease * mDNSPlatformOneSecond;
+					if (m->NextScheduledSPS - ar->TimeExpire > 0)
+						m->NextScheduledSPS = ar->TimeExpire;
+					mDNS_Register_internal(m, ar);
+					// Unsolicited Neighbor Advertisements (RFC 2461 Section 7.2.6) give us fast address cache updating,
+					// but some older IPv6 clients get confused by them, so for now we don't send them. Without Unsolicited
+					// Neighbor Advertisements we have to rely on Neighbor Unreachability Detection instead, which is slower.
+					// Given this, we'll do our best to wake for existing IPv6 connections, but we don't want to encourage
+					// new ones for sleeping clients, so we'll we send deletions for our SPS clients' AAAA records.
+					if (m->KnownBugs & mDNS_KnownBug_LimitedIPv6)
+						if (ar->resrec.rrtype == kDNSType_AAAA) ar->resrec.rroriginalttl = 0;
+					m->ProxyRecords++;
+					mDNS_UpdateAllowSleep(m);
+					LogSPS("SPS Registered %4d %X %s", m->ProxyRecords, RecordType, ARDisplayString(m,ar));
+					}
+				}
+			m->rec.r.resrec.RecordType = 0;		// Clear RecordType to show we're not still using it
+			}
+
+		if (m->omsg.h.flags.b[1] & kDNSFlag1_RC_Mask)
+			{
+			LogMsg("Refusing sleep proxy registration from %#a:%d: Out of memory", srcaddr, mDNSVal16(srcport));
+			ClearProxyRecords(m, &owner, m->DuplicateRecords);
+			ClearProxyRecords(m, &owner, m->ResourceRecords);
+			}
+		else
+			{
+			mDNS_SetupResourceRecord(&opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
+			opt.resrec.rrclass    = NormalMaxDNSMessageData;
+			opt.resrec.rdlength   = sizeof(rdataOPT);	// One option in this OPT record
+			opt.resrec.rdestimate = sizeof(rdataOPT);
+			opt.resrec.rdata->u.opt[0].opt           = kDNSOpt_Lease;
+			opt.resrec.rdata->u.opt[0].u.updatelease = updatelease;
+			p = PutResourceRecordTTLWithLimit(&m->omsg, p, &m->omsg.h.numAdditionals, &opt.resrec, opt.resrec.rroriginalttl, m->omsg.data + AbsoluteMaxDNSMessageData);
+			}
+		}
+
+	if (p) mDNSSendDNSMessage(m, &m->omsg, p, InterfaceID, m->SPSSocket, srcaddr, srcport, mDNSNULL, mDNSNULL);
+	}
+
+mDNSlocal void mDNSCoreReceiveUpdateR(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *end, const mDNSInterfaceID InterfaceID)
+	{
+	if (InterfaceID)
+		{
+		mDNSu32 updatelease = 60 * 60;		// If SPS fails to indicate lease time, assume one hour
+		const mDNSu8 *ptr = LocateOptRR(msg, end, DNSOpt_LeaseData_Space);
+		if (ptr)
+			{
+			ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &m->rec);
+			if (ptr && m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative && m->rec.r.resrec.rrtype == kDNSType_OPT)
+				{
+				const rdataOPT *o;
+				const rdataOPT *const e = (const rdataOPT *)&m->rec.r.resrec.rdata->u.data[m->rec.r.resrec.rdlength];
+				for (o = &m->rec.r.resrec.rdata->u.opt[0]; o < e; o++)
+					if (o->opt == kDNSOpt_Lease)
+						{
+						updatelease = o->u.updatelease;
+						LogSPS("Sleep Proxy granted lease time %4d seconds", updatelease);
+						}
+				}
+			m->rec.r.resrec.RecordType = 0;		// Clear RecordType to show we're not still using it
+			}
+
+		if (m->CurrentRecord)
+			LogMsg("mDNSCoreReceiveUpdateR ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord));
+		m->CurrentRecord = m->ResourceRecords;
+		while (m->CurrentRecord)
+			{
+			AuthRecord *const rr = m->CurrentRecord;
+			if (rr->resrec.InterfaceID == InterfaceID || (!rr->resrec.InterfaceID && (rr->ForceMCast || IsLocalDomain(rr->resrec.name))))
+				if (mDNSSameOpaque16(rr->updateid, msg->h.id))
+					{
+					rr->updateid = zeroID;
+					rr->expire   = NonZeroTime(m->timenow + updatelease * mDNSPlatformOneSecond);
+					LogSPS("Sleep Proxy %s record %5d %s", rr->WakeUp.HMAC.l[0] ? "transferred" : "registered", updatelease, ARDisplayString(m,rr));
+					if (rr->WakeUp.HMAC.l[0])
+						{
+						rr->WakeUp.HMAC = zeroEthAddr;	// Clear HMAC so that mDNS_Deregister_internal doesn't waste packets trying to wake this host
+						rr->RequireGoodbye = mDNSfalse;	// and we don't want to send goodbye for it
+						mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal);
+						}
+					}
+			// Mustn't advance m->CurrentRecord until *after* mDNS_Deregister_internal, because
+			// new records could have been added to the end of the list as a result of that call.
+			if (m->CurrentRecord == rr) // If m->CurrentRecord was not advanced for us, do it now
+				m->CurrentRecord = rr->next;
+			}
+		}
+	// If we were waiting to go to sleep, then this SPS registration or wide-area record deletion
+	// may have been the thing we were waiting for, so schedule another check to see if we can sleep now.
+	if (m->SleepLimit) m->NextScheduledSPRetry = m->timenow;
+	}
+
+mDNSexport void MakeNegativeCacheRecord(mDNS *const m, CacheRecord *const cr,
+	const domainname *const name, const mDNSu32 namehash, const mDNSu16 rrtype, const mDNSu16 rrclass, mDNSu32 ttl_seconds, mDNSInterfaceID InterfaceID, DNSServer *dnsserver)
+	{
+	if (cr == &m->rec.r && m->rec.r.resrec.RecordType)
+		{
+		LogMsg("MakeNegativeCacheRecord: m->rec appears to be already in use for %s", CRDisplayString(m, &m->rec.r));
+#if ForceAlerts
+		*(long*)0 = 0;
+#endif
+		}
+
+	// Create empty resource record
+	cr->resrec.RecordType    = kDNSRecordTypePacketNegative;
+	cr->resrec.InterfaceID   = InterfaceID;
+	cr->resrec.rDNSServer	 = dnsserver;
+	cr->resrec.name          = name;	// Will be updated to point to cg->name when we call CreateNewCacheEntry
+	cr->resrec.rrtype        = rrtype;
+	cr->resrec.rrclass       = rrclass;
+	cr->resrec.rroriginalttl = ttl_seconds;
+	cr->resrec.rdlength      = 0;
+	cr->resrec.rdestimate    = 0;
+	cr->resrec.namehash      = namehash;
+	cr->resrec.rdatahash     = 0;
+	cr->resrec.rdata = (RData*)&cr->smallrdatastorage;
+	cr->resrec.rdata->MaxRDLength = 0;
+
+	cr->NextInKAList       = mDNSNULL;
+	cr->TimeRcvd           = m->timenow;
+	cr->DelayDelivery      = 0;
+	cr->NextRequiredQuery  = m->timenow;
+	cr->LastUsed           = m->timenow;
+	cr->CRActiveQuestion   = mDNSNULL;
+	cr->UnansweredQueries  = 0;
+	cr->LastUnansweredTime = 0;
+#if ENABLE_MULTI_PACKET_QUERY_SNOOPING
+	cr->MPUnansweredQ      = 0;
+	cr->MPLastUnansweredQT = 0;
+	cr->MPUnansweredKA     = 0;
+	cr->MPExpectingKA      = mDNSfalse;
+#endif
+	cr->NextInCFList       = mDNSNULL;
+	}
+
+mDNSexport void mDNSCoreReceive(mDNS *const m, void *const pkt, const mDNSu8 *const end,
+	const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSIPPort dstport,
+	const mDNSInterfaceID InterfaceID)
+	{
+	mDNSInterfaceID ifid = InterfaceID;
+	DNSMessage  *msg  = (DNSMessage *)pkt;
+	const mDNSu8 StdQ = kDNSFlag0_QR_Query    | kDNSFlag0_OP_StdQuery;
+	const mDNSu8 StdR = kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery;
+	const mDNSu8 UpdQ = kDNSFlag0_QR_Query    | kDNSFlag0_OP_Update;
+	const mDNSu8 UpdR = kDNSFlag0_QR_Response | kDNSFlag0_OP_Update;
+	mDNSu8 QR_OP;
+	mDNSu8 *ptr = mDNSNULL;
+	mDNSBool TLS = (dstaddr == (mDNSAddr *)1);	// For debug logs: dstaddr = 0 means TCP; dstaddr = 1 means TLS
+	if (TLS) dstaddr = mDNSNULL;
+
+#ifndef UNICAST_DISABLED
+	if (mDNSSameAddress(srcaddr, &m->Router))
+		{
+#ifdef _LEGACY_NAT_TRAVERSAL_
+		if (mDNSSameIPPort(srcport, SSDPPort) || (m->SSDPSocket && mDNSSameIPPort(dstport, m->SSDPSocket->port)))
+			{
+			mDNS_Lock(m);
+			LNT_ConfigureRouterInfo(m, InterfaceID, pkt, (mDNSu16)(end - (mDNSu8 *)pkt));
+			mDNS_Unlock(m);
+			return;
+			}
+#endif
+		if (mDNSSameIPPort(srcport, NATPMPPort))
+			{
+			mDNS_Lock(m);
+			uDNS_ReceiveNATPMPPacket(m, InterfaceID, pkt, (mDNSu16)(end - (mDNSu8 *)pkt));
+			mDNS_Unlock(m);
+			return;
+			}
+		}
+#ifdef _LEGACY_NAT_TRAVERSAL_
+	else if (m->SSDPSocket && mDNSSameIPPort(dstport, m->SSDPSocket->port)) { debugf("Ignoring SSDP response from %#a:%d", srcaddr, mDNSVal16(srcport)); return; }
+#endif
+
+#endif
+	if ((unsigned)(end - (mDNSu8 *)pkt) < sizeof(DNSMessageHeader))
+		{
+		LogMsg("DNS Message from %#a:%d to %#a:%d length %d too short", srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), end - (mDNSu8 *)pkt);
+		return;
+		}
+	QR_OP = (mDNSu8)(msg->h.flags.b[0] & kDNSFlag0_QROP_Mask);
+	// Read the integer parts which are in IETF byte-order (MSB first, LSB second)
+	ptr = (mDNSu8 *)&msg->h.numQuestions;
+	msg->h.numQuestions   = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]);
+	msg->h.numAnswers     = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]);
+	msg->h.numAuthorities = (mDNSu16)((mDNSu16)ptr[4] << 8 | ptr[5]);
+	msg->h.numAdditionals = (mDNSu16)((mDNSu16)ptr[6] << 8 | ptr[7]);
+
+	if (!m) { LogMsg("mDNSCoreReceive ERROR m is NULL"); return; }
+	
+	// We use zero addresses and all-ones addresses at various places in the code to indicate special values like "no address"
+	// If we accept and try to process a packet with zero or all-ones source address, that could really mess things up
+	if (srcaddr && !mDNSAddressIsValid(srcaddr)) { debugf("mDNSCoreReceive ignoring packet from %#a", srcaddr); return; }
+
+	mDNS_Lock(m);
+	m->PktNum++;
+#ifndef UNICAST_DISABLED
+	if (!dstaddr || (!mDNSAddressIsAllDNSLinkGroup(dstaddr) && (QR_OP == StdR || QR_OP == UpdR)))
+		if (!mDNSOpaque16IsZero(msg->h.id)) // uDNS_ReceiveMsg only needs to get real uDNS responses, not "QU" mDNS responses
+			{
+			ifid = mDNSInterface_Any;
+			if (mDNS_PacketLoggingEnabled)
+				DumpPacket(m, mStatus_NoError, mDNSfalse, TLS ? "TLS" : !dstaddr ? "TCP" : "UDP", srcaddr, srcport, dstaddr, dstport, msg, end);
+			uDNS_ReceiveMsg(m, msg, end, srcaddr, srcport);
+			// Note: mDNSCore also needs to get access to received unicast responses
+			}
+#endif
+	if      (QR_OP == StdQ) mDNSCoreReceiveQuery   (m, msg, end, srcaddr, srcport, dstaddr, dstport, ifid);
+	else if (QR_OP == StdR) mDNSCoreReceiveResponse(m, msg, end, srcaddr, srcport, dstaddr, dstport, ifid);
+	else if (QR_OP == UpdQ) mDNSCoreReceiveUpdate  (m, msg, end, srcaddr, srcport, dstaddr, dstport, InterfaceID);
+	else if (QR_OP == UpdR) mDNSCoreReceiveUpdateR (m, msg, end,                                     InterfaceID);
+	else
+		{
+		LogMsg("Unknown DNS packet type %02X%02X from %#-15a:%-5d to %#-15a:%-5d length %d on %p (ignored)",
+			msg->h.flags.b[0], msg->h.flags.b[1], srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), end - (mDNSu8 *)pkt, InterfaceID);
+		if (mDNS_LoggingEnabled)
+			{
+			int i = 0;
+			while (i<end - (mDNSu8 *)pkt)
+				{
+				char buffer[128];
+				char *p = buffer + mDNS_snprintf(buffer, sizeof(buffer), "%04X", i);
+				do if (i<end - (mDNSu8 *)pkt) p += mDNS_snprintf(p, sizeof(buffer), " %02X", ((mDNSu8 *)pkt)[i]); while (++i & 15);
+				LogInfo("%s", buffer);
+				}
+			}
+		}
+	// Packet reception often causes a change to the task list:
+	// 1. Inbound queries can cause us to need to send responses
+	// 2. Conflicing response packets received from other hosts can cause us to need to send defensive responses
+	// 3. Other hosts announcing deletion of shared records can cause us to need to re-assert those records
+	// 4. Response packets that answer questions may cause our client to issue new questions
+	mDNS_Unlock(m);
+	}
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - Searcher Functions
+#endif
+
+// Targets are considered the same if both queries are untargeted, or
+// if both are targeted to the same address+port
+// (If Target address is zero, TargetPort is undefined)
+#define SameQTarget(A,B) (((A)->Target.type == mDNSAddrType_None && (B)->Target.type == mDNSAddrType_None) || \
+	(mDNSSameAddress(&(A)->Target, &(B)->Target) && mDNSSameIPPort((A)->TargetPort, (B)->TargetPort)))
+
+// Note: We explicitly disallow making a public query be a duplicate of a private one. This is to avoid the
+// circular deadlock where a client does a query for something like "dns-sd -Q _dns-query-tls._tcp.company.com SRV"
+// and we have a key for company.com, so we try to locate the private query server for company.com, which necessarily entails
+// doing a standard DNS query for the _dns-query-tls._tcp SRV record for company.com. If we make the latter (public) query
+// a duplicate of the former (private) query, then it will block forever waiting for an answer that will never come.
+//
+// We keep SuppressUnusable questions separate so that we can return a quick response to them and not get blocked behind
+// the queries that are not marked SuppressUnusable. But if the query is not suppressed, they are treated the same as
+// non-SuppressUnusable questions. This should be fine as the goal of SuppressUnusable is to return quickly only if it
+// is suppressed. If it is not suppressed, we do try all the DNS servers for valid answers like any other question.
+// The main reason for this design is that cache entries point to a *single* question and that question is responsible
+// for keeping the cache fresh as long as it is active. Having multiple active question for a single cache entry
+// breaks this design principle.
+
+// If IsLLQ(Q) is true, it means the question is both:
+// (a) long-lived and
+// (b) being performed by a unicast DNS long-lived query (either full LLQ, or polling)
+// for multicast questions, we don't want to treat LongLived as anything special
+#define IsLLQ(Q) ((Q)->LongLived && !mDNSOpaque16IsZero((Q)->TargetQID))
+
+mDNSlocal DNSQuestion *FindDuplicateQuestion(const mDNS *const m, const DNSQuestion *const question)
+	{
+	DNSQuestion *q;
+	// Note: A question can only be marked as a duplicate of one that occurs *earlier* in the list.
+	// This prevents circular references, where two questions are each marked as a duplicate of the other.
+	// Accordingly, we break out of the loop when we get to 'question', because there's no point searching
+	// further in the list.
+	for (q = m->Questions; q && q != question; q=q->next)		// Scan our list for another question
+		if (q->InterfaceID == question->InterfaceID &&			// with the same InterfaceID,
+			SameQTarget(q, question)                &&			// and same unicast/multicast target settings
+			q->qtype      == question->qtype        &&			// type,
+			q->qclass     == question->qclass       &&			// class,
+			IsLLQ(q)      == IsLLQ(question)        &&			// and long-lived status matches
+			(!q->AuthInfo || question->AuthInfo)    &&			// to avoid deadlock, don't make public query dup of a private one
+			(q->SuppressQuery == question->SuppressQuery) &&	// Questions that are suppressed/not suppressed
+			q->qnamehash  == question->qnamehash    &&
+			SameDomainName(&q->qname, &question->qname))		// and name
+			return(q);
+	return(mDNSNULL);
+	}
+
+// This is called after a question is deleted, in case other identical questions were being suppressed as duplicates
+mDNSlocal void UpdateQuestionDuplicates(mDNS *const m, DNSQuestion *const question)
+	{
+	DNSQuestion *q;
+	DNSQuestion *first = mDNSNULL;
+
+	// This is referring to some other question as duplicate. No other question can refer to this
+	// question as a duplicate.
+	if (question->DuplicateOf)
+		{
+		LogInfo("UpdateQuestionDuplicates: question %p %##s (%s) duplicate of %p %##s (%s)",
+			question, question->qname.c, DNSTypeName(question->qtype),
+			question->DuplicateOf, question->DuplicateOf->qname.c, DNSTypeName(question->DuplicateOf->qtype));
+		return;
+		}
+
+	for (q = m->Questions; q; q=q->next)		// Scan our list of questions
+		if (q->DuplicateOf == question)			// To see if any questions were referencing this as their duplicate
+			{
+			q->DuplicateOf = first;
+			if (!first)
+				{
+				first = q;
+				// If q used to be a duplicate, but now is not,
+				// then inherit the state from the question that's going away
+				q->LastQTime         = question->LastQTime;
+				q->ThisQInterval     = question->ThisQInterval;
+				q->ExpectUnicastResp = question->ExpectUnicastResp;
+				q->LastAnswerPktNum  = question->LastAnswerPktNum;
+				q->RecentAnswerPkts  = question->RecentAnswerPkts;
+				q->RequestUnicast    = question->RequestUnicast;
+				q->LastQTxTime       = question->LastQTxTime;
+				q->CNAMEReferrals    = question->CNAMEReferrals;
+				q->nta               = question->nta;
+				q->servAddr          = question->servAddr;
+				q->servPort          = question->servPort;
+				q->qDNSServer        = question->qDNSServer;
+				q->validDNSServers   = question->validDNSServers;
+				q->unansweredQueries = question->unansweredQueries;
+				q->noServerResponse  = question->noServerResponse;
+				q->triedAllServersOnce = question->triedAllServersOnce;
+
+				q->TargetQID         = question->TargetQID;
+				q->LocalSocket       = question->LocalSocket;
+
+				q->state             = question->state;
+			//	q->tcp               = question->tcp;
+				q->ReqLease          = question->ReqLease;
+				q->expire            = question->expire;
+				q->ntries            = question->ntries;
+				q->id                = question->id;
+
+				question->LocalSocket = mDNSNULL;
+				question->nta        = mDNSNULL;	// If we've got a GetZoneData in progress, transfer it to the newly active question
+			//	question->tcp        = mDNSNULL;
+
+				if (q->LocalSocket)
+					debugf("UpdateQuestionDuplicates transferred LocalSocket pointer for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+
+				if (q->nta)
+					{
+					LogInfo("UpdateQuestionDuplicates transferred nta pointer for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+					q->nta->ZoneDataContext = q;
+					}
+
+				// Need to work out how to safely transfer this state too -- appropriate context pointers need to be updated or the code will crash
+				if (question->tcp) LogInfo("UpdateQuestionDuplicates did not transfer tcp pointer");
+
+				if (question->state == LLQ_Established)
+					{
+					LogInfo("UpdateQuestionDuplicates transferred LLQ state for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+					question->state = 0;	// Must zero question->state, or mDNS_StopQuery_internal will clean up and cancel our LLQ from the server
+					}
+
+				SetNextQueryTime(m,q);
+				}
+			}
+	}
+
+mDNSexport McastResolver *mDNS_AddMcastResolver(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, mDNSu32 timeout)
+	{
+	McastResolver **p = &m->McastResolvers;
+	McastResolver *tmp = mDNSNULL;
+	
+	if (!d) d = (const domainname *)"";
+
+	LogInfo("mDNS_AddMcastResolver: Adding %##s, InterfaceID %p, timeout %u", d->c, interface, timeout);
+
+	if (m->mDNS_busy != m->mDNS_reentrancy+1)
+		LogMsg("mDNS_AddMcastResolver: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
+
+	while (*p)	// Check if we already have this {interface, domain} tuple registered
+		{
+		if ((*p)->interface == interface && SameDomainName(&(*p)->domain, d))
+			{
+			if (!((*p)->flags & DNSServer_FlagDelete)) LogMsg("Note: Mcast Resolver domain %##s (%p) registered more than once", d->c, interface);
+			(*p)->flags &= ~DNSServer_FlagDelete;
+			tmp = *p;
+			*p = tmp->next;
+			tmp->next = mDNSNULL;
+			}
+		else
+			p=&(*p)->next;
+		}
+
+	if (tmp) *p = tmp; // move to end of list, to ensure ordering from platform layer
+	else
+		{
+		// allocate, add to list
+		*p = mDNSPlatformMemAllocate(sizeof(**p));
+		if (!*p) LogMsg("mDNS_AddMcastResolver: ERROR!! - malloc");
+		else
+			{
+			(*p)->interface = interface;
+			(*p)->flags     = DNSServer_FlagNew;
+			(*p)->timeout   = timeout;
+			AssignDomainName(&(*p)->domain, d);
+			(*p)->next = mDNSNULL;
+			}
+		}
+	return(*p);
+	}
+
+mDNSinline mDNSs32 PenaltyTimeForServer(mDNS *m, DNSServer *server)
+	{
+	mDNSs32 ptime = 0;
+	if (server->penaltyTime != 0)
+		{
+		ptime = server->penaltyTime - m->timenow;
+		if (ptime < 0)
+			{
+			// This should always be a positive value between 0 and DNSSERVER_PENALTY_TIME
+			// If it does not get reset in ResetDNSServerPenalties for some reason, we do it
+			// here
+			LogMsg("PenaltyTimeForServer: PenaltyTime negative %d, (server penaltyTime %d, timenow %d) resetting the penalty",
+				ptime, server->penaltyTime, m->timenow);
+			server->penaltyTime = 0;
+			ptime = 0;
+			}
+		}
+	return ptime;
+	}
+
+//Checks to see whether the newname is a better match for the name, given the best one we have
+//seen so far (given in bestcount).
+//Returns -1 if the newname is not a better match
+//Returns 0 if the newname is the same as the old match
+//Returns 1 if the newname is a better match
+mDNSlocal int BetterMatchForName(const domainname *name, int namecount, const domainname *newname, int newcount,
+	int bestcount)
+	{
+	// If the name contains fewer labels than the new server's domain or the new name
+	// contains fewer labels than the current best, then it can't possibly be a better match
+	if (namecount < newcount || newcount < bestcount) return -1;
+
+	// If there is no match, return -1 and the caller will skip this newname for
+	// selection
+	//
+	// If we find a match and the number of labels is the same as bestcount, then
+	// we return 0 so that the caller can do additional logic to pick one of
+	// the best based on some other factors e.g., penaltyTime
+	//
+	// If we find a match and the number of labels is more than bestcount, then we
+	// return 1 so that the caller can pick this over the old one.
+	//
+	// Note: newcount can either be equal or greater than bestcount beause of the
+	// check above.
+
+	if (SameDomainName(SkipLeadingLabels(name, namecount - newcount), newname))
+		return bestcount == newcount ? 0 : 1;
+	else
+		return -1;
+	}
+
+// Normally, we have McastResolvers for .local, in-addr.arpa and ip6.arpa. But there
+// can be queries that can forced to multicast (ForceMCast) even though they don't end in these
+// names. In that case, we give a default timeout of 5 seconds
+#define DEFAULT_MCAST_TIMEOUT	5
+mDNSlocal mDNSu32 GetTimeoutForMcastQuestion(mDNS *m, DNSQuestion *question)
+	{
+	McastResolver *curmatch = mDNSNULL;
+	int bestmatchlen = -1, namecount = CountLabels(&question->qname);
+	McastResolver *curr;
+	int bettermatch, currcount;
+	for (curr = m->McastResolvers; curr; curr = curr->next)
+		{
+		currcount = CountLabels(&curr->domain);
+		bettermatch = BetterMatchForName(&question->qname, namecount, &curr->domain, currcount, bestmatchlen);
+		// Take the first best match. If there are multiple equally good matches (bettermatch = 0), we take
+		// the timeout value from the first one
+		if (bettermatch == 1)
+			{
+			curmatch = curr;
+			bestmatchlen = currcount;
+			}
+		}
+	LogInfo("GetTimeoutForMcastQuestion: question %##s curmatch %p, Timeout %d", question->qname.c, curmatch,
+		curmatch ? curmatch->timeout : DEFAULT_MCAST_TIMEOUT);
+	return ( curmatch ? curmatch->timeout : DEFAULT_MCAST_TIMEOUT);
+	}
+
+// Sets all the Valid DNS servers for a question
+mDNSexport mDNSu32 SetValidDNSServers(mDNS *m, DNSQuestion *question)
+	{
+	DNSServer *curmatch = mDNSNULL;
+	int bestmatchlen = -1, namecount = CountLabels(&question->qname);
+	DNSServer *curr;
+	int bettermatch, currcount;
+	int index = 0;
+	mDNSu32 timeout = 0;
+
+	question->validDNSServers = zeroOpaque64;
+	for (curr = m->DNSServers; curr; curr = curr->next)
+		{
+		debugf("SetValidDNSServers: Parsing DNS server Address %#a (Domain %##s), Scope: %d", &curr->addr, curr->domain.c, curr->scoped);
+		// skip servers that will soon be deleted
+		if (curr->flags & DNSServer_FlagDelete)
+			{ debugf("SetValidDNSServers: Delete set for index %d, DNS server %#a (Domain %##s), scoped %d", index, &curr->addr, curr->domain.c, curr->scoped); continue; }
+
+		// This happens normally when you unplug the interface where we reset the interfaceID to mDNSInterface_Any for all
+		// the DNS servers whose scope match the interfaceID. Few seconds later, we also receive the updated DNS configuration.
+		// But any questions that has mDNSInterface_Any scope that are started/restarted before we receive the update
+		// (e.g., CheckSuppressUnusableQuestions is called when interfaces are deregistered with the core) should not
+		// match the scoped entries by mistake. 
+		//
+		// Note: DNS configuration change will help pick the new dns servers but currently it does not affect the timeout
+
+		if (curr->scoped && curr->interface == mDNSInterface_Any)
+			{ debugf("SetValidDNSServers: Scoped DNS server %#a (Domain %##s) with Interface Any", &curr->addr, curr->domain.c); continue; }
+
+		currcount = CountLabels(&curr->domain);
+		if ((!curr->scoped && (!question->InterfaceID || (question->InterfaceID == mDNSInterface_Unicast))) || (curr->interface == question->InterfaceID))
+			{
+			bettermatch = BetterMatchForName(&question->qname, namecount, &curr->domain, currcount, bestmatchlen);
+
+			// If we found a better match (bettermatch == 1) then clear all the bits
+			// corresponding to the old DNSServers that we have may set before and start fresh.
+			// If we find an equal match, then include that DNSServer also by setting the corresponding
+			// bit
+			if ((bettermatch == 1) || (bettermatch == 0))
+				{
+				curmatch = curr;
+				bestmatchlen = currcount;
+				if (bettermatch) { debugf("SetValidDNSServers: Resetting all the bits"); question->validDNSServers = zeroOpaque64; timeout = 0; }
+				debugf("SetValidDNSServers: question %##s Setting the bit for DNS server Address %#a (Domain %##s), Scoped:%d index %d,"
+					" Timeout %d, interface %p", question->qname.c, &curr->addr, curr->domain.c, curr->scoped, index, curr->timeout,
+					curr->interface);
+				timeout += curr->timeout;
+				bit_set_opaque64(question->validDNSServers, index);
+				}
+			}
+		index++;
+		}
+	question->noServerResponse = 0;
+	
+	debugf("SetValidDNSServers: ValidDNSServer bits  0x%x%x for question %p %##s (%s)",
+		question->validDNSServers.l[1], question->validDNSServers.l[0], question, question->qname.c, DNSTypeName(question->qtype));
+	// If there are no matching resolvers, then use the default value to timeout
+	return (timeout ? timeout : DEFAULT_UDNS_TIMEOUT);
+	}
+
+// Get the Best server that matches a name. If you find penalized servers, look for the one
+// that will come out of the penalty box soon
+mDNSlocal DNSServer *GetBestServer(mDNS *m, const domainname *name, mDNSInterfaceID InterfaceID, mDNSOpaque64 validBits, int *selected, mDNSBool nameMatch)
+	{
+	DNSServer *curmatch = mDNSNULL;
+	int bestmatchlen = -1, namecount = name ? CountLabels(name) : 0;
+	DNSServer *curr;
+	mDNSs32 bestPenaltyTime, currPenaltyTime;
+	int bettermatch, currcount;
+	int index = 0;
+	int currindex = -1;
+
+	debugf("GetBestServer: ValidDNSServer bits  0x%x%x", validBits.l[1], validBits.l[0]);
+	bestPenaltyTime = DNSSERVER_PENALTY_TIME + 1;
+	for (curr = m->DNSServers; curr; curr = curr->next)
+		{
+		// skip servers that will soon be deleted
+		if (curr->flags & DNSServer_FlagDelete)
+			{ debugf("GetBestServer: Delete set for index %d, DNS server %#a (Domain %##s), scoped %d", index, &curr->addr, curr->domain.c, curr->scoped); continue; }
+
+		// Check if this is a valid DNSServer
+		if (!bit_get_opaque64(validBits, index)) { debugf("GetBestServer: continuing for index %d", index); index++; continue; }
+
+		currcount = CountLabels(&curr->domain);
+		currPenaltyTime = PenaltyTimeForServer(m, curr);
+
+		debugf("GetBestServer: Address %#a (Domain %##s), PenaltyTime(abs) %d, PenaltyTime(rel) %d",
+			&curr->addr, curr->domain.c, curr->penaltyTime, currPenaltyTime);
+
+		// If there are multiple best servers for a given question, we will pick the first one
+		// if none of them are penalized. If some of them are penalized in that list, we pick
+		// the least penalized one. BetterMatchForName walks through all best matches and
+		// "currPenaltyTime < bestPenaltyTime" check lets us either pick the first best server
+		// in the list when there are no penalized servers and least one among them
+		// when there are some penalized servers
+		//
+		// Notes on InterfaceID matching:
+		//
+		// 1) A DNSServer entry may have an InterfaceID but the scoped flag may not be set. This
+		// is the old way of specifying an InterfaceID option for DNSServer. We recoginize these
+		// entries by "scoped" being false. These are like any other unscoped entries except that
+		// if it is picked e.g., domain match, when the packet is sent out later, the packet will
+		// be sent out on that interface. Theese entries can be matched by either specifying a
+		// zero InterfaceID or non-zero InterfaceID on the question. Specifying an InterfaceID on
+		// the question will cause an extra check on matching the InterfaceID on the question
+		// against the DNSServer.
+		// 
+		// 2) A DNSServer may also have both scoped set and InterfaceID non-NULL. This
+		// is the new way of specifying an InterfaceID option for DNSServer. These will be considered
+		// only when the question has non-zero interfaceID.
+
+		if ((!curr->scoped && !InterfaceID) || (curr->interface == InterfaceID))
+			{
+
+			// If we know that all the names are already equally good matches, then skip calling BetterMatchForName.
+			// This happens when we initially walk all the DNS servers and set the validity bit on the question.
+			// Actually we just need PenaltyTime match, but for the sake of readability we just skip the expensive
+			// part and still do some redundant steps e.g., InterfaceID match
+
+			if (nameMatch) bettermatch = BetterMatchForName(name, namecount, &curr->domain, currcount, bestmatchlen);
+			else bettermatch = 0;
+
+			// If we found a better match (bettermatch == 1) then we don't need to
+			// compare penalty times. But if we found an equal match, then we compare
+			// the penalty times to pick a better match
+
+			if ((bettermatch == 1) || ((bettermatch == 0) && currPenaltyTime < bestPenaltyTime))
+				{ currindex = index; curmatch = curr; bestmatchlen = currcount; bestPenaltyTime = currPenaltyTime; }
+			}
+		index++;
+		}
+	if (selected) *selected = currindex;
+	return curmatch;
+	}
+
+// Look up a DNS Server, matching by name and InterfaceID
+mDNSexport DNSServer *GetServerForName(mDNS *m, const domainname *name, mDNSInterfaceID InterfaceID)
+    {
+	DNSServer *curmatch = mDNSNULL;
+	char *ifname = mDNSNULL;	// for logging purposes only
+	mDNSOpaque64 allValid;
+
+	if ((InterfaceID == mDNSInterface_Unicast) || (InterfaceID == mDNSInterface_LocalOnly))
+		InterfaceID = mDNSNULL;
+
+	if (InterfaceID) ifname = InterfaceNameForID(m, InterfaceID);
+
+	// By passing in all ones, we make sure that every DNS server is considered
+	allValid.l[0] = allValid.l[1] = 0xFFFFFFFF;
+
+	curmatch = GetBestServer(m, name, InterfaceID, allValid, mDNSNULL, mDNStrue);
+
+	if (curmatch != mDNSNULL)
+		LogInfo("GetServerForName: DNS server %#a:%d (Penalty Time Left %d) (Scope %s:%p) found for name %##s", &curmatch->addr,
+		    mDNSVal16(curmatch->port), (curmatch->penaltyTime ? (curmatch->penaltyTime - m->timenow) : 0), ifname ? ifname : "None",
+		InterfaceID, name);
+	else
+		LogInfo("GetServerForName: no DNS server (Scope %s:%p) found for name %##s", ifname ? ifname : "None", InterfaceID, name);
+
+	return(curmatch);
+	}
+
+// Look up a DNS Server for a question within its valid DNSServer bits
+mDNSexport DNSServer *GetServerForQuestion(mDNS *m, DNSQuestion *question)
+    {
+	DNSServer *curmatch = mDNSNULL;
+	char *ifname = mDNSNULL;	// for logging purposes only
+	mDNSInterfaceID InterfaceID = question->InterfaceID;
+	const domainname *name = &question->qname;
+	int currindex;
+
+	if ((InterfaceID == mDNSInterface_Unicast) || (InterfaceID == mDNSInterface_LocalOnly))
+		InterfaceID = mDNSNULL;
+
+	if (InterfaceID) ifname = InterfaceNameForID(m, InterfaceID);
+
+	if (!mDNSOpaque64IsZero(&question->validDNSServers))
+		{
+		curmatch = GetBestServer(m, name, InterfaceID, question->validDNSServers, &currindex, mDNSfalse);
+		if (currindex != -1) bit_clr_opaque64(question->validDNSServers, currindex);
+		}
+
+	if (curmatch != mDNSNULL)
+		LogInfo("GetServerForQuestion: %p DNS server %#a:%d (Penalty Time Left %d) (Scope %s:%p) found for name %##s (%s)", question, &curmatch->addr,
+		    mDNSVal16(curmatch->port), (curmatch->penaltyTime ? (curmatch->penaltyTime - m->timenow) : 0), ifname ? ifname : "None",
+		InterfaceID, name, DNSTypeName(question->qtype));
+	else
+		LogInfo("GetServerForQuestion: %p no DNS server (Scope %s:%p) found for name %##s (%s)", question, ifname ? ifname : "None", InterfaceID, name, DNSTypeName(question->qtype));
+
+	return(curmatch);
+	}
+
+
+#define ValidQuestionTarget(Q) (((Q)->Target.type == mDNSAddrType_IPv4 || (Q)->Target.type == mDNSAddrType_IPv6) && \
+	(mDNSSameIPPort((Q)->TargetPort, UnicastDNSPort) || mDNSSameIPPort((Q)->TargetPort, MulticastDNSPort)))
+
+// Called in normal client context (lock not held)
+mDNSlocal void LLQNATCallback(mDNS *m, NATTraversalInfo *n)
+	{
+	DNSQuestion *q;
+	(void)n;    // Unused
+	mDNS_Lock(m);
+	LogInfo("LLQNATCallback external address:port %.4a:%u, NAT result %d", &n->ExternalAddress, mDNSVal16(n->ExternalPort), n->Result);
+	for (q = m->Questions; q; q=q->next)
+		if (ActiveQuestion(q) && !mDNSOpaque16IsZero(q->TargetQID) && q->LongLived)
+			startLLQHandshake(m, q);	// If ExternalPort is zero, will do StartLLQPolling instead
+#if APPLE_OSX_mDNSResponder
+	UpdateAutoTunnelDomainStatuses(m);
+#endif
+	mDNS_Unlock(m);
+	}
+
+mDNSlocal mDNSBool ShouldSuppressQuery(mDNS *const m, domainname *qname, mDNSu16 qtype, mDNSInterfaceID InterfaceID)
+	{
+	NetworkInterfaceInfo *i;
+	mDNSs32 iptype;
+	DomainAuthInfo *AuthInfo;
+
+	if (qtype == kDNSType_A) iptype = mDNSAddrType_IPv4;
+	else if (qtype == kDNSType_AAAA) iptype = mDNSAddrType_IPv6;
+	else { LogInfo("ShouldSuppressQuery: Query not suppressed for %##s, qtype %s, not A/AAAA type", qname, DNSTypeName(qtype)); return mDNSfalse; }
+
+	// We still want the ability to be able to listen to the local services and hence
+	// don't fail .local requests. We always have a loopback interface which we don't
+	// check here.
+	if (InterfaceID != mDNSInterface_Unicast && IsLocalDomain(qname)) { LogInfo("ShouldSuppressQuery: Query not suppressed for %##s, qtype %s, Local question", qname, DNSTypeName(qtype)); return mDNSfalse; }
+
+	// Skip Private domains as we have special addresses to get the hosts in the Private domain
+	AuthInfo = GetAuthInfoForName_internal(m, qname);
+	if (AuthInfo && !AuthInfo->deltime && AuthInfo->AutoTunnel)
+		{ LogInfo("ShouldSuppressQuery: Query not suppressed for %##s, qtype %s, Private Domain", qname, DNSTypeName(qtype)); return mDNSfalse; }
+
+	// Match on Type, Address and InterfaceID
+	//
+	// Check whether we are looking for a name that ends in .local, then presence of a link-local
+	// address on the interface is sufficient.
+	for (i = m->HostInterfaces; i; i = i->next)
+		{
+		if (i->ip.type != iptype) continue;
+
+		if (!InterfaceID || (InterfaceID == mDNSInterface_LocalOnly) || (InterfaceID == mDNSInterface_P2P) ||
+			(InterfaceID == mDNSInterface_Unicast) || (i->InterfaceID == InterfaceID))
+			{
+			if (iptype == mDNSAddrType_IPv4 && !mDNSv4AddressIsLoopback(&i->ip.ip.v4) && !mDNSv4AddressIsLinkLocal(&i->ip.ip.v4))
+				{
+				LogInfo("ShouldSuppressQuery: Query not suppressed for %##s, qtype %s, Local Address %.4a found", qname, DNSTypeName(qtype),
+					&i->ip.ip.v4);
+				return mDNSfalse;
+				}
+			else if (iptype == mDNSAddrType_IPv6 &&
+				!mDNSv6AddressIsLoopback(&i->ip.ip.v6) &&
+				!mDNSv6AddressIsLinkLocal(&i->ip.ip.v6) &&
+				!mDNSSameIPv6Address(i->ip.ip.v6, m->AutoTunnelHostAddr) &&
+				!mDNSSameIPv6Address(i->ip.ip.v6, m->AutoTunnelRelayAddrOut))
+				{
+				LogInfo("ShouldSuppressQuery: Query not suppressed for %##s, qtype %s, Local Address %.16a found", qname, DNSTypeName(qtype),
+					&i->ip.ip.v6);
+				return mDNSfalse;
+				}
+			}
+		}
+	LogInfo("ShouldSuppressQuery: Query suppressed for %##s, qtype %s, because no matching interface found", qname, DNSTypeName(qtype));
+	return mDNStrue;
+	}
+
+mDNSlocal void CacheRecordRmvEventsForCurrentQuestion(mDNS *const m, DNSQuestion *q)
+	{
+	CacheRecord *rr;
+	mDNSu32 slot;
+	CacheGroup *cg;
+
+	slot = HashSlot(&q->qname);
+	cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname);
+	for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
+		{
+		// Don't deliver RMV events for negative records
+		if (rr->resrec.RecordType == kDNSRecordTypePacketNegative)
+			{
+ 			LogInfo("CacheRecordRmvEventsForCurrentQuestion: CacheRecord %s Suppressing RMV events for question %p %##s (%s), CRActiveQuestion %p, CurrentAnswers %d",
+				CRDisplayString(m, rr), q, q->qname.c, DNSTypeName(q->qtype), rr->CRActiveQuestion, q->CurrentAnswers);
+			continue;
+			}
+
+		if (SameNameRecordAnswersQuestion(&rr->resrec, q))
+			{
+ 			LogInfo("CacheRecordRmvEventsForCurrentQuestion: Calling AnswerCurrentQuestionWithResourceRecord (RMV) for question %##s using resource record %s LocalAnswers %d",
+				q->qname.c, CRDisplayString(m, rr), q->LOAddressAnswers);
+
+			q->CurrentAnswers--;
+			if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers--;
+			if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers--;
+
+			if (rr->CRActiveQuestion == q)
+				{
+				DNSQuestion *qptr;
+				// If this was the active question for this cache entry, it was the one that was
+				// responsible for keeping the cache entry fresh when the cache entry was reaching
+				// its expiry. We need to handover the responsibility to someone else. Otherwise,
+				// when the cache entry is about to expire, we won't find an active question
+				// (pointed by CRActiveQuestion) to refresh the cache.
+				for (qptr = m->Questions; qptr; qptr=qptr->next)
+ 					if (qptr != q && ActiveQuestion(qptr) && ResourceRecordAnswersQuestion(&rr->resrec, qptr))
+						break;
+
+				if (qptr)
+					LogInfo("CacheRecordRmvEventsForCurrentQuestion: Updating CRActiveQuestion to %p for cache record %s, "
+						"Original question CurrentAnswers %d, new question CurrentAnswers %d, SuppressUnusable %d, SuppressQuery %d",
+						qptr, CRDisplayString(m,rr), q->CurrentAnswers, qptr->CurrentAnswers, qptr->SuppressUnusable, qptr->SuppressQuery);
+
+				rr->CRActiveQuestion = qptr;		// Question used to be active; new value may or may not be null
+				if (!qptr) m->rrcache_active--;	// If no longer active, decrement rrcache_active count
+				}
+			AnswerCurrentQuestionWithResourceRecord(m, rr, QC_rmv);
+			if (m->CurrentQuestion != q) break;		// If callback deleted q, then we're finished here
+			}
+		}
+	}
+
+mDNSlocal mDNSBool IsQuestionNew(mDNS *const m, DNSQuestion *question)
+	{
+	DNSQuestion *q;
+	for (q = m->NewQuestions; q; q = q->next)
+		if (q == question) return mDNStrue;
+	return mDNSfalse;
+	}
+
+mDNSlocal mDNSBool LocalRecordRmvEventsForQuestion(mDNS *const m, DNSQuestion *q)
+	{
+	AuthRecord *rr;
+	mDNSu32 slot;
+	AuthGroup *ag;
+
+	if (m->CurrentQuestion)
+		LogMsg("LocalRecordRmvEventsForQuestion: ERROR m->CurrentQuestion already set: %##s (%s)",
+			m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype));
+
+	if (IsQuestionNew(m, q))
+		{
+		LogInfo("LocalRecordRmvEventsForQuestion: New Question %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+		return mDNStrue;
+		}
+	m->CurrentQuestion = q;
+	slot = AuthHashSlot(&q->qname);
+	ag = AuthGroupForName(&m->rrauth, slot, q->qnamehash, &q->qname);
+	if (ag)
+		{
+		for (rr = ag->members; rr; rr=rr->next)
+			// Filter the /etc/hosts records - LocalOnly, Unique, A/AAAA/CNAME
+			if (LORecordAnswersAddressType(rr) && LocalOnlyRecordAnswersQuestion(rr, q))
+				{
+				LogInfo("LocalRecordRmvEventsForQuestion: Delivering possible Rmv events with record %s",
+					ARDisplayString(m, rr));
+				if (q->CurrentAnswers <= 0 || q->LOAddressAnswers <= 0)
+					{
+					LogMsg("LocalRecordRmvEventsForQuestion: ERROR!! CurrentAnswers or LOAddressAnswers is zero %p %##s"
+						" (%s) CurrentAnswers %d, LOAddressAnswers %d", q, q->qname.c, DNSTypeName(q->qtype),
+						q->CurrentAnswers, q->LOAddressAnswers);
+					continue;
+					}
+				AnswerLocalQuestionWithLocalAuthRecord(m, rr, QC_rmv);		// MUST NOT dereference q again
+				if (m->CurrentQuestion != q) { m->CurrentQuestion = mDNSNULL; return mDNSfalse; }
+				}
+		}
+	m->CurrentQuestion = mDNSNULL;
+	return mDNStrue;
+	}
+
+// Returns false if the question got deleted while delivering the RMV events
+// The caller should handle the case 
+mDNSlocal mDNSBool CacheRecordRmvEventsForQuestion(mDNS *const m, DNSQuestion *q)
+	{
+	if (m->CurrentQuestion)
+		LogMsg("CacheRecordRmvEventsForQuestion: ERROR m->CurrentQuestion already set: %##s (%s)",
+			m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype));
+
+	// If it is a new question, we have not delivered any ADD events yet. So, don't deliver RMV events.
+	// If this question was answered using local auth records, then you can't deliver RMVs using cache
+	if (!IsQuestionNew(m, q) && !q->LOAddressAnswers) 
+		{
+		m->CurrentQuestion = q;
+		CacheRecordRmvEventsForCurrentQuestion(m, q);
+		if (m->CurrentQuestion != q) { m->CurrentQuestion = mDNSNULL; return mDNSfalse; }
+		m->CurrentQuestion = mDNSNULL;
+		}
+	else { LogInfo("CacheRecordRmvEventsForQuestion: Question %p %##s (%s) is a new question", q, q->qname.c, DNSTypeName(q->qtype)); }
+	return mDNStrue;
+	}
+
+// The caller should hold the lock
+mDNSexport void CheckSuppressUnusableQuestions(mDNS *const m)
+	{
+	DNSQuestion *q;
+	DNSQuestion *restart = mDNSNULL;
+
+	// We look through all questions including new questions. During network change events,
+	// we potentially restart questions here in this function that ends up as new questions,
+	// which may be suppressed at this instance. Before it is handled we get another network
+	// event that changes the status e.g., address becomes available. If we did not process
+	// new questions, we would never change its SuppressQuery status.
+	//
+	// CurrentQuestion is used by RmvEventsForQuestion below. While delivering RMV events, the
+	// application callback can potentially stop the current question (detected by CurrentQuestion) or
+	// *any* other question which could be the next one that we may process here. RestartQuestion
+	// points to the "next" question which will be automatically advanced in mDNS_StopQuery_internal
+	// if the "next" question is stopped while the CurrentQuestion is stopped
+	if (m->RestartQuestion)
+		LogMsg("CheckSuppressUnusableQuestions: ERROR!! m->RestartQuestion already set: %##s (%s)",
+			m->RestartQuestion->qname.c, DNSTypeName(m->RestartQuestion->qtype));
+	m->RestartQuestion = m->Questions;
+	while (m->RestartQuestion)
+		{
+		q = m->RestartQuestion;
+		m->RestartQuestion = q->next;
+		if (!mDNSOpaque16IsZero(q->TargetQID) && q->SuppressUnusable)
+			{
+			mDNSBool old = q->SuppressQuery;
+			q->SuppressQuery = ShouldSuppressQuery(m, &q->qname, q->qtype, q->InterfaceID);
+			if (q->SuppressQuery != old)
+				{
+				// NOTE: CacheRecordRmvEventsForQuestion will not generate RMV events for queries that have non-zero
+				// LOddressAnswers. Hence it is important that we call CacheRecordRmvEventsForQuestion before
+				// LocalRecordRmvEventsForQuestion (which decrements LOAddressAnswers)
+
+  				if (q->SuppressQuery)
+  					{
+  					// Previously it was not suppressed, Generate RMV events for the ADDs that we might have delivered before
+ 					// followed by a negative cache response. Temporarily turn off suppression so that
+ 					// AnswerCurrentQuestionWithResourceRecord can answer the question
+ 					q->SuppressQuery = mDNSfalse;
+ 					if (!CacheRecordRmvEventsForQuestion(m, q)) { LogInfo("CheckSuppressUnusableQuestions: Question deleted while delivering RMV events"); continue; }
+ 					q->SuppressQuery = mDNStrue;
+  					}
+
+				// SuppressUnusable does not affect questions that are answered from the local records (/etc/hosts)
+				// and SuppressQuery status does not mean anything for these questions. As we are going to stop the
+				// question below, we need to deliver the RMV events so that the ADDs that will be delivered during
+				// the restart will not be a duplicate ADD
+ 				if (!LocalRecordRmvEventsForQuestion(m, q)) { LogInfo("CheckSuppressUnusableQuestions: Question deleted while delivering RMV events"); continue; }
+
+				// There are two cases here.
+				//
+				// 1. Previously it was suppressed and now it is not suppressed, restart the question so
+				// that it will start as a new question. Note that we can't just call ActivateUnicastQuery
+				// because when we get the response, if we had entries in the cache already, it will not answer
+				// this question if the cache entry did not change. Hence, we need to restart
+				// the query so that it can be answered from the cache.
+				//
+				// 2. Previously it was not suppressed and now it is suppressed. We need to restart the questions
+				// so that we redo the duplicate checks in mDNS_StartQuery_internal. A SuppressUnusable question
+				// is a duplicate of non-SuppressUnusable question if it is not suppressed (SuppressQuery is false).
+				// A SuppressUnusable question is not a duplicate of non-SuppressUnusable question if it is suppressed
+				// (SuppressQuery is true). The reason for this is that when a question is suppressed, we want an
+				// immediate response and not want to be blocked behind a question that is querying DNS servers. When 
+				// the question is not suppressed, we don't want two active questions sending packets on the wire.
+				// This affects both efficiency and also the current design where there is only one active question
+				// pointed to from a cache entry.
+				//
+				// We restart queries in a two step process by first calling stop and build a temporary list which we
+				// will restart at the end. The main reason for the two step process is to handle duplicate questions.
+				// If there are duplicate questions, calling stop inherits the values from another question on the list (which
+				// will soon become the real question) including q->ThisQInterval which might be zero if it was
+				// suppressed before. At the end when we have restarted all questions, none of them is active as each
+				// inherits from one another and we need to reactivate one of the questions here which is a little hacky.
+				//
+				// It is much cleaner and less error prone to build a list of questions and restart at the end.
+
+				LogInfo("CheckSuppressUnusableQuestions: Stop question %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype));
+				mDNS_StopQuery_internal(m, q);
+				q->next = restart;
+				restart = q;
+				}
+			}
+		}
+	while (restart)
+		{
+		q = restart;
+		restart = restart->next;
+		q->next = mDNSNULL;
+		LogInfo("CheckSuppressUnusableQuestions: Start question %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype));
+		mDNS_StartQuery_internal(m, q);
+		}
+	}
+
+mDNSexport mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const question)
+	{
+	if (question->Target.type && !ValidQuestionTarget(question))
+		{
+		LogMsg("mDNS_StartQuery_internal: Warning! Target.type = %ld port = %u (Client forgot to initialize before calling mDNS_StartQuery? for question %##s)",
+			question->Target.type, mDNSVal16(question->TargetPort), question->qname.c);
+		question->Target.type = mDNSAddrType_None;
+		}
+
+	if (!question->Target.type) question->TargetPort = zeroIPPort;	// If no question->Target specified clear TargetPort
+
+	question->TargetQID =
+#ifndef UNICAST_DISABLED
+		(question->Target.type || Question_uDNS(question)) ? mDNS_NewMessageID(m) :
+#endif // UNICAST_DISABLED
+		zeroID;
+
+	debugf("mDNS_StartQuery: %##s (%s)", question->qname.c, DNSTypeName(question->qtype));
+
+	if (m->rrcache_size == 0)	// Can't do queries if we have no cache space allocated
+		return(mStatus_NoCache);
+	else
+		{
+		int i;
+		DNSQuestion **q;
+		
+		if (!ValidateDomainName(&question->qname))
+			{
+			LogMsg("Attempt to start query with invalid qname %##s (%s)", question->qname.c, DNSTypeName(question->qtype));
+			return(mStatus_Invalid);
+			}
+
+		// Note: It important that new questions are appended at the *end* of the list, not prepended at the start
+		q = &m->Questions;
+		if (question->InterfaceID == mDNSInterface_LocalOnly || question->InterfaceID == mDNSInterface_P2P) q = &m->LocalOnlyQuestions;
+		while (*q && *q != question) q=&(*q)->next;
+
+		if (*q)
+			{
+			LogMsg("Error! Tried to add a question %##s (%s) %p that's already in the active list",
+				question->qname.c, DNSTypeName(question->qtype), question);
+			return(mStatus_AlreadyRegistered);
+			}
+
+		*q = question;
+
+		// If this question is referencing a specific interface, verify it exists
+		if (question->InterfaceID && question->InterfaceID != mDNSInterface_LocalOnly && question->InterfaceID != mDNSInterface_Unicast && question->InterfaceID != mDNSInterface_P2P)
+			{
+			NetworkInterfaceInfo *intf = FirstInterfaceForID(m, question->InterfaceID);
+			if (!intf)
+				LogMsg("Note: InterfaceID %p for question %##s (%s) not currently found in active interface list",
+					question->InterfaceID, question->qname.c, DNSTypeName(question->qtype));
+			}
+
+		// Note: In the case where we already have the answer to this question in our cache, that may be all the client
+		// wanted, and they may immediately cancel their question. In this case, sending an actual query on the wire would
+		// be a waste. For that reason, we schedule our first query to go out in half a second (InitialQuestionInterval).
+		// If AnswerNewQuestion() finds that we have *no* relevant answers currently in our cache, then it will accelerate
+		// that to go out immediately.
+		question->next              = mDNSNULL;
+		question->qnamehash         = DomainNameHashValue(&question->qname);	// MUST do this before FindDuplicateQuestion()
+		question->DelayAnswering    = CheckForSoonToExpireRecords(m, &question->qname, question->qnamehash, HashSlot(&question->qname));
+		question->LastQTime         = m->timenow;
+		question->ThisQInterval     = InitialQuestionInterval;					// MUST be > zero for an active question
+		question->ExpectUnicastResp = 0;
+		question->LastAnswerPktNum  = m->PktNum;
+		question->RecentAnswerPkts  = 0;
+		question->CurrentAnswers    = 0;
+		question->LargeAnswers      = 0;
+		question->UniqueAnswers     = 0;
+		question->LOAddressAnswers  = 0;
+		question->FlappingInterface1 = mDNSNULL;
+		question->FlappingInterface2 = mDNSNULL;
+		// Must do AuthInfo and SuppressQuery before calling FindDuplicateQuestion()
+		question->AuthInfo          = GetAuthInfoForQuestion(m, question);
+		if (question->SuppressUnusable)
+			question->SuppressQuery = ShouldSuppressQuery(m, &question->qname, question->qtype, question->InterfaceID);
+		else
+			question->SuppressQuery = 0;
+		question->DuplicateOf       = FindDuplicateQuestion(m, question);
+		question->NextInDQList      = mDNSNULL;
+		question->SendQNow          = mDNSNULL;
+		question->SendOnAll         = mDNSfalse;
+		question->RequestUnicast    = 0;
+		question->LastQTxTime       = m->timenow;
+		question->CNAMEReferrals    = 0;
+
+		// We'll create our question->LocalSocket on demand, if needed.
+		// We won't need one for duplicate questions, or from questions answered immediately out of the cache.
+		// We also don't need one for LLQs because (when we're using NAT) we want them all to share a single
+		// NAT mapping for receiving inbound add/remove events.
+		question->LocalSocket       = mDNSNULL;
+		question->deliverAddEvents  = mDNSfalse;
+		question->qDNSServer        = mDNSNULL;
+		question->unansweredQueries = 0;
+		question->nta               = mDNSNULL;
+		question->servAddr          = zeroAddr;
+		question->servPort          = zeroIPPort;
+		question->tcp               = mDNSNULL;
+		question->NoAnswer          = NoAnswer_Normal;
+
+		question->state             = LLQ_InitialRequest;
+		question->ReqLease          = 0;
+		question->expire            = 0;
+		question->ntries            = 0;
+		question->id                = zeroOpaque64;
+		question->validDNSServers   = zeroOpaque64;
+		question->triedAllServersOnce = 0;
+		question->noServerResponse  = 0;
+		question->StopTime = 0;
+		if (question->WakeOnResolve)
+			{
+			question->WakeOnResolveCount = InitialWakeOnResolveCount;
+			mDNS_PurgeBeforeResolve(m, question);
+			}
+		else
+			question->WakeOnResolveCount = 0;
+
+		if (question->DuplicateOf) question->AuthInfo = question->DuplicateOf->AuthInfo;
+
+		for (i=0; i<DupSuppressInfoSize; i++)
+			question->DupSuppress[i].InterfaceID = mDNSNULL;
+
+		debugf("mDNS_StartQuery: Question %##s (%s) Interface %p Now %d Send in %d Answer in %d (%p) %s (%p)",
+			question->qname.c, DNSTypeName(question->qtype), question->InterfaceID, m->timenow,
+			NextQSendTime(question) - m->timenow,
+			question->DelayAnswering ? question->DelayAnswering - m->timenow : 0,
+			question, question->DuplicateOf ? "duplicate of" : "not duplicate", question->DuplicateOf);
+
+		if (question->DelayAnswering)
+			LogInfo("mDNS_StartQuery_internal: Delaying answering for %d ticks while cache stabilizes for %##s (%s)",
+				question->DelayAnswering - m->timenow, question->qname.c, DNSTypeName(question->qtype));
+
+		if (question->InterfaceID == mDNSInterface_LocalOnly || question->InterfaceID == mDNSInterface_P2P)
+			{
+			if (!m->NewLocalOnlyQuestions) m->NewLocalOnlyQuestions = question;
+			}
+		else
+			{
+			if (!m->NewQuestions) m->NewQuestions = question;
+
+			// If the question's id is non-zero, then it's Wide Area
+			// MUST NOT do this Wide Area setup until near the end of
+			// mDNS_StartQuery_internal -- this code may itself issue queries (e.g. SOA,
+			// NS, etc.) and if we haven't finished setting up our own question and setting
+			// m->NewQuestions if necessary then we could end up recursively re-entering
+			// this routine with the question list data structures in an inconsistent state.
+			if (!mDNSOpaque16IsZero(question->TargetQID))
+				{
+				// Duplicate questions should have the same DNSServers so that when we find
+				// a matching resource record, all of them get the answers. Calling GetServerForQuestion
+				// for the duplicate question may get a different DNS server from the original question
+				mDNSu32 timeout = SetValidDNSServers(m, question);
+				// We set the timeout whenever mDNS_StartQuery_internal is called. This means if we have
+				// a networking change/search domain change that calls this function again we keep
+				// reinitializing the timeout value which means it may never timeout. If this becomes
+				// a common case in the future, we can easily fix this by adding extra state that
+				// indicates that we have already set the StopTime.
+				if (question->TimeoutQuestion)
+					question->StopTime = NonZeroTime(m->timenow + timeout * mDNSPlatformOneSecond);
+				if (question->DuplicateOf)
+					{
+					question->validDNSServers = question->DuplicateOf->validDNSServers;
+					question->qDNSServer = question->DuplicateOf->qDNSServer;
+					LogInfo("mDNS_StartQuery_internal: Duplicate question %p (%p) %##s (%s), Timeout %d, DNS Server %#a:%d",
+						question, question->DuplicateOf, question->qname.c, DNSTypeName(question->qtype), timeout,
+						question->qDNSServer ? &question->qDNSServer->addr : mDNSNULL,
+					    mDNSVal16(question->qDNSServer ? question->qDNSServer->port : zeroIPPort));
+					}
+				else
+					{
+					question->qDNSServer = GetServerForQuestion(m, question);
+					LogInfo("mDNS_StartQuery_internal: question %p %##s (%s) Timeout %d, DNS Server %#a:%d",
+						question, question->qname.c, DNSTypeName(question->qtype), timeout,
+						question->qDNSServer ? &question->qDNSServer->addr : mDNSNULL,
+					    mDNSVal16(question->qDNSServer ? question->qDNSServer->port : zeroIPPort));
+					}
+				ActivateUnicastQuery(m, question, mDNSfalse);
+
+				// If long-lived query, and we don't have our NAT mapping active, start it now
+				if (question->LongLived && !m->LLQNAT.clientContext)
+					{
+					m->LLQNAT.Protocol       = NATOp_MapUDP;
+					m->LLQNAT.IntPort        = m->UnicastPort4;
+					m->LLQNAT.RequestedPort  = m->UnicastPort4;
+					m->LLQNAT.clientCallback = LLQNATCallback;
+					m->LLQNAT.clientContext  = (void*)1; // Means LLQ NAT Traversal is active
+					mDNS_StartNATOperation_internal(m, &m->LLQNAT);
+					}
+					
+#if APPLE_OSX_mDNSResponder
+				if (question->LongLived)
+					UpdateAutoTunnelDomainStatuses(m);
+#endif
+					
+				}
+			else
+				{
+				if (question->TimeoutQuestion)
+					question->StopTime = NonZeroTime(m->timenow + GetTimeoutForMcastQuestion(m, question) * mDNSPlatformOneSecond);
+				}
+			if (question->StopTime) SetNextQueryStopTime(m, question);
+			SetNextQueryTime(m,question);
+			}
+
+		return(mStatus_NoError);
+		}
+	}
+
+// CancelGetZoneData is an internal routine (i.e. must be called with the lock already held)
+mDNSexport void CancelGetZoneData(mDNS *const m, ZoneData *nta)
+	{
+	debugf("CancelGetZoneData %##s (%s)", nta->question.qname.c, DNSTypeName(nta->question.qtype));
+	// This function may be called anytime to free the zone information.The question may or may not have stopped.
+	// If it was already stopped, mDNS_StopQuery_internal would have set q->ThisQInterval to -1 and should not
+	// call it again
+	if (nta->question.ThisQInterval != -1)
+		{
+		mDNS_StopQuery_internal(m, &nta->question);
+		if (nta->question.ThisQInterval != -1)
+			LogMsg("CancelGetZoneData: Question %##s (%s) ThisQInterval %d not -1", nta->question.qname.c, DNSTypeName(nta->question.qtype), nta->question.ThisQInterval);
+		}
+	mDNSPlatformMemFree(nta);
+	}
+
+mDNSexport mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const question)
+	{
+	const mDNSu32 slot = HashSlot(&question->qname);
+	CacheGroup *cg = CacheGroupForName(m, slot, question->qnamehash, &question->qname);
+	CacheRecord *rr;
+	DNSQuestion **qp = &m->Questions;
+	
+	//LogInfo("mDNS_StopQuery_internal %##s (%s)", question->qname.c, DNSTypeName(question->qtype));
+
+	if (question->InterfaceID == mDNSInterface_LocalOnly || question->InterfaceID == mDNSInterface_P2P) qp = &m->LocalOnlyQuestions;
+	while (*qp && *qp != question) qp=&(*qp)->next;
+	if (*qp) *qp = (*qp)->next;
+	else
+		{
+#if !ForceAlerts
+		if (question->ThisQInterval >= 0)	// Only log error message if the query was supposed to be active
+#endif
+			LogMsg("mDNS_StopQuery_internal: Question %##s (%s) not found in active list",
+				question->qname.c, DNSTypeName(question->qtype));
+#if ForceAlerts
+		*(long*)0 = 0;
+#endif
+		return(mStatus_BadReferenceErr);
+		}
+
+	// Take care to cut question from list *before* calling UpdateQuestionDuplicates
+	UpdateQuestionDuplicates(m, question);
+	// But don't trash ThisQInterval until afterwards.
+	question->ThisQInterval = -1;
+
+	// If there are any cache records referencing this as their active question, then see if there is any
+	// other question that is also referencing them, else their CRActiveQuestion needs to get set to NULL.
+	for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
+		{
+		if (rr->CRActiveQuestion == question)
+			{
+			DNSQuestion *q;
+			// Checking for ActiveQuestion filters questions that are suppressed also
+			// as suppressed questions are not active
+			for (q = m->Questions; q; q=q->next)		// Scan our list of questions
+				if (ActiveQuestion(q) && ResourceRecordAnswersQuestion(&rr->resrec, q))
+					break;
+			if (q)
+				debugf("mDNS_StopQuery_internal: Updating CRActiveQuestion to %p for cache record %s, Original question CurrentAnswers %d, new question "
+					"CurrentAnswers %d, SuppressQuery %d", q, CRDisplayString(m,rr), question->CurrentAnswers, q->CurrentAnswers, q->SuppressQuery);
+			rr->CRActiveQuestion = q;		// Question used to be active; new value may or may not be null
+			if (!q) m->rrcache_active--;	// If no longer active, decrement rrcache_active count
+			}
+		}
+
+	// If we just deleted the question that CacheRecordAdd() or CacheRecordRmv() is about to look at,
+	// bump its pointer forward one question.
+	if (m->CurrentQuestion == question)
+		{
+		debugf("mDNS_StopQuery_internal: Just deleted the currently active question: %##s (%s)",
+			question->qname.c, DNSTypeName(question->qtype));
+		m->CurrentQuestion = question->next;
+		}
+
+	if (m->NewQuestions == question)
+		{
+		debugf("mDNS_StopQuery_internal: Just deleted a new question that wasn't even answered yet: %##s (%s)",
+			question->qname.c, DNSTypeName(question->qtype));
+		m->NewQuestions = question->next;
+		}
+
+	if (m->NewLocalOnlyQuestions == question) m->NewLocalOnlyQuestions = question->next;
+
+	if (m->RestartQuestion == question)
+		{
+		LogMsg("mDNS_StopQuery_internal: Just deleted the current restart question: %##s (%s)",
+			question->qname.c, DNSTypeName(question->qtype));
+		m->RestartQuestion = question->next;
+		}
+
+	// Take care not to trash question->next until *after* we've updated m->CurrentQuestion and m->NewQuestions
+	question->next = mDNSNULL;
+
+	// LogMsg("mDNS_StopQuery_internal: Question %##s (%s) removed", question->qname.c, DNSTypeName(question->qtype));
+
+	// And finally, cancel any associated GetZoneData operation that's still running.
+	// Must not do this until last, because there's a good chance the GetZoneData question is the next in the list,
+	// so if we delete it earlier in this routine, we could find that our "question->next" pointer above is already
+	// invalid before we even use it. By making sure that we update m->CurrentQuestion and m->NewQuestions if necessary
+	// *first*, then they're all ready to be updated a second time if necessary when we cancel our GetZoneData query.
+	if (question->tcp) { DisposeTCPConn(question->tcp); question->tcp = mDNSNULL; }
+	if (question->LocalSocket) { mDNSPlatformUDPClose(question->LocalSocket); question->LocalSocket = mDNSNULL; }
+	if (!mDNSOpaque16IsZero(question->TargetQID) && question->LongLived)
+		{
+		// Scan our list to see if any more wide-area LLQs remain. If not, stop our NAT Traversal.
+		DNSQuestion *q;
+		for (q = m->Questions; q; q=q->next)
+			if (!mDNSOpaque16IsZero(q->TargetQID) && q->LongLived) break;
+		if (!q)
+			{
+			if (!m->LLQNAT.clientContext)		// Should never happen, but just in case...
+				LogMsg("mDNS_StopQuery ERROR LLQNAT.clientContext NULL");
+			else
+				{
+				LogInfo("Stopping LLQNAT");
+				mDNS_StopNATOperation_internal(m, &m->LLQNAT);
+				m->LLQNAT.clientContext = mDNSNULL; // Means LLQ NAT Traversal not running
+				}
+			}
+
+		// If necessary, tell server it can delete this LLQ state
+		if (question->state == LLQ_Established)
+			{
+			question->ReqLease = 0;
+			sendLLQRefresh(m, question);
+			// If we need need to make a TCP connection to cancel the LLQ, that's going to take a little while.
+			// We clear the tcp->question backpointer so that when the TCP connection completes, it doesn't
+			// crash trying to access our cancelled question, but we don't cancel the TCP operation itself --
+			// we let that run out its natural course and complete asynchronously.
+			if (question->tcp)
+				{
+				question->tcp->question = mDNSNULL;
+				question->tcp           = mDNSNULL;
+				}
+			}
+#if APPLE_OSX_mDNSResponder
+		UpdateAutoTunnelDomainStatuses(m);
+#endif
+		}
+	// wait until we send the refresh above which needs the nta
+	if (question->nta) { CancelGetZoneData(m, question->nta); question->nta = mDNSNULL; }
+
+	return(mStatus_NoError);
+	}
+
+mDNSexport mStatus mDNS_StartQuery(mDNS *const m, DNSQuestion *const question)
+	{
+	mStatus status;
+	mDNS_Lock(m);
+	status = mDNS_StartQuery_internal(m, question);
+	mDNS_Unlock(m);
+	return(status);
+	}
+
+mDNSexport mStatus mDNS_StopQuery(mDNS *const m, DNSQuestion *const question)
+	{
+	mStatus status;
+	mDNS_Lock(m);
+	status = mDNS_StopQuery_internal(m, question);
+	mDNS_Unlock(m);
+	return(status);
+	}
+
+// Note that mDNS_StopQueryWithRemoves() does not currently implement the full generality of the other APIs
+// Specifically, question callbacks invoked as a result of this call cannot themselves make API calls.
+// We invoke the callback without using mDNS_DropLockBeforeCallback/mDNS_ReclaimLockAfterCallback
+// specifically to catch and report if the client callback does try to make API calls
+mDNSexport mStatus mDNS_StopQueryWithRemoves(mDNS *const m, DNSQuestion *const question)
+	{
+	mStatus status;
+	DNSQuestion *qq;
+	mDNS_Lock(m);
+
+	// Check if question is new -- don't want to give remove events for a question we haven't even answered yet
+	for (qq = m->NewQuestions; qq; qq=qq->next) if (qq == question) break;
+
+	status = mDNS_StopQuery_internal(m, question);
+	if (status == mStatus_NoError && !qq)
+		{
+		const CacheRecord *rr;
+		const mDNSu32 slot = HashSlot(&question->qname);
+		CacheGroup *const cg = CacheGroupForName(m, slot, question->qnamehash, &question->qname);
+		LogInfo("Generating terminal removes for %##s (%s)", question->qname.c, DNSTypeName(question->qtype));
+		for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
+			if (rr->resrec.RecordType != kDNSRecordTypePacketNegative && SameNameRecordAnswersQuestion(&rr->resrec, question))
+				{
+				// Don't use mDNS_DropLockBeforeCallback() here, since we don't allow API calls
+				if (question->QuestionCallback)
+					question->QuestionCallback(m, question, &rr->resrec, mDNSfalse);
+				}
+		}
+	mDNS_Unlock(m);
+	return(status);
+	}
+
+mDNSexport mStatus mDNS_Reconfirm(mDNS *const m, CacheRecord *const cr)
+	{
+	mStatus status;
+	mDNS_Lock(m);
+	status = mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForNoAnswer);
+	if (status == mStatus_NoError) ReconfirmAntecedents(m, cr->resrec.name, cr->resrec.namehash, 0);
+	mDNS_Unlock(m);
+	return(status);
+	}
+
+mDNSexport mStatus mDNS_ReconfirmByValue(mDNS *const m, ResourceRecord *const rr)
+	{
+	mStatus status = mStatus_BadReferenceErr;
+	CacheRecord *cr;
+	mDNS_Lock(m);
+	cr = FindIdenticalRecordInCache(m, rr);
+	debugf("mDNS_ReconfirmByValue: %p %s", cr, RRDisplayString(m, rr));
+	if (cr) status = mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForNoAnswer);
+	if (status == mStatus_NoError) ReconfirmAntecedents(m, cr->resrec.name, cr->resrec.namehash, 0);
+	mDNS_Unlock(m);
+	return(status);
+	}
+
+mDNSlocal mStatus mDNS_StartBrowse_internal(mDNS *const m, DNSQuestion *const question,
+	const domainname *const srv, const domainname *const domain,
+	const mDNSInterfaceID InterfaceID, mDNSBool ForceMCast, mDNSQuestionCallback *Callback, void *Context)
+	{
+	question->InterfaceID      = InterfaceID;
+	question->Target           = zeroAddr;
+	question->qtype            = kDNSType_PTR;
+	question->qclass           = kDNSClass_IN;
+	question->LongLived        = mDNStrue;
+	question->ExpectUnique     = mDNSfalse;
+	question->ForceMCast       = ForceMCast;
+	question->ReturnIntermed   = mDNSfalse;
+	question->SuppressUnusable = mDNSfalse;
+	question->SearchListIndex  = 0;
+	question->AppendSearchDomains = 0;
+	question->RetryWithSearchDomains = mDNSfalse;
+	question->TimeoutQuestion  = 0;
+	question->WakeOnResolve    = 0;
+	question->qnameOrig        = mDNSNULL;
+	question->QuestionCallback = Callback;
+	question->QuestionContext  = Context;
+	if (!ConstructServiceName(&question->qname, mDNSNULL, srv, domain)) return(mStatus_BadParamErr);
+
+	return(mDNS_StartQuery_internal(m, question));
+	}
+
+mDNSexport mStatus mDNS_StartBrowse(mDNS *const m, DNSQuestion *const question,
+	const domainname *const srv, const domainname *const domain,
+	const mDNSInterfaceID InterfaceID, mDNSBool ForceMCast, mDNSQuestionCallback *Callback, void *Context)
+	{
+	mStatus status;
+	mDNS_Lock(m);
+	status = mDNS_StartBrowse_internal(m, question, srv, domain, InterfaceID, ForceMCast, Callback, Context);
+	mDNS_Unlock(m);
+	return(status);
+	}
+
+mDNSlocal mDNSBool MachineHasActiveIPv6(mDNS *const m)
+	{
+	NetworkInterfaceInfo *intf;
+	for (intf = m->HostInterfaces; intf; intf = intf->next)
+	if (intf->ip.type == mDNSAddrType_IPv6) return(mDNStrue);
+	return(mDNSfalse);
+	}
+
+mDNSlocal void FoundServiceInfoSRV(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
+	{
+	ServiceInfoQuery *query = (ServiceInfoQuery *)question->QuestionContext;
+	mDNSBool PortChanged = !mDNSSameIPPort(query->info->port, answer->rdata->u.srv.port);
+	if (!AddRecord) return;
+	if (answer->rrtype != kDNSType_SRV) return;
+
+	query->info->port = answer->rdata->u.srv.port;
+
+	// If this is our first answer, then set the GotSRV flag and start the address query
+	if (!query->GotSRV)
+		{
+		query->GotSRV             = mDNStrue;
+		query->qAv4.InterfaceID   = answer->InterfaceID;
+		AssignDomainName(&query->qAv4.qname, &answer->rdata->u.srv.target);
+		query->qAv6.InterfaceID   = answer->InterfaceID;
+		AssignDomainName(&query->qAv6.qname, &answer->rdata->u.srv.target);
+		mDNS_StartQuery(m, &query->qAv4);
+		// Only do the AAAA query if this machine actually has IPv6 active
+		if (MachineHasActiveIPv6(m)) mDNS_StartQuery(m, &query->qAv6);
+		}
+	// If this is not our first answer, only re-issue the address query if the target host name has changed
+	else if ((query->qAv4.InterfaceID != query->qSRV.InterfaceID && query->qAv4.InterfaceID != answer->InterfaceID) ||
+		!SameDomainName(&query->qAv4.qname, &answer->rdata->u.srv.target))
+		{
+		mDNS_StopQuery(m, &query->qAv4);
+		if (query->qAv6.ThisQInterval >= 0) mDNS_StopQuery(m, &query->qAv6);
+		if (SameDomainName(&query->qAv4.qname, &answer->rdata->u.srv.target) && !PortChanged)
+			{
+			// If we get here, it means:
+			// 1. This is not our first SRV answer
+			// 2. The interface ID is different, but the target host and port are the same
+			// This implies that we're seeing the exact same SRV record on more than one interface, so we should
+			// make our address queries at least as broad as the original SRV query so that we catch all the answers.
+			query->qAv4.InterfaceID = query->qSRV.InterfaceID;	// Will be mDNSInterface_Any, or a specific interface
+			query->qAv6.InterfaceID = query->qSRV.InterfaceID;
+			}
+		else
+			{
+			query->qAv4.InterfaceID   = answer->InterfaceID;
+			AssignDomainName(&query->qAv4.qname, &answer->rdata->u.srv.target);
+			query->qAv6.InterfaceID   = answer->InterfaceID;
+			AssignDomainName(&query->qAv6.qname, &answer->rdata->u.srv.target);
+			}
+		debugf("FoundServiceInfoSRV: Restarting address queries for %##s (%s)", query->qAv4.qname.c, DNSTypeName(query->qAv4.qtype));
+		mDNS_StartQuery(m, &query->qAv4);
+		// Only do the AAAA query if this machine actually has IPv6 active
+		if (MachineHasActiveIPv6(m)) mDNS_StartQuery(m, &query->qAv6);
+		}
+	else if (query->ServiceInfoQueryCallback && query->GotADD && query->GotTXT && PortChanged)
+		{
+		if (++query->Answers >= 100)
+			debugf("**** WARNING **** Have given %lu answers for %##s (SRV) %##s %u",
+				query->Answers, query->qSRV.qname.c, answer->rdata->u.srv.target.c,
+				mDNSVal16(answer->rdata->u.srv.port));
+		query->ServiceInfoQueryCallback(m, query);
+		}
+	// CAUTION: MUST NOT do anything more with query after calling query->Callback(), because the client's
+	// callback function is allowed to do anything, including deleting this query and freeing its memory.
+	}
+
+mDNSlocal void FoundServiceInfoTXT(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
+	{
+	ServiceInfoQuery *query = (ServiceInfoQuery *)question->QuestionContext;
+	if (!AddRecord) return;
+	if (answer->rrtype != kDNSType_TXT) return;
+	if (answer->rdlength > sizeof(query->info->TXTinfo)) return;
+
+	query->GotTXT       = mDNStrue;
+	query->info->TXTlen = answer->rdlength;
+	query->info->TXTinfo[0] = 0;		// In case answer->rdlength is zero
+	mDNSPlatformMemCopy(query->info->TXTinfo, answer->rdata->u.txt.c, answer->rdlength);
+
+	verbosedebugf("FoundServiceInfoTXT: %##s GotADD=%d", query->info->name.c, query->GotADD);
+
+	// CAUTION: MUST NOT do anything more with query after calling query->Callback(), because the client's
+	// callback function is allowed to do anything, including deleting this query and freeing its memory.
+	if (query->ServiceInfoQueryCallback && query->GotADD)
+		{
+		if (++query->Answers >= 100)
+			debugf("**** WARNING **** have given %lu answers for %##s (TXT) %#s...",
+				query->Answers, query->qSRV.qname.c, answer->rdata->u.txt.c);
+		query->ServiceInfoQueryCallback(m, query);
+		}
+	}
+
+mDNSlocal void FoundServiceInfo(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
+	{
+	ServiceInfoQuery *query = (ServiceInfoQuery *)question->QuestionContext;
+	//LogInfo("FoundServiceInfo %d %s", AddRecord, RRDisplayString(m, answer));
+	if (!AddRecord) return;
+	
+	if (answer->rrtype == kDNSType_A)
+		{
+		query->info->ip.type = mDNSAddrType_IPv4;
+		query->info->ip.ip.v4 = answer->rdata->u.ipv4;
+		}
+	else if (answer->rrtype == kDNSType_AAAA)
+		{
+		query->info->ip.type = mDNSAddrType_IPv6;
+		query->info->ip.ip.v6 = answer->rdata->u.ipv6;
+		}
+	else
+		{
+		debugf("FoundServiceInfo: answer %##s type %d (%s) unexpected", answer->name->c, answer->rrtype, DNSTypeName(answer->rrtype));
+		return;
+		}
+
+	query->GotADD = mDNStrue;
+	query->info->InterfaceID = answer->InterfaceID;
+
+	verbosedebugf("FoundServiceInfo v%ld: %##s GotTXT=%d", query->info->ip.type, query->info->name.c, query->GotTXT);
+
+	// CAUTION: MUST NOT do anything more with query after calling query->Callback(), because the client's
+	// callback function is allowed to do anything, including deleting this query and freeing its memory.
+	if (query->ServiceInfoQueryCallback && query->GotTXT)
+		{
+		if (++query->Answers >= 100)
+			debugf(answer->rrtype == kDNSType_A ?
+				"**** WARNING **** have given %lu answers for %##s (A) %.4a" :
+				"**** WARNING **** have given %lu answers for %##s (AAAA) %.16a",
+				query->Answers, query->qSRV.qname.c, &answer->rdata->u.data);
+		query->ServiceInfoQueryCallback(m, query);
+		}
+	}
+
+// On entry, the client must have set the name and InterfaceID fields of the ServiceInfo structure
+// If the query is not interface-specific, then InterfaceID may be zero
+// Each time the Callback is invoked, the remainder of the fields will have been filled in
+// In addition, InterfaceID will be updated to give the interface identifier corresponding to that response
+mDNSexport mStatus mDNS_StartResolveService(mDNS *const m,
+	ServiceInfoQuery *query, ServiceInfo *info, mDNSServiceInfoQueryCallback *Callback, void *Context)
+	{
+	mStatus status;
+	mDNS_Lock(m);
+
+	query->qSRV.ThisQInterval       = -1;		// So that mDNS_StopResolveService() knows whether to cancel this question
+	query->qSRV.InterfaceID         = info->InterfaceID;
+	query->qSRV.Target              = zeroAddr;
+	AssignDomainName(&query->qSRV.qname, &info->name);
+	query->qSRV.qtype               = kDNSType_SRV;
+	query->qSRV.qclass              = kDNSClass_IN;
+	query->qSRV.LongLived           = mDNSfalse;
+	query->qSRV.ExpectUnique        = mDNStrue;
+	query->qSRV.ForceMCast          = mDNSfalse;
+	query->qSRV.ReturnIntermed      = mDNSfalse;
+	query->qSRV.SuppressUnusable    = mDNSfalse;
+	query->qSRV.SearchListIndex     = 0;
+	query->qSRV.AppendSearchDomains = 0;
+	query->qSRV.RetryWithSearchDomains = mDNSfalse;
+	query->qSRV.TimeoutQuestion     = 0;
+	query->qSRV.WakeOnResolve       = 0;
+	query->qSRV.qnameOrig           = mDNSNULL;
+	query->qSRV.QuestionCallback    = FoundServiceInfoSRV;
+	query->qSRV.QuestionContext     = query;
+
+	query->qTXT.ThisQInterval       = -1;		// So that mDNS_StopResolveService() knows whether to cancel this question
+	query->qTXT.InterfaceID         = info->InterfaceID;
+	query->qTXT.Target              = zeroAddr;
+	AssignDomainName(&query->qTXT.qname, &info->name);
+	query->qTXT.qtype               = kDNSType_TXT;
+	query->qTXT.qclass              = kDNSClass_IN;
+	query->qTXT.LongLived           = mDNSfalse;
+	query->qTXT.ExpectUnique        = mDNStrue;
+	query->qTXT.ForceMCast          = mDNSfalse;
+	query->qTXT.ReturnIntermed      = mDNSfalse;
+	query->qTXT.SuppressUnusable    = mDNSfalse;
+	query->qTXT.SearchListIndex     = 0;
+	query->qTXT.AppendSearchDomains = 0;
+	query->qTXT.RetryWithSearchDomains = mDNSfalse;
+	query->qTXT.TimeoutQuestion     = 0;
+	query->qTXT.WakeOnResolve       = 0;
+	query->qTXT.qnameOrig           = mDNSNULL;
+	query->qTXT.QuestionCallback    = FoundServiceInfoTXT;
+	query->qTXT.QuestionContext     = query;
+
+	query->qAv4.ThisQInterval       = -1;		// So that mDNS_StopResolveService() knows whether to cancel this question
+	query->qAv4.InterfaceID         = info->InterfaceID;
+	query->qAv4.Target              = zeroAddr;
+	query->qAv4.qname.c[0]          = 0;
+	query->qAv4.qtype               = kDNSType_A;
+	query->qAv4.qclass              = kDNSClass_IN;
+	query->qAv4.LongLived           = mDNSfalse;
+	query->qAv4.ExpectUnique        = mDNStrue;
+	query->qAv4.ForceMCast          = mDNSfalse;
+	query->qAv4.ReturnIntermed      = mDNSfalse;
+	query->qAv4.SuppressUnusable    = mDNSfalse;
+	query->qAv4.SearchListIndex     = 0;
+	query->qAv4.AppendSearchDomains = 0;
+	query->qAv4.RetryWithSearchDomains = mDNSfalse;
+	query->qAv4.TimeoutQuestion     = 0;
+	query->qAv4.WakeOnResolve       = 0;
+	query->qAv4.qnameOrig           = mDNSNULL;
+	query->qAv4.QuestionCallback    = FoundServiceInfo;
+	query->qAv4.QuestionContext     = query;
+
+	query->qAv6.ThisQInterval       = -1;		// So that mDNS_StopResolveService() knows whether to cancel this question
+	query->qAv6.InterfaceID         = info->InterfaceID;
+	query->qAv6.Target              = zeroAddr;
+	query->qAv6.qname.c[0]          = 0;
+	query->qAv6.qtype               = kDNSType_AAAA;
+	query->qAv6.qclass              = kDNSClass_IN;
+	query->qAv6.LongLived           = mDNSfalse;
+	query->qAv6.ExpectUnique        = mDNStrue;
+	query->qAv6.ForceMCast          = mDNSfalse;
+	query->qAv6.ReturnIntermed      = mDNSfalse;
+	query->qAv6.SuppressUnusable    = mDNSfalse;
+	query->qAv6.SearchListIndex     = 0;
+	query->qAv6.AppendSearchDomains = 0;
+	query->qAv6.RetryWithSearchDomains = mDNSfalse;
+	query->qAv6.TimeoutQuestion     = 0;
+	query->qAv6.WakeOnResolve       = 0;
+	query->qAv6.qnameOrig           = mDNSNULL;
+	query->qAv6.QuestionCallback    = FoundServiceInfo;
+	query->qAv6.QuestionContext     = query;
+
+	query->GotSRV                   = mDNSfalse;
+	query->GotTXT                   = mDNSfalse;
+	query->GotADD                   = mDNSfalse;
+	query->Answers                  = 0;
+
+	query->info                     = info;
+	query->ServiceInfoQueryCallback = Callback;
+	query->ServiceInfoQueryContext  = Context;
+
+//	info->name      = Must already be set up by client
+//	info->interface = Must already be set up by client
+	info->ip        = zeroAddr;
+	info->port      = zeroIPPort;
+	info->TXTlen    = 0;
+
+	// We use mDNS_StartQuery_internal here because we're already holding the lock
+	status = mDNS_StartQuery_internal(m, &query->qSRV);
+	if (status == mStatus_NoError) status = mDNS_StartQuery_internal(m, &query->qTXT);
+	if (status != mStatus_NoError) mDNS_StopResolveService(m, query);
+
+	mDNS_Unlock(m);
+	return(status);
+	}
+
+mDNSexport void    mDNS_StopResolveService (mDNS *const m, ServiceInfoQuery *q)
+	{
+	mDNS_Lock(m);
+	// We use mDNS_StopQuery_internal here because we're already holding the lock
+	if (q->qSRV.ThisQInterval >= 0) mDNS_StopQuery_internal(m, &q->qSRV);
+	if (q->qTXT.ThisQInterval >= 0) mDNS_StopQuery_internal(m, &q->qTXT);
+	if (q->qAv4.ThisQInterval >= 0) mDNS_StopQuery_internal(m, &q->qAv4);
+	if (q->qAv6.ThisQInterval >= 0) mDNS_StopQuery_internal(m, &q->qAv6);
+	mDNS_Unlock(m);
+	}
+
+mDNSexport mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, mDNS_DomainType DomainType, const domainname *dom,
+	const mDNSInterfaceID InterfaceID, mDNSQuestionCallback *Callback, void *Context)
+	{
+	question->InterfaceID      = InterfaceID;
+	question->Target           = zeroAddr;
+	question->qtype            = kDNSType_PTR;
+	question->qclass           = kDNSClass_IN;
+	question->LongLived        = mDNSfalse;
+	question->ExpectUnique     = mDNSfalse;
+	question->ForceMCast       = mDNSfalse;
+	question->ReturnIntermed   = mDNSfalse;
+	question->SuppressUnusable = mDNSfalse;
+	question->SearchListIndex  = 0;
+	question->AppendSearchDomains = 0;
+	question->RetryWithSearchDomains = mDNSfalse;
+	question->TimeoutQuestion  = 0;
+	question->WakeOnResolve    = 0;
+	question->qnameOrig        = mDNSNULL;
+	question->QuestionCallback = Callback;
+	question->QuestionContext  = Context;
+	if (DomainType > mDNS_DomainTypeMax) return(mStatus_BadParamErr);
+	if (!MakeDomainNameFromDNSNameString(&question->qname, mDNS_DomainTypeNames[DomainType])) return(mStatus_BadParamErr);
+	if (!dom) dom = &localdomain;
+	if (!AppendDomainName(&question->qname, dom)) return(mStatus_BadParamErr);
+	return(mDNS_StartQuery(m, question));
+	}
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - Responder Functions
+#endif
+
+mDNSexport mStatus mDNS_Register(mDNS *const m, AuthRecord *const rr)
+	{
+	mStatus status;
+	mDNS_Lock(m);
+	status = mDNS_Register_internal(m, rr);
+	mDNS_Unlock(m);
+	return(status);
+	}
+
+mDNSexport mStatus mDNS_Update(mDNS *const m, AuthRecord *const rr, mDNSu32 newttl,
+	const mDNSu16 newrdlength, RData *const newrdata, mDNSRecordUpdateCallback *Callback)
+	{
+	if (!ValidateRData(rr->resrec.rrtype, newrdlength, newrdata))
+		{
+		LogMsg("Attempt to update record with invalid rdata: %s", GetRRDisplayString_rdb(&rr->resrec, &newrdata->u, m->MsgBuffer));
+		return(mStatus_Invalid);
+		}
+	
+	mDNS_Lock(m);
+
+	// If TTL is unspecified, leave TTL unchanged
+	if (newttl == 0) newttl = rr->resrec.rroriginalttl;
+
+	// If we already have an update queued up which has not gone through yet, give the client a chance to free that memory
+	if (rr->NewRData)
+		{
+		RData *n = rr->NewRData;
+		rr->NewRData = mDNSNULL;							// Clear the NewRData pointer ...
+		if (rr->UpdateCallback)
+			rr->UpdateCallback(m, rr, n, rr->newrdlength);	// ...and let the client free this memory, if necessary
+		}
+
+	rr->NewRData             = newrdata;
+	rr->newrdlength          = newrdlength;
+	rr->UpdateCallback       = Callback;
+
+#ifndef UNICAST_DISABLED
+	if (rr->ARType != AuthRecordLocalOnly && rr->ARType != AuthRecordP2P && !IsLocalDomain(rr->resrec.name))
+		{
+		mStatus status = uDNS_UpdateRecord(m, rr);
+		// The caller frees the memory on error, don't retain stale pointers
+		if (status != mStatus_NoError) { rr->NewRData = mDNSNULL; rr->newrdlength = 0; }
+		mDNS_Unlock(m);
+		return(status);
+		}
+#endif
+
+	if (RRLocalOnly(rr) || (rr->resrec.rroriginalttl == newttl &&
+		rr->resrec.rdlength == newrdlength && mDNSPlatformMemSame(rr->resrec.rdata->u.data, newrdata->u.data, newrdlength)))
+		CompleteRDataUpdate(m, rr);
+	else
+		{
+		rr->AnnounceCount = InitialAnnounceCount;
+		InitializeLastAPTime(m, rr);
+		while (rr->NextUpdateCredit && m->timenow - rr->NextUpdateCredit >= 0) GrantUpdateCredit(rr);
+		if (!rr->UpdateBlocked && rr->UpdateCredits) rr->UpdateCredits--;
+		if (!rr->NextUpdateCredit) rr->NextUpdateCredit = NonZeroTime(m->timenow + kUpdateCreditRefreshInterval);
+		if (rr->AnnounceCount > rr->UpdateCredits + 1) rr->AnnounceCount = (mDNSu8)(rr->UpdateCredits + 1);
+		if (rr->UpdateCredits <= 5)
+			{
+			mDNSu32 delay = 6 - rr->UpdateCredits;		// Delay 1 second, then 2, then 3, etc. up to 6 seconds maximum
+			if (!rr->UpdateBlocked) rr->UpdateBlocked = NonZeroTime(m->timenow + (mDNSs32)delay * mDNSPlatformOneSecond);
+			rr->ThisAPInterval *= 4;
+			rr->LastAPTime = rr->UpdateBlocked - rr->ThisAPInterval;
+			LogMsg("Excessive update rate for %##s; delaying announcement by %ld second%s",
+				rr->resrec.name->c, delay, delay > 1 ? "s" : "");
+			}
+		rr->resrec.rroriginalttl = newttl;
+		}
+
+	mDNS_Unlock(m);
+	return(mStatus_NoError);
+	}
+
+// Note: mDNS_Deregister calls mDNS_Deregister_internal which can call a user callback, which may change
+// the record list and/or question list.
+// Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
+mDNSexport mStatus mDNS_Deregister(mDNS *const m, AuthRecord *const rr)
+	{
+	mStatus status;
+	mDNS_Lock(m);
+	status = mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal);
+	mDNS_Unlock(m);
+	return(status);
+	}
+
+// Circular reference: AdvertiseInterface references mDNS_HostNameCallback, which calls mDNS_SetFQDN, which call AdvertiseInterface
+mDNSlocal void mDNS_HostNameCallback(mDNS *const m, AuthRecord *const rr, mStatus result);
+
+mDNSlocal NetworkInterfaceInfo *FindFirstAdvertisedInterface(mDNS *const m)
+	{
+	NetworkInterfaceInfo *intf;
+	for (intf = m->HostInterfaces; intf; intf = intf->next)
+		if (intf->Advertise) break;
+	return(intf);
+	}
+
+mDNSlocal void AdvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set)
+	{
+	char buffer[MAX_REVERSE_MAPPING_NAME];
+	NetworkInterfaceInfo *primary = FindFirstAdvertisedInterface(m);
+	if (!primary) primary = set; // If no existing advertised interface, this new NetworkInterfaceInfo becomes our new primary
+
+	// Send dynamic update for non-linklocal IPv4 Addresses
+	mDNS_SetupResourceRecord(&set->RR_A,     mDNSNULL, set->InterfaceID, kDNSType_A,     kHostNameTTL, kDNSRecordTypeUnique,      AuthRecordAny, mDNS_HostNameCallback, set);
+	mDNS_SetupResourceRecord(&set->RR_PTR,   mDNSNULL, set->InterfaceID, kDNSType_PTR,   kHostNameTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
+	mDNS_SetupResourceRecord(&set->RR_HINFO, mDNSNULL, set->InterfaceID, kDNSType_HINFO, kHostNameTTL, kDNSRecordTypeUnique,      AuthRecordAny, mDNSNULL, mDNSNULL);
+
+#if ANSWER_REMOTE_HOSTNAME_QUERIES
+	set->RR_A    .AllowRemoteQuery  = mDNStrue;
+	set->RR_PTR  .AllowRemoteQuery  = mDNStrue;
+	set->RR_HINFO.AllowRemoteQuery  = mDNStrue;
+#endif
+	// 1. Set up Address record to map from host name ("foo.local.") to IP address
+	// 2. Set up reverse-lookup PTR record to map from our address back to our host name
+	AssignDomainName(&set->RR_A.namestorage, &m->MulticastHostname);
+	if (set->ip.type == mDNSAddrType_IPv4)
+		{
+		set->RR_A.resrec.rrtype = kDNSType_A;
+		set->RR_A.resrec.rdata->u.ipv4 = set->ip.ip.v4;
+		// Note: This is reverse order compared to a normal dotted-decimal IP address, so we can't use our customary "%.4a" format code
+		mDNS_snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d.in-addr.arpa.",
+			set->ip.ip.v4.b[3], set->ip.ip.v4.b[2], set->ip.ip.v4.b[1], set->ip.ip.v4.b[0]);
+		}
+	else if (set->ip.type == mDNSAddrType_IPv6)
+		{
+		int i;
+		set->RR_A.resrec.rrtype = kDNSType_AAAA;
+		set->RR_A.resrec.rdata->u.ipv6 = set->ip.ip.v6;
+		for (i = 0; i < 16; i++)
+			{
+			static const char hexValues[] = "0123456789ABCDEF";
+			buffer[i * 4    ] = hexValues[set->ip.ip.v6.b[15 - i] & 0x0F];
+			buffer[i * 4 + 1] = '.';
+			buffer[i * 4 + 2] = hexValues[set->ip.ip.v6.b[15 - i] >> 4];
+			buffer[i * 4 + 3] = '.';
+			}
+		mDNS_snprintf(&buffer[64], sizeof(buffer)-64, "ip6.arpa.");
+		}
+
+	MakeDomainNameFromDNSNameString(&set->RR_PTR.namestorage, buffer);
+	set->RR_PTR.AutoTarget = Target_AutoHost;	// Tell mDNS that the target of this PTR is to be kept in sync with our host name
+	set->RR_PTR.ForceMCast = mDNStrue;			// This PTR points to our dot-local name, so don't ever try to write it into a uDNS server
+
+	set->RR_A.RRSet = &primary->RR_A;			// May refer to self
+
+	mDNS_Register_internal(m, &set->RR_A);
+	mDNS_Register_internal(m, &set->RR_PTR);
+
+	if (!NO_HINFO && m->HIHardware.c[0] > 0 && m->HISoftware.c[0] > 0 && m->HIHardware.c[0] + m->HISoftware.c[0] <= 254)
+		{
+		mDNSu8 *p = set->RR_HINFO.resrec.rdata->u.data;
+		AssignDomainName(&set->RR_HINFO.namestorage, &m->MulticastHostname);
+		set->RR_HINFO.DependentOn = &set->RR_A;
+		mDNSPlatformMemCopy(p, &m->HIHardware, 1 + (mDNSu32)m->HIHardware.c[0]);
+		p += 1 + (int)p[0];
+		mDNSPlatformMemCopy(p, &m->HISoftware, 1 + (mDNSu32)m->HISoftware.c[0]);
+		mDNS_Register_internal(m, &set->RR_HINFO);
+		}
+	else
+		{
+		debugf("Not creating HINFO record: platform support layer provided no information");
+		set->RR_HINFO.resrec.RecordType = kDNSRecordTypeUnregistered;
+		}
+	}
+
+mDNSlocal void DeadvertiseInterface(mDNS *const m, NetworkInterfaceInfo *set)
+	{
+	NetworkInterfaceInfo *intf;
+	
+    // If we still have address records referring to this one, update them
+	NetworkInterfaceInfo *primary = FindFirstAdvertisedInterface(m);
+	AuthRecord *A = primary ? &primary->RR_A : mDNSNULL;
+	for (intf = m->HostInterfaces; intf; intf = intf->next)
+		if (intf->RR_A.RRSet == &set->RR_A)
+			intf->RR_A.RRSet = A;
+
+	// Unregister these records.
+	// When doing the mDNS_Exit processing, we first call DeadvertiseInterface for each interface, so by the time the platform
+	// support layer gets to call mDNS_DeregisterInterface, the address and PTR records have already been deregistered for it.
+	// Also, in the event of a name conflict, one or more of our records will have been forcibly deregistered.
+	// To avoid unnecessary and misleading warning messages, we check the RecordType before calling mDNS_Deregister_internal().
+	if (set->RR_A.    resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_A,     mDNS_Dereg_normal);
+	if (set->RR_PTR.  resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_PTR,   mDNS_Dereg_normal);
+	if (set->RR_HINFO.resrec.RecordType) mDNS_Deregister_internal(m, &set->RR_HINFO, mDNS_Dereg_normal);
+	}
+
+mDNSexport void mDNS_SetFQDN(mDNS *const m)
+	{
+	domainname newmname;
+	NetworkInterfaceInfo *intf;
+	AuthRecord *rr;
+	newmname.c[0] = 0;
+
+	if (!AppendDomainLabel(&newmname, &m->hostlabel))  { LogMsg("ERROR: mDNS_SetFQDN: Cannot create MulticastHostname"); return; }
+	if (!AppendLiteralLabelString(&newmname, "local")) { LogMsg("ERROR: mDNS_SetFQDN: Cannot create MulticastHostname"); return; }
+
+	mDNS_Lock(m);
+
+	if (SameDomainNameCS(&m->MulticastHostname, &newmname)) debugf("mDNS_SetFQDN - hostname unchanged");
+	else
+		{
+		AssignDomainName(&m->MulticastHostname, &newmname);
+	
+		// 1. Stop advertising our address records on all interfaces
+		for (intf = m->HostInterfaces; intf; intf = intf->next)
+			if (intf->Advertise) DeadvertiseInterface(m, intf);
+	
+		// 2. Start advertising our address records using the new name
+		for (intf = m->HostInterfaces; intf; intf = intf->next)
+			if (intf->Advertise) AdvertiseInterface(m, intf);
+		}
+
+	// 3. Make sure that any AutoTarget SRV records (and the like) get updated
+	for (rr = m->ResourceRecords;  rr; rr=rr->next) if (rr->AutoTarget) SetTargetToHostName(m, rr);
+	for (rr = m->DuplicateRecords; rr; rr=rr->next) if (rr->AutoTarget) SetTargetToHostName(m, rr);
+	
+	mDNS_Unlock(m);
+	}
+
+mDNSlocal void mDNS_HostNameCallback(mDNS *const m, AuthRecord *const rr, mStatus result)
+	{
+	(void)rr;	// Unused parameter
+	
+	#if MDNS_DEBUGMSGS
+		{
+		char *msg = "Unknown result";
+		if      (result == mStatus_NoError)      msg = "Name registered";
+		else if (result == mStatus_NameConflict) msg = "Name conflict";
+		debugf("mDNS_HostNameCallback: %##s (%s) %s (%ld)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), msg, result);
+		}
+	#endif
+
+	if (result == mStatus_NoError)
+		{
+		// Notify the client that the host name is successfully registered
+		if (m->MainCallback)
+			m->MainCallback(m, mStatus_NoError);
+		}
+	else if (result == mStatus_NameConflict)
+		{
+		domainlabel oldlabel = m->hostlabel;
+
+		// 1. First give the client callback a chance to pick a new name
+		if (m->MainCallback)
+			m->MainCallback(m, mStatus_NameConflict);
+
+		// 2. If the client callback didn't do it, add (or increment) an index ourselves
+		// This needs to be case-INSENSITIVE compare, because we need to know that the name has been changed so as to
+		// remedy the conflict, and a name that differs only in capitalization will just suffer the exact same conflict again.
+		if (SameDomainLabel(m->hostlabel.c, oldlabel.c))
+			IncrementLabelSuffix(&m->hostlabel, mDNSfalse);
+		
+		// 3. Generate the FQDNs from the hostlabel,
+		// and make sure all SRV records, etc., are updated to reference our new hostname
+		mDNS_SetFQDN(m);
+		LogMsg("Local Hostname %#s.local already in use; will try %#s.local instead", oldlabel.c, m->hostlabel.c);
+		}
+	else if (result == mStatus_MemFree)
+		{
+		// .local hostnames do not require goodbyes - we ignore the MemFree (which is sent directly by
+		// mDNS_Deregister_internal), and allow the caller to deallocate immediately following mDNS_DeadvertiseInterface
+		debugf("mDNS_HostNameCallback: MemFree (ignored)");
+		}
+	else
+		LogMsg("mDNS_HostNameCallback: Unknown error %d for registration of record %s", result,  rr->resrec.name->c);
+	}
+
+mDNSlocal void UpdateInterfaceProtocols(mDNS *const m, NetworkInterfaceInfo *active)
+	{
+	NetworkInterfaceInfo *intf;
+	active->IPv4Available = mDNSfalse;
+	active->IPv6Available = mDNSfalse;
+	for (intf = m->HostInterfaces; intf; intf = intf->next)
+		if (intf->InterfaceID == active->InterfaceID)
+			{
+			if (intf->ip.type == mDNSAddrType_IPv4 && intf->McastTxRx) active->IPv4Available = mDNStrue;
+			if (intf->ip.type == mDNSAddrType_IPv6 && intf->McastTxRx) active->IPv6Available = mDNStrue;
+			}
+	}
+
+mDNSlocal void RestartRecordGetZoneData(mDNS * const m)
+	{
+	AuthRecord *rr;
+	LogInfo("RestartRecordGetZoneData: ResourceRecords");
+	for (rr = m->ResourceRecords; rr; rr=rr->next)
+		if (AuthRecord_uDNS(rr) && rr->state != regState_NoTarget)
+			{
+			debugf("RestartRecordGetZoneData: StartGetZoneData for %##s", rr->resrec.name->c);
+			// Zero out the updateid so that if we have a pending response from the server, it won't
+			// be accepted as a valid response. If we accept the response, we might free the new "nta"
+			if (rr->nta) { rr->updateid = zeroID; CancelGetZoneData(m, rr->nta); }
+			rr->nta = StartGetZoneData(m, rr->resrec.name, ZoneServiceUpdate, RecordRegistrationGotZoneData, rr);
+			}
+	}
+
+mDNSlocal void InitializeNetWakeState(mDNS *const m, NetworkInterfaceInfo *set)
+	{
+	int i;
+	set->NetWakeBrowse.ThisQInterval = -1;
+	for (i=0; i<3; i++)
+		{
+		set->NetWakeResolve[i].ThisQInterval = -1;
+		set->SPSAddr[i].type = mDNSAddrType_None;
+		}
+	set->NextSPSAttempt     = -1;
+	set->NextSPSAttemptTime = m->timenow;
+	}
+
+mDNSexport void mDNS_ActivateNetWake_internal(mDNS *const m, NetworkInterfaceInfo *set)
+	{
+	NetworkInterfaceInfo *p = m->HostInterfaces;
+	while (p && p != set) p=p->next;
+	if (!p) { LogMsg("mDNS_ActivateNetWake_internal: NetworkInterfaceInfo %p not found in active list", set); return; }
+
+	if (set->InterfaceActive)
+		{
+		LogSPS("ActivateNetWake for %s (%#a)", set->ifname, &set->ip);
+		mDNS_StartBrowse_internal(m, &set->NetWakeBrowse, &SleepProxyServiceType, &localdomain, set->InterfaceID, mDNSfalse, m->SPSBrowseCallback, set);
+		}
+	}
+
+mDNSexport void mDNS_DeactivateNetWake_internal(mDNS *const m, NetworkInterfaceInfo *set)
+	{
+	NetworkInterfaceInfo *p = m->HostInterfaces;
+	while (p && p != set) p=p->next;
+	if (!p) { LogMsg("mDNS_DeactivateNetWake_internal: NetworkInterfaceInfo %p not found in active list", set); return; }
+
+	if (set->NetWakeBrowse.ThisQInterval >= 0)
+		{
+		int i;
+		LogSPS("DeactivateNetWake for %s (%#a)", set->ifname, &set->ip);
+
+		// Stop our browse and resolve operations
+		mDNS_StopQuery_internal(m, &set->NetWakeBrowse);
+		for (i=0; i<3; i++) if (set->NetWakeResolve[i].ThisQInterval >= 0) mDNS_StopQuery_internal(m, &set->NetWakeResolve[i]);
+
+		// Make special call to the browse callback to let it know it can to remove all records for this interface
+		if (m->SPSBrowseCallback)
+			{
+			mDNS_DropLockBeforeCallback();		// Allow client to legally make mDNS API calls from the callback
+			m->SPSBrowseCallback(m, &set->NetWakeBrowse, mDNSNULL, mDNSfalse);
+			mDNS_ReclaimLockAfterCallback();	// Decrement mDNS_reentrancy to block mDNS API calls again
+			}
+
+		// Reset our variables back to initial state, so we're ready for when NetWake is turned back on
+		// (includes resetting NetWakeBrowse.ThisQInterval back to -1)
+		InitializeNetWakeState(m, set);
+		}
+	}
+
+mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *set, mDNSBool flapping)
+	{
+	AuthRecord *rr;
+	mDNSBool FirstOfType = mDNStrue;
+	NetworkInterfaceInfo **p = &m->HostInterfaces;
+
+	if (!set->InterfaceID)
+		{ LogMsg("mDNS_RegisterInterface: Error! Tried to register a NetworkInterfaceInfo %#a with zero InterfaceID", &set->ip); return(mStatus_Invalid); }
+
+	if (!mDNSAddressIsValidNonZero(&set->mask))
+		{ LogMsg("mDNS_RegisterInterface: Error! Tried to register a NetworkInterfaceInfo %#a with invalid mask %#a", &set->ip, &set->mask); return(mStatus_Invalid); }
+
+	mDNS_Lock(m);
+	
+	// Assume this interface will be active now, unless we find a duplicate already in the list
+	set->InterfaceActive = mDNStrue;
+	set->IPv4Available   = (mDNSu8)(set->ip.type == mDNSAddrType_IPv4 && set->McastTxRx);
+	set->IPv6Available   = (mDNSu8)(set->ip.type == mDNSAddrType_IPv6 && set->McastTxRx);
+	
+	InitializeNetWakeState(m, set);
+
+	// Scan list to see if this InterfaceID is already represented
+	while (*p)
+		{
+		if (*p == set)
+			{
+			LogMsg("mDNS_RegisterInterface: Error! Tried to register a NetworkInterfaceInfo that's already in the list");
+			mDNS_Unlock(m);
+			return(mStatus_AlreadyRegistered);
+			}
+
+		if ((*p)->InterfaceID == set->InterfaceID)
+			{
+			// This InterfaceID already represented by a different interface in the list, so mark this instance inactive for now
+			set->InterfaceActive = mDNSfalse;
+			if (set->ip.type == (*p)->ip.type) FirstOfType = mDNSfalse;
+			if (set->ip.type == mDNSAddrType_IPv4 && set->McastTxRx) (*p)->IPv4Available = mDNStrue;
+			if (set->ip.type == mDNSAddrType_IPv6 && set->McastTxRx) (*p)->IPv6Available = mDNStrue;
+			}
+
+		p=&(*p)->next;
+		}
+
+	set->next = mDNSNULL;
+	*p = set;
+	
+	if (set->Advertise)
+		AdvertiseInterface(m, set);
+
+	LogInfo("mDNS_RegisterInterface: InterfaceID %p %s (%#a) %s", set->InterfaceID, set->ifname, &set->ip,
+		set->InterfaceActive ?
+			"not represented in list; marking active and retriggering queries" :
+			"already represented in list; marking inactive for now");
+	
+	if (set->NetWake) mDNS_ActivateNetWake_internal(m, set);
+
+	// In early versions of OS X the IPv6 address remains on an interface even when the interface is turned off,
+	// giving the false impression that there's an active representative of this interface when there really isn't.
+	// Therefore, when registering an interface, we want to re-trigger our questions and re-probe our Resource Records,
+	// even if we believe that we previously had an active representative of this interface.
+	if (set->McastTxRx && (FirstOfType || set->InterfaceActive))
+		{
+		DNSQuestion *q;
+		// Normally, after an interface comes up, we pause half a second before beginning probing.
+		// This is to guard against cases where there's rapid interface changes, where we could be confused by
+		// seeing packets we ourselves sent just moments ago (perhaps when this interface had a different address)
+		// which are then echoed back after a short delay by some Ethernet switches and some 802.11 base stations.
+		// We don't want to do a probe, and then see a stale echo of an announcement we ourselves sent,
+		// and think it's a conflicting answer to our probe.
+		// In the case of a flapping interface, we pause for five seconds, and reduce the announcement count to one packet.
+		const mDNSs32 probedelay  = flapping ? mDNSPlatformOneSecond * 5 : mDNSPlatformOneSecond / 2;
+		const mDNSu8  numannounce = flapping ? (mDNSu8)1                 : InitialAnnounceCount;
+
+		// Use a small amount of randomness:
+		// In the case of a network administrator turning on an Ethernet hub so that all the
+		// connected machines establish link at exactly the same time, we don't want them all
+		// to go and hit the network with identical queries at exactly the same moment.
+		// We set a random delay of up to InitialQuestionInterval (1/3 second).
+		// We must *never* set m->SuppressSending to more than that (or set it repeatedly in a way
+		// that causes mDNSResponder to remain in a prolonged state of SuppressSending, because
+		// suppressing packet sending for more than about 1/3 second can cause protocol correctness
+		// to start to break down (e.g. we don't answer probes fast enough, and get name conflicts).
+		// See <rdar://problem/4073853> mDNS: m->SuppressSending set too enthusiastically
+		if (!m->SuppressSending) m->SuppressSending = m->timenow + (mDNSs32)mDNSRandom((mDNSu32)InitialQuestionInterval);
+
+		if (flapping) LogMsg("mDNS_RegisterInterface: Frequent transitions for interface %s (%#a)", set->ifname, &set->ip);
+
+		LogInfo("mDNS_RegisterInterface: %s (%#a) probedelay %d", set->ifname, &set->ip, probedelay);
+		if (m->SuppressProbes == 0 ||
+			m->SuppressProbes - NonZeroTime(m->timenow + probedelay) < 0)
+			m->SuppressProbes = NonZeroTime(m->timenow + probedelay);
+
+		// Include OWNER option in packets for 60 seconds after connecting to the network. Setting
+		// it here also handles the wake up case as the network link comes UP after waking causing
+		// us to reconnect to the network. If we do this as part of the wake up code, it is possible
+		// that the network link comes UP after 60 seconds and we never set the OWNER option
+		m->AnnounceOwner = NonZeroTime(m->timenow + 60 * mDNSPlatformOneSecond);
+		LogInfo("mDNS_RegisterInterface: Setting AnnounceOwner");
+
+		for (q = m->Questions; q; q=q->next)								// Scan our list of questions
+			if (mDNSOpaque16IsZero(q->TargetQID))
+				if (!q->InterfaceID || q->InterfaceID == set->InterfaceID)		// If non-specific Q, or Q on this specific interface,
+					{															// then reactivate this question
+					// If flapping, delay between first and second queries is nine seconds instead of one second
+					mDNSBool dodelay = flapping && (q->FlappingInterface1 == set->InterfaceID || q->FlappingInterface2 == set->InterfaceID);
+					mDNSs32 initial  = dodelay ? InitialQuestionInterval * QuestionIntervalStep2 : InitialQuestionInterval;
+					mDNSs32 qdelay   = dodelay ? mDNSPlatformOneSecond * 5 : 0;
+					if (dodelay) LogInfo("No cache records expired for %##s (%s); okay to delay questions a little", q->qname.c, DNSTypeName(q->qtype));
+						
+					if (!q->ThisQInterval || q->ThisQInterval > initial)
+						{
+						q->ThisQInterval = initial;
+						q->RequestUnicast = 2; // Set to 2 because is decremented once *before* we check it
+						}
+					q->LastQTime = m->timenow - q->ThisQInterval + qdelay;
+					q->RecentAnswerPkts = 0;
+					SetNextQueryTime(m,q);
+					}
+		
+		// For all our non-specific authoritative resource records (and any dormant records specific to this interface)
+		// we now need them to re-probe if necessary, and then re-announce.
+		for (rr = m->ResourceRecords; rr; rr=rr->next)
+			if (!AuthRecord_uDNS(rr))
+				if (!rr->resrec.InterfaceID || rr->resrec.InterfaceID == set->InterfaceID)
+					{
+					if (rr->resrec.RecordType == kDNSRecordTypeVerified && !rr->DependentOn) rr->resrec.RecordType = kDNSRecordTypeUnique;
+					rr->ProbeCount     = DefaultProbeCountForRecordType(rr->resrec.RecordType);
+					if (rr->AnnounceCount < numannounce) rr->AnnounceCount  = numannounce;
+					rr->SendNSECNow    = mDNSNULL;
+					InitializeLastAPTime(m, rr);
+					}
+		}
+
+	RestartRecordGetZoneData(m);
+
+	CheckSuppressUnusableQuestions(m);
+
+	mDNS_UpdateAllowSleep(m);
+
+	mDNS_Unlock(m);
+	return(mStatus_NoError);
+	}
+
+// Note: mDNS_DeregisterInterface calls mDNS_Deregister_internal which can call a user callback, which may change
+// the record list and/or question list.
+// Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
+mDNSexport void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *set, mDNSBool flapping)
+	{
+	NetworkInterfaceInfo **p = &m->HostInterfaces;
+	mDNSBool revalidate = mDNSfalse;
+
+	mDNS_Lock(m);
+
+	// Find this record in our list
+	while (*p && *p != set) p=&(*p)->next;
+	if (!*p) { debugf("mDNS_DeregisterInterface: NetworkInterfaceInfo not found in list"); mDNS_Unlock(m); return; }
+
+	mDNS_DeactivateNetWake_internal(m, set);
+
+	// Unlink this record from our list
+	*p = (*p)->next;
+	set->next = mDNSNULL;
+
+	if (!set->InterfaceActive)
+		{
+		// If this interface not the active member of its set, update the v4/v6Available flags for the active member
+		NetworkInterfaceInfo *intf;
+		for (intf = m->HostInterfaces; intf; intf = intf->next)
+			if (intf->InterfaceActive && intf->InterfaceID == set->InterfaceID)
+				UpdateInterfaceProtocols(m, intf);
+		}
+	else
+		{
+		NetworkInterfaceInfo *intf = FirstInterfaceForID(m, set->InterfaceID);
+		if (intf)
+			{
+			LogInfo("mDNS_DeregisterInterface: Another representative of InterfaceID %p %s (%#a) exists;"
+				" making it active", set->InterfaceID, set->ifname, &set->ip);
+			if (intf->InterfaceActive)
+				LogMsg("mDNS_DeregisterInterface: ERROR intf->InterfaceActive already set for %s (%#a)", set->ifname, &set->ip);
+			intf->InterfaceActive = mDNStrue;
+			UpdateInterfaceProtocols(m, intf);
+
+			if (intf->NetWake) mDNS_ActivateNetWake_internal(m, intf);
+			
+			// See if another representative *of the same type* exists. If not, we mave have gone from
+			// dual-stack to v6-only (or v4-only) so we need to reconfirm which records are still valid.
+			for (intf = m->HostInterfaces; intf; intf = intf->next)
+				if (intf->InterfaceID == set->InterfaceID && intf->ip.type == set->ip.type)
+					break;
+			if (!intf) revalidate = mDNStrue;
+			}
+		else
+			{
+			mDNSu32 slot;
+			CacheGroup *cg;
+			CacheRecord *rr;
+			DNSQuestion *q;
+			DNSServer *s;
+
+			LogInfo("mDNS_DeregisterInterface: Last representative of InterfaceID %p %s (%#a) deregistered;"
+				" marking questions etc. dormant", set->InterfaceID, set->ifname, &set->ip);
+
+			if (set->McastTxRx && flapping)
+				LogMsg("DeregisterInterface: Frequent transitions for interface %s (%#a)", set->ifname, &set->ip);
+
+			// 1. Deactivate any questions specific to this interface, and tag appropriate questions
+			// so that mDNS_RegisterInterface() knows how swiftly it needs to reactivate them
+			for (q = m->Questions; q; q=q->next)
+				{
+				if (q->InterfaceID == set->InterfaceID) q->ThisQInterval = 0;
+				if (!q->InterfaceID || q->InterfaceID == set->InterfaceID)
+					{
+					q->FlappingInterface2 = q->FlappingInterface1;
+					q->FlappingInterface1 = set->InterfaceID;		// Keep history of the last two interfaces to go away
+					}
+				}
+
+			// 2. Flush any cache records received on this interface
+			revalidate = mDNSfalse;		// Don't revalidate if we're flushing the records
+			FORALL_CACHERECORDS(slot, cg, rr)
+				if (rr->resrec.InterfaceID == set->InterfaceID)
+					{
+					// If this interface is deemed flapping,
+					// postpone deleting the cache records in case the interface comes back again
+					if (set->McastTxRx && flapping)
+						{
+						// For a flapping interface we want these record to go away after 30 seconds
+						mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForFlappingInterface);
+						// We set UnansweredQueries = MaxUnansweredQueries so we don't waste time doing any queries for them --
+						// if the interface does come back, any relevant questions will be reactivated anyway
+						rr->UnansweredQueries = MaxUnansweredQueries;
+						}
+					else
+						mDNS_PurgeCacheResourceRecord(m, rr);
+					}
+
+			// 3. Any DNS servers specific to this interface are now unusable
+			for (s = m->DNSServers; s; s = s->next)
+				if (s->interface == set->InterfaceID)
+					{
+					s->interface = mDNSInterface_Any;
+					s->teststate = DNSServer_Disabled;
+					}
+			}
+		}
+
+	// If we were advertising on this interface, deregister those address and reverse-lookup records now
+	if (set->Advertise) DeadvertiseInterface(m, set);
+
+	// If we have any cache records received on this interface that went away, then re-verify them.
+	// In some versions of OS X the IPv6 address remains on an interface even when the interface is turned off,
+	// giving the false impression that there's an active representative of this interface when there really isn't.
+	// Don't need to do this when shutting down, because *all* interfaces are about to go away
+	if (revalidate && !m->ShutdownTime)
+		{
+		mDNSu32 slot;
+		CacheGroup *cg;
+		CacheRecord *rr;
+		FORALL_CACHERECORDS(slot, cg, rr)
+			if (rr->resrec.InterfaceID == set->InterfaceID)
+				mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForFlappingInterface);
+		}
+
+	CheckSuppressUnusableQuestions(m);
+
+	mDNS_UpdateAllowSleep(m);
+
+	mDNS_Unlock(m);
+	}
+
+mDNSlocal void ServiceCallback(mDNS *const m, AuthRecord *const rr, mStatus result)
+	{
+	ServiceRecordSet *sr = (ServiceRecordSet *)rr->RecordContext;
+	(void)m;	// Unused parameter
+
+	#if MDNS_DEBUGMSGS
+		{
+		char *msg = "Unknown result";
+		if      (result == mStatus_NoError)      msg = "Name Registered";
+		else if (result == mStatus_NameConflict) msg = "Name Conflict";
+		else if (result == mStatus_MemFree)      msg = "Memory Free";
+		debugf("ServiceCallback: %##s (%s) %s (%d)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), msg, result);
+		}
+	#endif
+
+	// Only pass on the NoError acknowledgement for the SRV record (when it finishes probing)
+	if (result == mStatus_NoError && rr != &sr->RR_SRV) return;
+
+	// If we got a name conflict on either SRV or TXT, forcibly deregister this service, and record that we did that
+	if (result == mStatus_NameConflict)
+		{
+		sr->Conflict = mDNStrue;				// Record that this service set had a conflict
+		mDNS_DeregisterService(m, sr);			// Unlink the records from our list
+		return;
+		}
+	
+	if (result == mStatus_MemFree)
+		{
+		// If the SRV/TXT/PTR records, or the _services._dns-sd._udp record, or any of the subtype PTR records,
+		// are still in the process of deregistering, don't pass on the NameConflict/MemFree message until
+		// every record is finished cleaning up.
+		mDNSu32 i;
+		ExtraResourceRecord *e = sr->Extras;
+
+		if (sr->RR_SRV.resrec.RecordType != kDNSRecordTypeUnregistered) return;
+		if (sr->RR_TXT.resrec.RecordType != kDNSRecordTypeUnregistered) return;
+		if (sr->RR_PTR.resrec.RecordType != kDNSRecordTypeUnregistered) return;
+		if (sr->RR_ADV.resrec.RecordType != kDNSRecordTypeUnregistered) return;
+		for (i=0; i<sr->NumSubTypes; i++) if (sr->SubTypes[i].resrec.RecordType != kDNSRecordTypeUnregistered) return;
+
+		while (e)
+			{
+			if (e->r.resrec.RecordType != kDNSRecordTypeUnregistered) return;
+			e = e->next;
+			}
+
+		// If this ServiceRecordSet was forcibly deregistered, and now its memory is ready for reuse,
+		// then we can now report the NameConflict to the client
+		if (sr->Conflict) result = mStatus_NameConflict;
+
+		}
+
+	LogInfo("ServiceCallback: All records %s for %##s", (result == mStatus_MemFree ? "Unregistered": "Registered"), sr->RR_PTR.resrec.name->c);
+	// CAUTION: MUST NOT do anything more with sr after calling sr->Callback(), because the client's callback
+	// function is allowed to do anything, including deregistering this service and freeing its memory.
+	if (sr->ServiceCallback)
+		sr->ServiceCallback(m, sr, result);
+	}
+
+mDNSlocal void NSSCallback(mDNS *const m, AuthRecord *const rr, mStatus result)
+	{
+	ServiceRecordSet *sr = (ServiceRecordSet *)rr->RecordContext;
+	if (sr->ServiceCallback)
+		sr->ServiceCallback(m, sr, result);
+	}
+
+// Note:
+// Name is first label of domain name (any dots in the name are actual dots, not label separators)
+// Type is service type (e.g. "_ipp._tcp.")
+// Domain is fully qualified domain name (i.e. ending with a null label)
+// We always register a TXT, even if it is empty (so that clients are not
+// left waiting forever looking for a nonexistent record.)
+// If the host parameter is mDNSNULL or the root domain (ASCII NUL),
+// then the default host name (m->MulticastHostname) is automatically used
+// If the optional target host parameter is set, then the storage it points to must remain valid for the lifetime of the service registration
+mDNSexport mStatus mDNS_RegisterService(mDNS *const m, ServiceRecordSet *sr,
+	const domainlabel *const name, const domainname *const type, const domainname *const domain,
+	const domainname *const host, mDNSIPPort port, const mDNSu8 txtinfo[], mDNSu16 txtlen,
+	AuthRecord *SubTypes, mDNSu32 NumSubTypes,
+	mDNSInterfaceID InterfaceID, mDNSServiceCallback Callback, void *Context, mDNSu32 flags)
+	{
+	mStatus err;
+	mDNSu32 i;
+	mDNSu32 hostTTL;
+	AuthRecType artype;
+	mDNSu8 recordType = (flags & regFlagKnownUnique) ? kDNSRecordTypeKnownUnique : kDNSRecordTypeUnique;
+
+	sr->ServiceCallback = Callback;
+	sr->ServiceContext  = Context;
+	sr->Conflict        = mDNSfalse;
+
+	sr->Extras          = mDNSNULL;
+	sr->NumSubTypes     = NumSubTypes;
+	sr->SubTypes        = SubTypes;
+	
+	if (InterfaceID == mDNSInterface_LocalOnly)
+		artype = AuthRecordLocalOnly;
+	else if (InterfaceID == mDNSInterface_P2P)
+		artype = AuthRecordP2P;
+	else if ((InterfaceID == mDNSInterface_Any) && (flags & regFlagIncludeP2P))
+		artype = AuthRecordAnyIncludeP2P;
+	else
+		artype = AuthRecordAny;
+
+	// Initialize the AuthRecord objects to sane values
+	// Need to initialize everything correctly *before* making the decision whether to do a RegisterNoSuchService and bail out
+	mDNS_SetupResourceRecord(&sr->RR_ADV, mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeAdvisory, artype, ServiceCallback, sr);
+	mDNS_SetupResourceRecord(&sr->RR_PTR, mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeShared,   artype, ServiceCallback, sr);
+
+	if (SameDomainName(type, (const domainname *) "\x4" "_ubd" "\x4" "_tcp"))
+		hostTTL = kHostNameSmallTTL;
+	else
+		hostTTL = kHostNameTTL;
+
+	mDNS_SetupResourceRecord(&sr->RR_SRV, mDNSNULL, InterfaceID, kDNSType_SRV, hostTTL, recordType, artype, ServiceCallback, sr);
+	mDNS_SetupResourceRecord(&sr->RR_TXT, mDNSNULL, InterfaceID, kDNSType_TXT, kStandardTTL, kDNSRecordTypeUnique, artype, ServiceCallback, sr);
+
+	// If port number is zero, that means the client is really trying to do a RegisterNoSuchService
+	if (mDNSIPPortIsZero(port))
+		return(mDNS_RegisterNoSuchService(m, &sr->RR_SRV, name, type, domain, mDNSNULL, InterfaceID, NSSCallback, sr, (flags & regFlagIncludeP2P)));
+
+	// If the client is registering an oversized TXT record,
+	// it is the client's responsibility to alloate a ServiceRecordSet structure that is large enough for it
+	if (sr->RR_TXT.resrec.rdata->MaxRDLength < txtlen)
+		sr->RR_TXT.resrec.rdata->MaxRDLength = txtlen;
+
+	// Set up the record names
+	// For now we only create an advisory record for the main type, not for subtypes
+	// We need to gain some operational experience before we decide if there's a need to create them for subtypes too
+	if (ConstructServiceName(&sr->RR_ADV.namestorage, (const domainlabel*)"\x09_services", (const domainname*)"\x07_dns-sd\x04_udp", domain) == mDNSNULL)
+		return(mStatus_BadParamErr);
+	if (ConstructServiceName(&sr->RR_PTR.namestorage, mDNSNULL, type, domain) == mDNSNULL) return(mStatus_BadParamErr);
+	if (ConstructServiceName(&sr->RR_SRV.namestorage, name,     type, domain) == mDNSNULL) return(mStatus_BadParamErr);
+	AssignDomainName(&sr->RR_TXT.namestorage, sr->RR_SRV.resrec.name);
+	
+	// 1. Set up the ADV record rdata to advertise our service type
+	AssignDomainName(&sr->RR_ADV.resrec.rdata->u.name, sr->RR_PTR.resrec.name);
+
+	// 2. Set up the PTR record rdata to point to our service name
+	// We set up two additionals, so when a client asks for this PTR we automatically send the SRV and the TXT too
+	// Note: uDNS registration code assumes that Additional1 points to the SRV record
+	AssignDomainName(&sr->RR_PTR.resrec.rdata->u.name, sr->RR_SRV.resrec.name);
+	sr->RR_PTR.Additional1 = &sr->RR_SRV;
+	sr->RR_PTR.Additional2 = &sr->RR_TXT;
+
+	// 2a. Set up any subtype PTRs to point to our service name
+	// If the client is using subtypes, it is the client's responsibility to have
+	// already set the first label of the record name to the subtype being registered
+	for (i=0; i<NumSubTypes; i++)
+		{
+		domainname st;
+		AssignDomainName(&st, sr->SubTypes[i].resrec.name);
+		st.c[1+st.c[0]] = 0;			// Only want the first label, not the whole FQDN (particularly for mDNS_RenameAndReregisterService())
+		AppendDomainName(&st, type);
+		mDNS_SetupResourceRecord(&sr->SubTypes[i], mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeShared, artype, ServiceCallback, sr);
+		if (ConstructServiceName(&sr->SubTypes[i].namestorage, mDNSNULL, &st, domain) == mDNSNULL) return(mStatus_BadParamErr);
+		AssignDomainName(&sr->SubTypes[i].resrec.rdata->u.name, &sr->RR_SRV.namestorage);
+		sr->SubTypes[i].Additional1 = &sr->RR_SRV;
+		sr->SubTypes[i].Additional2 = &sr->RR_TXT;
+		}
+
+	// 3. Set up the SRV record rdata.
+	sr->RR_SRV.resrec.rdata->u.srv.priority = 0;
+	sr->RR_SRV.resrec.rdata->u.srv.weight   = 0;
+	sr->RR_SRV.resrec.rdata->u.srv.port     = port;
+
+	// Setting AutoTarget tells DNS that the target of this SRV is to be automatically kept in sync with our host name
+	if (host && host->c[0]) AssignDomainName(&sr->RR_SRV.resrec.rdata->u.srv.target, host);
+	else { sr->RR_SRV.AutoTarget = Target_AutoHost; sr->RR_SRV.resrec.rdata->u.srv.target.c[0] = '\0'; }
+
+	// 4. Set up the TXT record rdata,
+	// and set DependentOn because we're depending on the SRV record to find and resolve conflicts for us
+	// Note: uDNS registration code assumes that DependentOn points to the SRV record
+	if (txtinfo == mDNSNULL) sr->RR_TXT.resrec.rdlength = 0;
+	else if (txtinfo != sr->RR_TXT.resrec.rdata->u.txt.c)
+		{
+		sr->RR_TXT.resrec.rdlength = txtlen;
+		if (sr->RR_TXT.resrec.rdlength > sr->RR_TXT.resrec.rdata->MaxRDLength) return(mStatus_BadParamErr);
+		mDNSPlatformMemCopy(sr->RR_TXT.resrec.rdata->u.txt.c, txtinfo, txtlen);
+		}
+	sr->RR_TXT.DependentOn = &sr->RR_SRV;
+
+	mDNS_Lock(m);
+	// It is important that we register SRV first. uDNS assumes that SRV is registered first so
+	// that if the SRV cannot find a target, rest of the records that belong to this service
+	// will not be activated.
+	err = mDNS_Register_internal(m, &sr->RR_SRV);
+	// If we can't register the SRV record due to errors, bail out. It has not been inserted in
+	// any list and hence no need to deregister. We could probably do similar checks for other
+	// records below and bail out. For now, this seems to be sufficient to address rdar://9304275
+	if (err)
+		{
+		mDNS_Unlock(m);
+		return err;
+		}
+	if (!err) err = mDNS_Register_internal(m, &sr->RR_TXT);
+	// We register the RR_PTR last, because we want to be sure that in the event of a forced call to
+	// mDNS_StartExit, the RR_PTR will be the last one to be forcibly deregistered, since that is what triggers
+	// the mStatus_MemFree callback to ServiceCallback, which in turn passes on the mStatus_MemFree back to
+	// the client callback, which is then at liberty to free the ServiceRecordSet memory at will. We need to
+	// make sure we've deregistered all our records and done any other necessary cleanup before that happens.
+	if (!err) err = mDNS_Register_internal(m, &sr->RR_ADV);
+	for (i=0; i<NumSubTypes; i++) if (!err) err = mDNS_Register_internal(m, &sr->SubTypes[i]);
+	if (!err) err = mDNS_Register_internal(m, &sr->RR_PTR);
+
+	mDNS_Unlock(m);
+	
+	if (err) mDNS_DeregisterService(m, sr);
+	return(err);
+	}
+
+mDNSexport mStatus mDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr,
+	ExtraResourceRecord *extra, RData *rdata, mDNSu32 ttl,  mDNSu32 includeP2P)
+	{
+	ExtraResourceRecord **e;
+	mStatus status;
+	AuthRecType artype;
+	mDNSInterfaceID InterfaceID = sr->RR_PTR.resrec.InterfaceID;
+
+	if (InterfaceID == mDNSInterface_LocalOnly)
+		artype = AuthRecordLocalOnly;
+	if (InterfaceID == mDNSInterface_P2P)
+		artype = AuthRecordP2P;
+	else if ((InterfaceID == mDNSInterface_Any) && includeP2P)
+		artype = AuthRecordAnyIncludeP2P;
+	else
+		artype = AuthRecordAny;
+
+	extra->next = mDNSNULL;
+	mDNS_SetupResourceRecord(&extra->r, rdata, sr->RR_PTR.resrec.InterfaceID,
+		extra->r.resrec.rrtype, ttl, kDNSRecordTypeUnique, artype, ServiceCallback, sr);
+	AssignDomainName(&extra->r.namestorage, sr->RR_SRV.resrec.name);
+	
+	mDNS_Lock(m);
+	e = &sr->Extras;
+	while (*e) e = &(*e)->next;
+
+	if (ttl == 0) ttl = kStandardTTL;
+
+	extra->r.DependentOn = &sr->RR_SRV;
+
+	debugf("mDNS_AddRecordToService adding record to %##s %s %d",
+		extra->r.resrec.name->c, DNSTypeName(extra->r.resrec.rrtype), extra->r.resrec.rdlength);
+
+	status = mDNS_Register_internal(m, &extra->r);
+	if (status == mStatus_NoError) *e = extra;
+
+	mDNS_Unlock(m);
+	return(status);
+	}
+
+mDNSexport mStatus mDNS_RemoveRecordFromService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra,
+	mDNSRecordCallback MemFreeCallback, void *Context)
+	{
+	ExtraResourceRecord **e;
+	mStatus status;
+
+	mDNS_Lock(m);
+	e = &sr->Extras;
+	while (*e && *e != extra) e = &(*e)->next;
+	if (!*e)
+		{
+		debugf("mDNS_RemoveRecordFromService failed to remove record from %##s", extra->r.resrec.name->c);
+		status = mStatus_BadReferenceErr;
+		}
+	else
+		{
+		debugf("mDNS_RemoveRecordFromService removing record from %##s", extra->r.resrec.name->c);
+		extra->r.RecordCallback = MemFreeCallback;
+		extra->r.RecordContext  = Context;
+		*e = (*e)->next;
+		status = mDNS_Deregister_internal(m, &extra->r, mDNS_Dereg_normal);
+		}
+	mDNS_Unlock(m);
+	return(status);
+	}
+
+mDNSexport mStatus mDNS_RenameAndReregisterService(mDNS *const m, ServiceRecordSet *const sr, const domainlabel *newname)
+	{
+	// Note: Don't need to use mDNS_Lock(m) here, because this code is just using public routines
+	// mDNS_RegisterService() and mDNS_AddRecordToService(), which do the right locking internally.
+	domainlabel name1, name2;
+	domainname type, domain;
+	const domainname *host = sr->RR_SRV.AutoTarget ? mDNSNULL : &sr->RR_SRV.resrec.rdata->u.srv.target;
+	ExtraResourceRecord *extras = sr->Extras;
+	mStatus err;
+
+	DeconstructServiceName(sr->RR_SRV.resrec.name, &name1, &type, &domain);
+	if (!newname)
+		{
+		name2 = name1;
+		IncrementLabelSuffix(&name2, mDNStrue);
+		newname = &name2;
+		}
+	
+	if (SameDomainName(&domain, &localdomain))
+		debugf("%##s service renamed from \"%#s\" to \"%#s\"", type.c, name1.c, newname->c);
+	else debugf("%##s service (domain %##s) renamed from \"%#s\" to \"%#s\"",type.c, domain.c, name1.c, newname->c);
+
+	err = mDNS_RegisterService(m, sr, newname, &type, &domain,
+		host, sr->RR_SRV.resrec.rdata->u.srv.port, sr->RR_TXT.resrec.rdata->u.txt.c, sr->RR_TXT.resrec.rdlength,
+		sr->SubTypes, sr->NumSubTypes,
+		sr->RR_PTR.resrec.InterfaceID, sr->ServiceCallback, sr->ServiceContext, 0);
+
+	// mDNS_RegisterService() just reset sr->Extras to NULL.
+	// Fortunately we already grabbed ourselves a copy of this pointer (above), so we can now run
+	// through the old list of extra records, and re-add them to our freshly created service registration
+	while (!err && extras)
+		{
+		ExtraResourceRecord *e = extras;
+		extras = extras->next;
+		err = mDNS_AddRecordToService(m, sr, e, e->r.resrec.rdata, e->r.resrec.rroriginalttl, 0);
+		}
+	
+	return(err);
+	}
+
+// Note: mDNS_DeregisterService calls mDNS_Deregister_internal which can call a user callback,
+// which may change the record list and/or question list.
+// Any code walking either list must use the CurrentQuestion and/or CurrentRecord mechanism to protect against this.
+mDNSexport mStatus mDNS_DeregisterService_drt(mDNS *const m, ServiceRecordSet *sr, mDNS_Dereg_type drt)
+	{
+	// If port number is zero, that means this was actually registered using mDNS_RegisterNoSuchService()
+	if (mDNSIPPortIsZero(sr->RR_SRV.resrec.rdata->u.srv.port)) return(mDNS_DeregisterNoSuchService(m, &sr->RR_SRV));
+
+	if (sr->RR_PTR.resrec.RecordType == kDNSRecordTypeUnregistered)
+		{
+		debugf("Service set for %##s already deregistered", sr->RR_SRV.resrec.name->c);
+		return(mStatus_BadReferenceErr);
+		}
+	else if (sr->RR_PTR.resrec.RecordType == kDNSRecordTypeDeregistering)
+		{
+		LogInfo("Service set for %##s already in the process of deregistering", sr->RR_SRV.resrec.name->c);
+		// Avoid race condition:
+		// If a service gets a conflict, then we set the Conflict flag to tell us to generate
+		// an mStatus_NameConflict message when we get the mStatus_MemFree for our PTR record.
+		// If the client happens to deregister the service in the middle of that process, then
+		// we clear the flag back to the normal state, so that we deliver a plain mStatus_MemFree
+		// instead of incorrectly promoting it to mStatus_NameConflict.
+		// This race condition is exposed particularly when the conformance test generates
+		// a whole batch of simultaneous conflicts across a range of services all advertised
+		// using the same system default name, and if we don't take this precaution then
+		// we end up incrementing m->nicelabel multiple times instead of just once.
+		// <rdar://problem/4060169> Bug when auto-renaming Computer Name after name collision
+		sr->Conflict = mDNSfalse;
+		return(mStatus_NoError);
+		}
+	else
+		{
+		mDNSu32 i;
+		mStatus status;
+		ExtraResourceRecord *e;
+		mDNS_Lock(m);
+		e = sr->Extras;
+	
+		// We use mDNS_Dereg_repeat because, in the event of a collision, some or all of the
+		// SRV, TXT, or Extra records could have already been automatically deregistered, and that's okay
+		mDNS_Deregister_internal(m, &sr->RR_SRV, mDNS_Dereg_repeat);
+		mDNS_Deregister_internal(m, &sr->RR_TXT, mDNS_Dereg_repeat);
+		
+		mDNS_Deregister_internal(m, &sr->RR_ADV, drt);
+	
+		// We deregister all of the extra records, but we leave the sr->Extras list intact
+		// in case the client wants to do a RenameAndReregister and reinstate the registration
+		while (e)
+			{
+			mDNS_Deregister_internal(m, &e->r, mDNS_Dereg_repeat);
+			e = e->next;
+			}
+
+		for (i=0; i<sr->NumSubTypes; i++)
+			mDNS_Deregister_internal(m, &sr->SubTypes[i], drt);
+
+		status = mDNS_Deregister_internal(m, &sr->RR_PTR, drt);
+		mDNS_Unlock(m);
+		return(status);
+		}
+	}
+
+// Create a registration that asserts that no such service exists with this name.
+// This can be useful where there is a given function is available through several protocols.
+// For example, a printer called "Stuart's Printer" may implement printing via the "pdl-datastream" and "IPP"
+// protocols, but not via "LPR". In this case it would be prudent for the printer to assert the non-existence of an
+// "LPR" service called "Stuart's Printer". Without this precaution, another printer than offers only "LPR" printing
+// could inadvertently advertise its service under the same name "Stuart's Printer", which might be confusing for users.
+mDNSexport mStatus mDNS_RegisterNoSuchService(mDNS *const m, AuthRecord *const rr,
+	const domainlabel *const name, const domainname *const type, const domainname *const domain,
+	const domainname *const host,
+	const mDNSInterfaceID InterfaceID, mDNSRecordCallback Callback, void *Context, mDNSBool includeP2P)
+	{
+	AuthRecType artype;
+
+	if (InterfaceID == mDNSInterface_LocalOnly)
+		artype = AuthRecordLocalOnly;
+	else if (InterfaceID == mDNSInterface_P2P)
+		artype = AuthRecordP2P;
+	else if ((InterfaceID == mDNSInterface_Any) && includeP2P)
+		artype = AuthRecordAnyIncludeP2P;
+	else
+		artype = AuthRecordAny;
+
+	mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceID, kDNSType_SRV, kHostNameTTL, kDNSRecordTypeUnique, artype, Callback, Context);
+	if (ConstructServiceName(&rr->namestorage, name, type, domain) == mDNSNULL) return(mStatus_BadParamErr);
+	rr->resrec.rdata->u.srv.priority    = 0;
+	rr->resrec.rdata->u.srv.weight      = 0;
+	rr->resrec.rdata->u.srv.port        = zeroIPPort;
+	if (host && host->c[0]) AssignDomainName(&rr->resrec.rdata->u.srv.target, host);
+	else rr->AutoTarget = Target_AutoHost;
+	return(mDNS_Register(m, rr));
+	}
+
+mDNSexport mStatus mDNS_AdvertiseDomains(mDNS *const m, AuthRecord *rr,
+	mDNS_DomainType DomainType, const mDNSInterfaceID InterfaceID, char *domname)
+	{
+	AuthRecType artype;
+
+	if (InterfaceID == mDNSInterface_LocalOnly)
+		artype = AuthRecordLocalOnly;
+	else if (InterfaceID == mDNSInterface_P2P)
+		artype = AuthRecordP2P;
+	else
+		artype = AuthRecordAny;
+	mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeShared, artype, mDNSNULL, mDNSNULL);
+	if (!MakeDomainNameFromDNSNameString(&rr->namestorage, mDNS_DomainTypeNames[DomainType])) return(mStatus_BadParamErr);
+	if (!MakeDomainNameFromDNSNameString(&rr->resrec.rdata->u.name, domname))                 return(mStatus_BadParamErr);
+	return(mDNS_Register(m, rr));
+	}
+	
+mDNSlocal mDNSBool mDNS_IdUsedInResourceRecordsList(mDNS * const m, mDNSOpaque16 id)
+	{
+	AuthRecord *r;
+	for (r = m->ResourceRecords; r; r=r->next) if (mDNSSameOpaque16(id, r->updateid)) return mDNStrue;
+	return mDNSfalse;
+	}
+	
+mDNSlocal mDNSBool mDNS_IdUsedInQuestionsList(mDNS * const m, mDNSOpaque16 id)
+	{
+	DNSQuestion *q;
+	for (q = m->Questions; q; q=q->next) if (mDNSSameOpaque16(id, q->TargetQID)) return mDNStrue;
+	return mDNSfalse;
+	}
+	
+mDNSexport mDNSOpaque16 mDNS_NewMessageID(mDNS * const m)
+	{
+	mDNSOpaque16 id;
+	int i;
+
+	for (i=0; i<10; i++)
+		{
+		id = mDNSOpaque16fromIntVal(1 + (mDNSu16)mDNSRandom(0xFFFE));
+		if (!mDNS_IdUsedInResourceRecordsList(m, id) && !mDNS_IdUsedInQuestionsList(m, id)) break;
+		}
+		
+	debugf("mDNS_NewMessageID: %5d", mDNSVal16(id));
+
+	return id;
+	}
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - Sleep Proxy Server
+#endif
+
+mDNSlocal void RestartARPProbing(mDNS *const m, AuthRecord *const rr)
+	{
+	// If we see an ARP from a machine we think is sleeping, then either
+	// (i) the machine has woken, or
+	// (ii) it's just a stray old packet from before the machine slept
+	// To handle the second case, we reset ProbeCount, so we'll suppress our own answers for a while, to avoid
+	// generating ARP conflicts with a waking machine, and set rr->LastAPTime so we'll start probing again in 10 seconds.
+	// If the machine has just woken then we'll discard our records when we see the first new mDNS probe from that machine.
+	// If it was a stray old packet, then after 10 seconds we'll probe again and then start answering ARPs again. In this case we *do*
+	// need to send new ARP Announcements, because the owner's ARP broadcasts will have updated neighboring ARP caches, so we need to
+	// re-assert our (temporary) ownership of that IP address in order to receive subsequent packets addressed to that IPv4 address.
+	
+	rr->resrec.RecordType = kDNSRecordTypeUnique;
+	rr->ProbeCount        = DefaultProbeCountForTypeUnique;
+
+	// If we haven't started announcing yet (and we're not already in ten-second-delay mode) the machine is probably
+	// still going to sleep, so we just reset rr->ProbeCount so we'll continue probing until it stops responding.
+	// If we *have* started announcing, the machine is probably in the process of waking back up, so in that case
+	// we're more cautious and we wait ten seconds before probing it again. We do this because while waking from
+	// sleep, some network interfaces tend to lose or delay inbound packets, and without this delay, if the waking machine
+	// didn't answer our three probes within three seconds then we'd announce and cause it an unnecessary address conflict.
+	if (rr->AnnounceCount == InitialAnnounceCount && m->timenow - rr->LastAPTime >= 0)
+		InitializeLastAPTime(m, rr);
+	else
+		{
+		rr->AnnounceCount  = InitialAnnounceCount;
+		rr->ThisAPInterval = mDNSPlatformOneSecond;
+		rr->LastAPTime     = m->timenow + mDNSPlatformOneSecond * 9;	// Send first packet at rr->LastAPTime + rr->ThisAPInterval, i.e. 10 seconds from now
+		SetNextAnnounceProbeTime(m, rr);
+		}
+	}
+
+mDNSlocal void mDNSCoreReceiveRawARP(mDNS *const m, const ARP_EthIP *const arp, const mDNSInterfaceID InterfaceID)
+	{
+	static const mDNSOpaque16 ARP_op_request = { { 0, 1 } };
+	AuthRecord *rr;
+	NetworkInterfaceInfo *intf = FirstInterfaceForID(m, InterfaceID);
+	if (!intf) return;
+
+	mDNS_Lock(m);
+
+	// Pass 1:
+	// Process ARP Requests and Probes (but not Announcements), and generate an ARP Reply if necessary.
+	// We also process ARPs from our own kernel (and 'answer' them by injecting a local ARP table entry)
+	// We ignore ARP Announcements here -- Announcements are not questions, they're assertions, so we don't need to answer them.
+	// The times we might need to react to an ARP Announcement are:
+	// (i) as an indication that the host in question has not gone to sleep yet (so we should delay beginning to proxy for it) or
+	// (ii) if it's a conflicting Announcement from another host
+	// -- and we check for these in Pass 2 below.
+	if (mDNSSameOpaque16(arp->op, ARP_op_request) && !mDNSSameIPv4Address(arp->spa, arp->tpa))
+		{
+		for (rr = m->ResourceRecords; rr; rr=rr->next)
+			if (rr->resrec.InterfaceID == InterfaceID && rr->resrec.RecordType != kDNSRecordTypeDeregistering &&
+				rr->AddressProxy.type == mDNSAddrType_IPv4 && mDNSSameIPv4Address(rr->AddressProxy.ip.v4, arp->tpa))
+				{
+				static const char msg1[] = "ARP Req from owner -- re-probing";
+				static const char msg2[] = "Ignoring  ARP Request from      ";
+				static const char msg3[] = "Creating Local ARP Cache entry  ";
+				static const char msg4[] = "Answering ARP Request from      ";
+				const char *const msg = mDNSSameEthAddress(&arp->sha, &rr->WakeUp.IMAC) ? msg1 :
+										(rr->AnnounceCount == InitialAnnounceCount)     ? msg2 :
+										mDNSSameEthAddress(&arp->sha, &intf->MAC)       ? msg3 : msg4;
+				LogSPS("%-7s %s %.6a %.4a for %.4a -- H-MAC %.6a I-MAC %.6a %s",
+					intf->ifname, msg, &arp->sha, &arp->spa, &arp->tpa, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, rr));
+				if      (msg == msg1) RestartARPProbing(m, rr);
+				else if (msg == msg3) mDNSPlatformSetLocalAddressCacheEntry(m, &rr->AddressProxy, &rr->WakeUp.IMAC, InterfaceID);
+				else if (msg == msg4) SendARP(m, 2, rr, &arp->tpa, &arp->sha, &arp->spa, &arp->sha);
+				}
+		}
+
+	// Pass 2:
+	// For all types of ARP packet we check the Sender IP address to make sure it doesn't conflict with any AddressProxy record we're holding.
+	// (Strictly speaking we're only checking Announcement/Request/Reply packets, since ARP Probes have zero Sender IP address,
+	// so by definition (and by design) they can never conflict with any real (i.e. non-zero) IP address).
+	// We ignore ARPs we sent ourselves (Sender MAC address is our MAC address) because our own proxy ARPs do not constitute a conflict that we need to handle.
+	// If we see an apparently conflicting ARP, we check the sender hardware address:
+	//   If the sender hardware address is the original owner this is benign, so we just suppress our own proxy answering for a while longer.
+	//   If the sender hardware address is *not* the original owner, then this is a conflict, and we need to wake the sleeping machine to handle it.
+	if (mDNSSameEthAddress(&arp->sha, &intf->MAC))
+		debugf("ARP from self for %.4a", &arp->tpa);
+	else
+		{
+		if (!mDNSSameIPv4Address(arp->spa, zerov4Addr))
+			for (rr = m->ResourceRecords; rr; rr=rr->next)
+				if (rr->resrec.InterfaceID == InterfaceID && rr->resrec.RecordType != kDNSRecordTypeDeregistering &&
+					rr->AddressProxy.type == mDNSAddrType_IPv4 && mDNSSameIPv4Address(rr->AddressProxy.ip.v4, arp->spa))
+					{
+					RestartARPProbing(m, rr);
+					if (mDNSSameEthAddress(&arp->sha, &rr->WakeUp.IMAC))
+						LogSPS("%-7s ARP %s from owner %.6a %.4a for %-15.4a -- re-starting probing for %s", intf->ifname,
+							mDNSSameIPv4Address(arp->spa, arp->tpa) ? "Announcement " : mDNSSameOpaque16(arp->op, ARP_op_request) ? "Request      " : "Response     ",
+							&arp->sha, &arp->spa, &arp->tpa, ARDisplayString(m, rr));
+					else
+						{
+						LogMsg("%-7s Conflicting ARP from %.6a %.4a for %.4a -- waking H-MAC %.6a I-MAC %.6a %s", intf->ifname,
+							&arp->sha, &arp->spa, &arp->tpa, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, rr));
+						ScheduleWakeup(m, rr->resrec.InterfaceID, &rr->WakeUp.HMAC);
+						}
+					}
+		}
+
+	mDNS_Unlock(m);
+	}
+
+/*
+// Option 1 is Source Link Layer Address Option
+// Option 2 is Target Link Layer Address Option
+mDNSlocal const mDNSEthAddr *GetLinkLayerAddressOption(const IPv6NDP *const ndp, const mDNSu8 *const end, mDNSu8 op)
+	{
+	const mDNSu8 *options = (mDNSu8 *)(ndp+1);
+	while (options < end)
+		{
+		debugf("NDP Option %02X len %2d %d", options[0], options[1], end - options);
+		if (options[0] == op && options[1] == 1) return (const mDNSEthAddr*)(options+2);
+		options += options[1] * 8;
+		}
+	return mDNSNULL;
+	}
+*/
+
+mDNSlocal void mDNSCoreReceiveRawND(mDNS *const m, const mDNSEthAddr *const sha, const mDNSv6Addr *spa,
+	const IPv6NDP *const ndp, const mDNSu8 *const end, const mDNSInterfaceID InterfaceID)
+	{
+	AuthRecord *rr;
+	NetworkInterfaceInfo *intf = FirstInterfaceForID(m, InterfaceID);
+	if (!intf) return;
+
+	mDNS_Lock(m);
+
+	// Pass 1: Process Neighbor Solicitations, and generate a Neighbor Advertisement if necessary.
+	if (ndp->type == NDP_Sol)
+		{
+		//const mDNSEthAddr *const sha = GetLinkLayerAddressOption(ndp, end, NDP_SrcLL);
+		(void)end;
+		for (rr = m->ResourceRecords; rr; rr=rr->next)
+			if (rr->resrec.InterfaceID == InterfaceID && rr->resrec.RecordType != kDNSRecordTypeDeregistering &&
+				rr->AddressProxy.type == mDNSAddrType_IPv6 && mDNSSameIPv6Address(rr->AddressProxy.ip.v6, ndp->target))
+				{
+				static const char msg1[] = "NDP Req from owner -- re-probing";
+				static const char msg2[] = "Ignoring  NDP Request from      ";
+				static const char msg3[] = "Creating Local NDP Cache entry  ";
+				static const char msg4[] = "Answering NDP Request from      ";
+				static const char msg5[] = "Answering NDP Probe   from      ";
+				const char *const msg = sha && mDNSSameEthAddress(sha, &rr->WakeUp.IMAC) ? msg1 :
+										(rr->AnnounceCount == InitialAnnounceCount)      ? msg2 :
+										sha && mDNSSameEthAddress(sha, &intf->MAC)       ? msg3 :
+										spa && mDNSIPv6AddressIsZero(*spa)               ? msg4 : msg5;
+				LogSPS("%-7s %s %.6a %.16a for %.16a -- H-MAC %.6a I-MAC %.6a %s",
+					intf->ifname, msg, sha, spa, &ndp->target, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, rr));
+				if      (msg == msg1) RestartARPProbing(m, rr);
+				else if (msg == msg3)
+					{
+					if (!(m->KnownBugs & mDNS_KnownBug_LimitedIPv6))
+						mDNSPlatformSetLocalAddressCacheEntry(m, &rr->AddressProxy, &rr->WakeUp.IMAC, InterfaceID);
+					}
+				else if (msg == msg4) SendNDP(m, NDP_Adv, NDP_Solicited, rr, &ndp->target, mDNSNULL, spa,          sha             );
+				else if (msg == msg5) SendNDP(m, NDP_Adv, 0,             rr, &ndp->target, mDNSNULL, &AllHosts_v6, &AllHosts_v6_Eth);
+				}
+		}
+
+	// Pass 2: For all types of NDP packet we check the Sender IP address to make sure it doesn't conflict with any AddressProxy record we're holding.
+	if (mDNSSameEthAddress(sha, &intf->MAC))
+		debugf("NDP from self for %.16a", &ndp->target);
+	else
+		{
+		// For Neighbor Advertisements we check the Target address field, not the actual IPv6 source address.
+		// When a machine has both link-local and routable IPv6 addresses, it may send NDP packets making assertions
+		// about its routable IPv6 address, using its link-local address as the source address for all NDP packets.
+		// Hence it is the NDP target address we care about, not the actual packet source address.
+		if (ndp->type == NDP_Adv) spa = &ndp->target;
+		if (!mDNSSameIPv6Address(*spa, zerov6Addr))
+			for (rr = m->ResourceRecords; rr; rr=rr->next)
+				if (rr->resrec.InterfaceID == InterfaceID && rr->resrec.RecordType != kDNSRecordTypeDeregistering &&
+					rr->AddressProxy.type == mDNSAddrType_IPv6 && mDNSSameIPv6Address(rr->AddressProxy.ip.v6, *spa))
+					{
+					RestartARPProbing(m, rr);
+					if (mDNSSameEthAddress(sha, &rr->WakeUp.IMAC))
+						LogSPS("%-7s NDP %s from owner %.6a %.16a for %.16a -- re-starting probing for %s", intf->ifname,
+							ndp->type == NDP_Sol ? "Solicitation " : "Advertisement", sha, spa, &ndp->target, ARDisplayString(m, rr));
+					else
+						{
+						LogMsg("%-7s Conflicting NDP from %.6a %.16a for %.16a -- waking H-MAC %.6a I-MAC %.6a %s", intf->ifname,
+							sha, spa, &ndp->target, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, rr));
+						ScheduleWakeup(m, rr->resrec.InterfaceID, &rr->WakeUp.HMAC);
+						}
+					}
+		}
+
+	mDNS_Unlock(m);
+	}
+
+mDNSlocal void mDNSCoreReceiveRawTransportPacket(mDNS *const m, const mDNSEthAddr *const sha, const mDNSAddr *const src, const mDNSAddr *const dst, const mDNSu8 protocol,
+	const mDNSu8 *const p, const TransportLayerPacket *const t, const mDNSu8 *const end, const mDNSInterfaceID InterfaceID, const mDNSu16 len)
+	{
+	const mDNSIPPort port = (protocol == 0x06) ? t->tcp.dst : (protocol == 0x11) ? t->udp.dst : zeroIPPort;
+	mDNSBool wake = mDNSfalse;
+
+	switch (protocol)
+		{
+		#define XX wake ? "Received" : "Ignoring", end-p
+		case 0x01:	LogSPS("Ignoring %d-byte ICMP from %#a to %#a", end-p, src, dst);
+					break;
+
+		case 0x06:	{
+					#define SSH_AsNumber 22
+					static const mDNSIPPort SSH = { { SSH_AsNumber >> 8, SSH_AsNumber & 0xFF } };
+
+					// Plan to wake if
+					// (a) RST is not set, AND
+					// (b) packet is SYN, SYN+FIN, or plain data packet (no SYN or FIN). We won't wake for FIN alone.
+					wake = (!(t->tcp.flags & 4) && (t->tcp.flags & 3) != 1);
+
+					// For now, to reduce spurious wakeups, we wake only for TCP SYN,
+					// except for ssh connections, where we'll wake for plain data packets too
+					if (!mDNSSameIPPort(port, SSH) && !(t->tcp.flags & 2)) wake = mDNSfalse;
+
+					LogSPS("%s %d-byte TCP from %#a:%d to %#a:%d%s%s%s", XX,
+						src, mDNSVal16(t->tcp.src), dst, mDNSVal16(port),
+						(t->tcp.flags & 2) ? " SYN" : "",
+						(t->tcp.flags & 1) ? " FIN" : "",
+						(t->tcp.flags & 4) ? " RST" : "");
+					}
+					break;
+
+		case 0x11:	{
+					#define ARD_AsNumber 3283
+					static const mDNSIPPort ARD = { { ARD_AsNumber >> 8, ARD_AsNumber & 0xFF } };
+					const mDNSu16 udplen = (mDNSu16)((mDNSu16)t->bytes[4] << 8 | t->bytes[5]);		// Length *including* 8-byte UDP header
+					if (udplen >= sizeof(UDPHeader))
+						{
+						const mDNSu16 datalen = udplen - sizeof(UDPHeader);
+						wake = mDNStrue;
+
+						// For Back to My Mac UDP port 4500 (IPSEC) packets, we do some special handling
+						if (mDNSSameIPPort(port, IPSECPort))
+							{
+							// Specifically ignore NAT keepalive packets
+							if (datalen == 1 && end >= &t->bytes[9] && t->bytes[8] == 0xFF) wake = mDNSfalse;
+							else
+								{
+								// Skip over the Non-ESP Marker if present
+								const mDNSBool NonESP = (end >= &t->bytes[12] && t->bytes[8] == 0 && t->bytes[9] == 0 && t->bytes[10] == 0 && t->bytes[11] == 0);
+								const IKEHeader *const ike    = (IKEHeader *)(t + (NonESP ? 12 : 8));
+								const mDNSu16          ikelen = datalen - (NonESP ? 4 : 0);
+								if (ikelen >= sizeof(IKEHeader) && end >= ((mDNSu8 *)ike) + sizeof(IKEHeader))
+									if ((ike->Version & 0x10) == 0x10)
+										{
+										// ExchangeType ==  5 means 'Informational' <http://www.ietf.org/rfc/rfc2408.txt>
+										// ExchangeType == 34 means 'IKE_SA_INIT'   <http://www.iana.org/assignments/ikev2-parameters>
+										if (ike->ExchangeType == 5 || ike->ExchangeType == 34) wake = mDNSfalse;
+										LogSPS("%s %d-byte IKE ExchangeType %d", XX, ike->ExchangeType);
+										}
+								}
+							}
+
+						// For now, because we haven't yet worked out a clean elegant way to do this, we just special-case the
+						// Apple Remote Desktop port number -- we ignore all packets to UDP 3283 (the "Net Assistant" port),
+						// except for Apple Remote Desktop's explicit manual wakeup packet, which looks like this:
+						// UDP header (8 bytes)
+						// Payload: 13 88 00 6a 41 4e 41 20 (8 bytes) ffffffffffff (6 bytes) 16xMAC (96 bytes) = 110 bytes total
+						if (mDNSSameIPPort(port, ARD)) wake = (datalen >= 110 && end >= &t->bytes[10] && t->bytes[8] == 0x13 && t->bytes[9] == 0x88);
+
+						LogSPS("%s %d-byte UDP from %#a:%d to %#a:%d", XX, src, mDNSVal16(t->udp.src), dst, mDNSVal16(port));
+						}
+					}
+					break;
+
+		case 0x3A:	if (&t->bytes[len] <= end)
+						{
+						mDNSu16 checksum = IPv6CheckSum(&src->ip.v6, &dst->ip.v6, protocol, t->bytes, len);
+						if (!checksum) mDNSCoreReceiveRawND(m, sha, &src->ip.v6, &t->ndp, &t->bytes[len], InterfaceID);
+						else LogInfo("IPv6CheckSum bad %04X %02X%02X from %#a to %#a", checksum, t->bytes[2], t->bytes[3], src, dst);
+						}
+					break;
+
+		default:	LogSPS("Ignoring %d-byte IP packet unknown protocol %d from %#a to %#a", end-p, protocol, src, dst);
+					break;
+		}
+
+	if (wake)
+		{
+		AuthRecord *rr, *r2;
+
+		mDNS_Lock(m);
+		for (rr = m->ResourceRecords; rr; rr=rr->next)
+			if (rr->resrec.InterfaceID == InterfaceID &&
+				rr->resrec.RecordType != kDNSRecordTypeDeregistering &&
+				rr->AddressProxy.type && mDNSSameAddress(&rr->AddressProxy, dst))
+				{
+				const mDNSu8 *const tp = (protocol == 6) ? (const mDNSu8 *)"\x4_tcp" : (const mDNSu8 *)"\x4_udp";
+				for (r2 = m->ResourceRecords; r2; r2=r2->next)
+					if (r2->resrec.InterfaceID == InterfaceID && mDNSSameEthAddress(&r2->WakeUp.HMAC, &rr->WakeUp.HMAC) &&
+						r2->resrec.RecordType != kDNSRecordTypeDeregistering &&
+						r2->resrec.rrtype == kDNSType_SRV && mDNSSameIPPort(r2->resrec.rdata->u.srv.port, port) &&
+						SameDomainLabel(ThirdLabel(r2->resrec.name)->c, tp))
+						break;
+				if (!r2 && mDNSSameIPPort(port, IPSECPort)) r2 = rr;	// So that we wake for BTMM IPSEC packets, even without a matching SRV record
+				if (r2)
+					{
+					LogMsg("Waking host at %s %#a H-MAC %.6a I-MAC %.6a for %s",
+						InterfaceNameForID(m, rr->resrec.InterfaceID), dst, &rr->WakeUp.HMAC, &rr->WakeUp.IMAC, ARDisplayString(m, r2));
+					ScheduleWakeup(m, rr->resrec.InterfaceID, &rr->WakeUp.HMAC);
+					}
+				else
+					LogSPS("Sleeping host at %s %#a %.6a has no service on %#s %d",
+						InterfaceNameForID(m, rr->resrec.InterfaceID), dst, &rr->WakeUp.HMAC, tp, mDNSVal16(port));
+				}
+		mDNS_Unlock(m);
+		}
+	}
+
+mDNSexport void mDNSCoreReceiveRawPacket(mDNS *const m, const mDNSu8 *const p, const mDNSu8 *const end, const mDNSInterfaceID InterfaceID)
+	{
+	static const mDNSOpaque16 Ethertype_ARP  = { { 0x08, 0x06 } };	// Ethertype 0x0806 = ARP
+	static const mDNSOpaque16 Ethertype_IPv4 = { { 0x08, 0x00 } };	// Ethertype 0x0800 = IPv4
+	static const mDNSOpaque16 Ethertype_IPv6 = { { 0x86, 0xDD } };	// Ethertype 0x86DD = IPv6
+	static const mDNSOpaque16 ARP_hrd_eth    = { { 0x00, 0x01 } };	// Hardware address space (Ethernet = 1)
+	static const mDNSOpaque16 ARP_pro_ip     = { { 0x08, 0x00 } };	// Protocol address space (IP = 0x0800)
+
+	// Note: BPF guarantees that the NETWORK LAYER header will be word aligned, not the link-layer header.
+	// In other words, we can safely assume that pkt below (ARP, IPv4 or IPv6) is properly word aligned,
+	// but if pkt is 4-byte aligned, that necessarily means that eth CANNOT also be 4-byte aligned
+	// since it points to a an address 14 bytes before pkt.
+	const EthernetHeader     *const eth = (const EthernetHeader *)p;
+	const NetworkLayerPacket *const pkt = (const NetworkLayerPacket *)(eth+1);
+	mDNSAddr src, dst;
+	#define RequiredCapLen(P) ((P)==0x01 ? 4 : (P)==0x06 ? 20 : (P)==0x11 ? 8 : (P)==0x3A ? 24 : 0)
+
+	// Is ARP? Length must be at least 14 + 28 = 42 bytes
+	if (end >= p+42 && mDNSSameOpaque16(eth->ethertype, Ethertype_ARP) && mDNSSameOpaque16(pkt->arp.hrd, ARP_hrd_eth) && mDNSSameOpaque16(pkt->arp.pro, ARP_pro_ip))
+		mDNSCoreReceiveRawARP(m, &pkt->arp, InterfaceID);
+	// Is IPv4 with zero fragmentation offset? Length must be at least 14 + 20 = 34 bytes
+	else if (end >= p+34 && mDNSSameOpaque16(eth->ethertype, Ethertype_IPv4) && (pkt->v4.flagsfrags.b[0] & 0x1F) == 0 && pkt->v4.flagsfrags.b[1] == 0)
+		{
+		const mDNSu8 *const trans = p + 14 + (pkt->v4.vlen & 0xF) * 4;
+		debugf("Got IPv4 %02X from %.4a to %.4a", pkt->v4.protocol, &pkt->v4.src, &pkt->v4.dst);
+		src.type = mDNSAddrType_IPv4; src.ip.v4 = pkt->v4.src;
+		dst.type = mDNSAddrType_IPv4; dst.ip.v4 = pkt->v4.dst;
+		if (end >= trans + RequiredCapLen(pkt->v4.protocol))
+			mDNSCoreReceiveRawTransportPacket(m, &eth->src, &src, &dst, pkt->v4.protocol, p, (TransportLayerPacket*)trans, end, InterfaceID, 0);
+		}
+	// Is IPv6? Length must be at least 14 + 28 = 42 bytes
+	else if (end >= p+54 && mDNSSameOpaque16(eth->ethertype, Ethertype_IPv6))
+		{
+		const mDNSu8 *const trans = p + 54;
+		debugf("Got IPv6  %02X from %.16a to %.16a", pkt->v6.pro, &pkt->v6.src, &pkt->v6.dst);
+		src.type = mDNSAddrType_IPv6; src.ip.v6 = pkt->v6.src;
+		dst.type = mDNSAddrType_IPv6; dst.ip.v6 = pkt->v6.dst;
+		if (end >= trans + RequiredCapLen(pkt->v6.pro))
+			mDNSCoreReceiveRawTransportPacket(m, &eth->src, &src, &dst, pkt->v6.pro, p, (TransportLayerPacket*)trans, end, InterfaceID,
+				(mDNSu16)pkt->bytes[4] << 8 | pkt->bytes[5]);
+		}
+	}
+
+mDNSlocal void ConstructSleepProxyServerName(mDNS *const m, domainlabel *name)
+	{
+	name->c[0] = (mDNSu8)mDNS_snprintf((char*)name->c+1, 62, "%d-%d-%d-%d %#s",
+		m->SPSType, m->SPSPortability, m->SPSMarginalPower, m->SPSTotalPower, &m->nicelabel);
+	}
+
+mDNSlocal void SleepProxyServerCallback(mDNS *const m, ServiceRecordSet *const srs, mStatus result)
+	{
+	if (result == mStatus_NameConflict)
+		mDNS_RenameAndReregisterService(m, srs, mDNSNULL);
+	else if (result == mStatus_MemFree)
+		{
+		if (m->SleepState)
+			m->SPSState = 3;
+		else
+			{
+			m->SPSState = (mDNSu8)(m->SPSSocket != mDNSNULL);
+			if (m->SPSState)
+				{
+				domainlabel name;
+				ConstructSleepProxyServerName(m, &name);
+				mDNS_RegisterService(m, srs,
+					&name, &SleepProxyServiceType, &localdomain,
+					mDNSNULL, m->SPSSocket->port,				// Host, port
+					(mDNSu8 *)"", 1,							// TXT data, length
+					mDNSNULL, 0,								// Subtypes (none)
+					mDNSInterface_Any,							// Interface ID
+					SleepProxyServerCallback, mDNSNULL, 0);		// Callback, context, flags
+				}
+			LogSPS("Sleep Proxy Server %#s %s", srs->RR_SRV.resrec.name->c, m->SPSState ? "started" : "stopped");
+			}
+		}
+	}
+
+// Called with lock held
+mDNSexport void mDNSCoreBeSleepProxyServer_internal(mDNS *const m, mDNSu8 sps, mDNSu8 port, mDNSu8 marginalpower, mDNSu8 totpower)
+	{
+	// This routine uses mDNS_DeregisterService and calls SleepProxyServerCallback, so we execute in user callback context
+	mDNS_DropLockBeforeCallback();
+
+	// If turning off SPS, close our socket
+	// (Do this first, BEFORE calling mDNS_DeregisterService below)
+	if (!sps && m->SPSSocket) { mDNSPlatformUDPClose(m->SPSSocket); m->SPSSocket = mDNSNULL; }
+
+	// If turning off, or changing type, deregister old name
+	if (m->SPSState == 1 && sps != m->SPSType)
+		{ m->SPSState = 2; mDNS_DeregisterService_drt(m, &m->SPSRecords, sps ? mDNS_Dereg_rapid : mDNS_Dereg_normal); }
+
+	// Record our new SPS parameters
+	m->SPSType          = sps;
+	m->SPSPortability   = port;
+	m->SPSMarginalPower = marginalpower;
+	m->SPSTotalPower    = totpower;
+
+	// If turning on, open socket and advertise service
+	if (sps)
+		{
+		if (!m->SPSSocket)
+			{
+			m->SPSSocket = mDNSPlatformUDPSocket(m, zeroIPPort);
+			if (!m->SPSSocket) { LogMsg("mDNSCoreBeSleepProxyServer: Failed to allocate SPSSocket"); goto fail; }
+			}
+		if (m->SPSState == 0) SleepProxyServerCallback(m, &m->SPSRecords, mStatus_MemFree);
+		}
+	else if (m->SPSState)
+		{
+		LogSPS("mDNSCoreBeSleepProxyServer turning off from state %d; will wake clients", m->SPSState);
+		m->NextScheduledSPS = m->timenow;
+		}
+fail:
+	mDNS_ReclaimLockAfterCallback();
+	}
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - Startup and Shutdown
+#endif
+
+mDNSlocal void mDNS_GrowCache_internal(mDNS *const m, CacheEntity *storage, mDNSu32 numrecords)
+	{
+	if (storage && numrecords)
+		{
+		mDNSu32 i;
+		debugf("Adding cache storage for %d more records (%d bytes)", numrecords, numrecords*sizeof(CacheEntity));
+		for (i=0; i<numrecords; i++) storage[i].next = &storage[i+1];
+		storage[numrecords-1].next = m->rrcache_free;
+		m->rrcache_free = storage;
+		m->rrcache_size += numrecords;
+		}
+	}
+
+mDNSexport void mDNS_GrowCache(mDNS *const m, CacheEntity *storage, mDNSu32 numrecords)
+	{
+	mDNS_Lock(m);
+	mDNS_GrowCache_internal(m, storage, numrecords);
+	mDNS_Unlock(m);
+	}
+
+mDNSexport mStatus mDNS_Init(mDNS *const m, mDNS_PlatformSupport *const p,
+	CacheEntity *rrcachestorage, mDNSu32 rrcachesize,
+	mDNSBool AdvertiseLocalAddresses, mDNSCallback *Callback, void *Context)
+	{
+	mDNSu32 slot;
+	mDNSs32 timenow;
+	mStatus result;
+	
+	if (!rrcachestorage) rrcachesize = 0;
+	
+	m->p                             = p;
+	m->KnownBugs                     = 0;
+	m->CanReceiveUnicastOn5353       = mDNSfalse; // Assume we can't receive unicasts on 5353, unless platform layer tells us otherwise
+	m->AdvertiseLocalAddresses       = AdvertiseLocalAddresses;
+	m->DivertMulticastAdvertisements = mDNSfalse;
+	m->mDNSPlatformStatus            = mStatus_Waiting;
+	m->UnicastPort4                  = zeroIPPort;
+	m->UnicastPort6                  = zeroIPPort;
+	m->PrimaryMAC                    = zeroEthAddr;
+	m->MainCallback                  = Callback;
+	m->MainContext                   = Context;
+	m->rec.r.resrec.RecordType       = 0;
+
+	// For debugging: To catch and report locking failures
+	m->mDNS_busy               = 0;
+	m->mDNS_reentrancy         = 0;
+	m->ShutdownTime            = 0;
+	m->lock_rrcache            = 0;
+	m->lock_Questions          = 0;
+	m->lock_Records            = 0;
+
+	// Task Scheduling variables
+	result = mDNSPlatformTimeInit();
+	if (result != mStatus_NoError) return(result);
+	m->timenow_adjust = (mDNSs32)mDNSRandom(0xFFFFFFFF);
+	timenow = mDNS_TimeNow_NoLock(m);
+
+	m->timenow                 = 0;		// MUST only be set within mDNS_Lock/mDNS_Unlock section
+	m->timenow_last            = timenow;
+	m->NextScheduledEvent      = timenow;
+	m->SuppressSending         = timenow;
+	m->NextCacheCheck          = timenow + 0x78000000;
+	m->NextScheduledQuery      = timenow + 0x78000000;
+	m->NextScheduledProbe      = timenow + 0x78000000;
+	m->NextScheduledResponse   = timenow + 0x78000000;
+	m->NextScheduledNATOp      = timenow + 0x78000000;
+	m->NextScheduledSPS        = timenow + 0x78000000;
+	m->NextScheduledStopTime   = timenow + 0x78000000;
+	m->RandomQueryDelay        = 0;
+	m->RandomReconfirmDelay    = 0;
+	m->PktNum                  = 0;
+	m->LocalRemoveEvents       = mDNSfalse;
+	m->SleepState              = SleepState_Awake;
+	m->SleepSeqNum             = 0;
+	m->SystemWakeOnLANEnabled  = mDNSfalse;
+	m->AnnounceOwner           = NonZeroTime(timenow + 60 * mDNSPlatformOneSecond);
+	m->DelaySleep              = 0;
+	m->SleepLimit              = 0;
+
+	// These fields only required for mDNS Searcher...
+	m->Questions               = mDNSNULL;
+	m->NewQuestions            = mDNSNULL;
+	m->CurrentQuestion         = mDNSNULL;
+	m->LocalOnlyQuestions      = mDNSNULL;
+	m->NewLocalOnlyQuestions   = mDNSNULL;
+	m->RestartQuestion	       = mDNSNULL;
+	m->rrcache_size            = 0;
+	m->rrcache_totalused       = 0;
+	m->rrcache_active          = 0;
+	m->rrcache_report          = 10;
+	m->rrcache_free            = mDNSNULL;
+
+	for (slot = 0; slot < CACHE_HASH_SLOTS; slot++)
+		{
+		m->rrcache_hash[slot]      = mDNSNULL;
+		m->rrcache_nextcheck[slot] = timenow + 0x78000000;;
+		}
+
+	mDNS_GrowCache_internal(m, rrcachestorage, rrcachesize);
+	m->rrauth.rrauth_free            = mDNSNULL;
+
+	for (slot = 0; slot < AUTH_HASH_SLOTS; slot++)
+		m->rrauth.rrauth_hash[slot] = mDNSNULL;
+
+	// Fields below only required for mDNS Responder...
+	m->hostlabel.c[0]          = 0;
+	m->nicelabel.c[0]          = 0;
+	m->MulticastHostname.c[0]  = 0;
+	m->HIHardware.c[0]         = 0;
+	m->HISoftware.c[0]         = 0;
+	m->ResourceRecords         = mDNSNULL;
+	m->DuplicateRecords        = mDNSNULL;
+	m->NewLocalRecords         = mDNSNULL;
+	m->NewLocalOnlyRecords     = mDNSfalse;
+	m->CurrentRecord           = mDNSNULL;
+	m->HostInterfaces          = mDNSNULL;
+	m->ProbeFailTime           = 0;
+	m->NumFailedProbes         = 0;
+	m->SuppressProbes          = 0;
+
+#ifndef UNICAST_DISABLED
+	m->NextuDNSEvent            = timenow + 0x78000000;
+	m->NextSRVUpdate            = timenow + 0x78000000;
+
+	m->DNSServers               = mDNSNULL;
+
+	m->Router                   = zeroAddr;
+	m->AdvertisedV4             = zeroAddr;
+	m->AdvertisedV6             = zeroAddr;
+
+	m->AuthInfoList             = mDNSNULL;
+
+	m->ReverseMap.ThisQInterval = -1;
+	m->StaticHostname.c[0]      = 0;
+	m->FQDN.c[0]                = 0;
+	m->Hostnames                = mDNSNULL;
+	m->AutoTunnelHostAddr.b[0]  = 0;
+	m->AutoTunnelHostAddrActive = mDNSfalse;
+	m->AutoTunnelLabel.c[0]     = 0;
+
+	m->StartWABQueries          = mDNSfalse;
+	m->RegisterAutoTunnel6      = mDNStrue;
+
+	// NAT traversal fields
+	m->NATTraversals            = mDNSNULL;
+	m->CurrentNATTraversal      = mDNSNULL;
+	m->retryIntervalGetAddr     = 0;	// delta between time sent and retry
+	m->retryGetAddr             = timenow + 0x78000000;	// absolute time when we retry
+	m->ExternalAddress          = zerov4Addr;
+
+	m->NATMcastRecvskt          = mDNSNULL;
+	m->LastNATupseconds         = 0;
+	m->LastNATReplyLocalTime    = timenow;
+	m->LastNATMapResultCode     = NATErr_None;
+
+	m->UPnPInterfaceID          = 0;
+	m->SSDPSocket               = mDNSNULL;
+	m->SSDPWANPPPConnection     = mDNSfalse;
+	m->UPnPRouterPort           = zeroIPPort;
+	m->UPnPSOAPPort             = zeroIPPort;
+	m->UPnPRouterURL            = mDNSNULL;
+	m->UPnPWANPPPConnection     = mDNSfalse;
+	m->UPnPSOAPURL              = mDNSNULL;
+	m->UPnPRouterAddressString  = mDNSNULL;
+	m->UPnPSOAPAddressString    = mDNSNULL;
+	m->SPSType                  = 0;
+	m->SPSPortability           = 0;
+	m->SPSMarginalPower         = 0;
+	m->SPSTotalPower            = 0;
+	m->SPSState                 = 0;
+	m->SPSProxyListChanged      = mDNSNULL;
+	m->SPSSocket                = mDNSNULL;
+	m->SPSBrowseCallback        = mDNSNULL;
+	m->ProxyRecords             = 0;
+
+#endif
+
+#if APPLE_OSX_mDNSResponder
+	m->TunnelClients            = mDNSNULL;
+
+#if ! NO_WCF
+	CHECK_WCF_FUNCTION(WCFConnectionNew)
+		{
+		m->WCF = WCFConnectionNew();
+		if (!m->WCF) { LogMsg("WCFConnectionNew failed"); return -1; }
+		}
+#endif
+
+#endif
+
+	result = mDNSPlatformInit(m);
+
+#ifndef UNICAST_DISABLED
+	// It's better to do this *after* the platform layer has set up the
+	// interface list and security credentials
+	uDNS_SetupDNSConfig(m);						// Get initial DNS configuration
+#endif
+
+	return(result);
+	}
+
+mDNSexport void mDNS_ConfigChanged(mDNS *const m)
+	{
+	if (m->SPSState == 1)
+		{
+		domainlabel name, newname;
+		domainname type, domain;
+		DeconstructServiceName(m->SPSRecords.RR_SRV.resrec.name, &name, &type, &domain);
+		ConstructSleepProxyServerName(m, &newname);
+		if (!SameDomainLabelCS(name.c, newname.c))
+			{
+			LogSPS("Renaming SPS from “%#s” to “%#s”", name.c, newname.c);
+			// When SleepProxyServerCallback gets the mStatus_MemFree message,
+			// it will reregister the service under the new name
+			m->SPSState = 2;
+			mDNS_DeregisterService_drt(m, &m->SPSRecords, mDNS_Dereg_rapid);
+			}
+		}
+	
+	if (m->MainCallback)
+		m->MainCallback(m, mStatus_ConfigChanged);
+	}
+
+mDNSlocal void DynDNSHostNameCallback(mDNS *const m, AuthRecord *const rr, mStatus result)
+	{
+	(void)m;	// unused
+	debugf("NameStatusCallback: result %d for registration of name %##s", result, rr->resrec.name->c);
+	mDNSPlatformDynDNSHostNameStatusChanged(rr->resrec.name, result);
+	}
+
+mDNSlocal void PurgeOrReconfirmCacheRecord(mDNS *const m, CacheRecord *cr, const DNSServer * const ptr, mDNSBool lameduck)
+	{
+	mDNSBool purge = cr->resrec.RecordType == kDNSRecordTypePacketNegative ||
+					 cr->resrec.rrtype     == kDNSType_A ||
+					 cr->resrec.rrtype     == kDNSType_AAAA ||
+					 cr->resrec.rrtype     == kDNSType_SRV;
+
+	(void) lameduck;
+	(void) ptr;
+	debugf("PurgeOrReconfirmCacheRecord: %s cache record due to %s server %p %#a:%d (%##s): %s",
+		purge    ? "purging"   : "reconfirming",
+		lameduck ? "lame duck" : "new",
+		ptr, &ptr->addr, mDNSVal16(ptr->port), ptr->domain.c, CRDisplayString(m, cr));
+
+	if (purge)
+		{
+		LogInfo("PurgeorReconfirmCacheRecord: Purging Resourcerecord %s, RecordType %x", CRDisplayString(m, cr), cr->resrec.RecordType);
+		mDNS_PurgeCacheResourceRecord(m, cr);
+		}
+	else
+		{
+		LogInfo("PurgeorReconfirmCacheRecord: Reconfirming Resourcerecord %s, RecordType %x", CRDisplayString(m, cr), cr->resrec.RecordType);
+		mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForNoAnswer);
+		}
+	}
+
+mDNSlocal void mDNS_PurgeBeforeResolve(mDNS *const m, DNSQuestion *q)
+	{
+	const mDNSu32 slot = HashSlot(&q->qname);
+	CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname);
+	CacheRecord *rp;
+
+	for (rp = cg ? cg->members : mDNSNULL; rp; rp = rp->next)
+		{
+		if (SameNameRecordAnswersQuestion(&rp->resrec, q))
+			{
+			LogInfo("mDNS_PurgeBeforeResolve: Flushing %s", CRDisplayString(m, rp));
+			mDNS_PurgeCacheResourceRecord(m, rp);
+			}
+		}
+	}
+
+mDNSlocal void CacheRecordResetDNSServer(mDNS *const m, DNSQuestion *q, DNSServer *new)
+	{
+	const mDNSu32 slot = HashSlot(&q->qname);
+	CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname);
+	CacheRecord *rp;
+	mDNSBool found = mDNSfalse;
+	mDNSBool foundNew = mDNSfalse;
+	DNSServer *old = q->qDNSServer;
+	mDNSBool newQuestion = IsQuestionNew(m, q);
+	DNSQuestion *qptr;
+
+	// This function is called when the DNSServer is updated to the new question. There may already be
+	// some cache entries matching the old DNSServer and/or new DNSServer. There are four cases. In the
+	// following table, "Yes" denotes that a cache entry was found for old/new DNSServer.
+	//
+	// 					old DNSServer		new DNSServer
+	//
+	//	Case 1				Yes					Yes
+	//  Case 2				No					Yes
+	//  Case 3				Yes					No
+	//  Case 4				No					No
+	//
+	// Case 1: There are cache entries for both old and new DNSServer. We handle this case by simply
+	//		   expiring the old Cache entries, deliver a RMV event (if an ADD event was delivered before)
+	//		   followed by the ADD event of the cache entries corresponding to the new server. This
+	//		   case happens when we pick a DNSServer, issue a query and get a valid response and create
+	//		   cache entries after which it stops responding. Another query (non-duplicate) picks a different
+	//	       DNSServer and creates identical cache entries (perhaps through records in Additional records).
+	//		   Now if the first one expires and tries to pick the new DNSServer (the original DNSServer
+	//		   is not responding) we will find cache entries corresponding to both DNSServers.
+	//
+	// Case 2: There are no cache entries for the old DNSServer but there are some for the new DNSServer.
+	//		   This means we should deliver an ADD event. Normally ADD events are delivered by
+	//		   AnswerNewQuestion if it is a new question. So, we check to see if it is a new question
+	//		   and if so, leave it to AnswerNewQuestion to deliver it. Otherwise, we use
+	//		   AnswerQuestionsForDNSServerChanges to deliver the ADD event. This case happens when a
+	//		   question picks a DNS server for which AnswerNewQuestion could not deliver an answer even
+	//         though there were potential cache entries but DNSServer did not match. Now when we
+	//         pick a new DNSServer, those cache entries may answer this question.
+	//
+	// Case 3: There are the cache entries for the old DNSServer but none for the new. We just move
+	//		   the old cache entries to point to the new DNSServer and the caller is expected to
+	//		   do a purge or reconfirm to delete or validate the RDATA. We don't need to do anything
+	//		   special for delivering ADD events, as it should have been done/will be done by
+	//		   AnswerNewQuestion. This case happens when we picked a DNSServer, sent the query and
+	//		   got a response and the cache is expired now and we are reissuing the question but the
+	//		   original DNSServer does not respond.
+	//
+	// Case 4: There are no cache entries either for the old or for the new DNSServer. There is nothing
+	//		   much we can do here.
+	//
+	// Case 2 and 3 are the most common while case 4 is possible when no DNSServers are working. Case 1
+	// is relatively less likely to happen in practice
+
+	// Temporarily set the DNSServer to look for the matching records for the new DNSServer.
+	q->qDNSServer = new;
+	for (rp = cg ? cg->members : mDNSNULL; rp; rp = rp->next)
+		{
+		if (SameNameRecordAnswersQuestion(&rp->resrec, q))
+			{
+			LogInfo("CacheRecordResetDNSServer: Found cache record %##s for new DNSServer address: %#a", rp->resrec.name->c,
+				(rp->resrec.rDNSServer != mDNSNULL ?  &rp->resrec.rDNSServer->addr : mDNSNULL));
+			foundNew = mDNStrue;
+			break;
+			}
+		}
+	q->qDNSServer = old;
+
+	for (rp = cg ? cg->members : mDNSNULL; rp; rp = rp->next)
+		{
+		if (SameNameRecordAnswersQuestion(&rp->resrec, q))
+			{
+			// Case1
+			found = mDNStrue;
+			if (foundNew)
+				{
+				LogInfo("CacheRecordResetDNSServer: Flushing Resourcerecord %##s, before:%#a, after:%#a", rp->resrec.name->c,
+					(rp->resrec.rDNSServer != mDNSNULL ?  &rp->resrec.rDNSServer->addr : mDNSNULL),
+					(new != mDNSNULL ?  &new->addr : mDNSNULL));
+				mDNS_PurgeCacheResourceRecord(m, rp);
+				if (newQuestion)
+					{
+					// "q" is not a duplicate question. If it is a newQuestion, then the CRActiveQuestion can't be
+					// possibly set as it is set only when we deliver the ADD event to the question.
+					if (rp->CRActiveQuestion != mDNSNULL)
+						{
+						LogMsg("CacheRecordResetDNSServer: ERROR!!: CRActiveQuestion %p set, current question %p, name %##s", rp->CRActiveQuestion, q, q->qname.c);
+						rp->CRActiveQuestion = mDNSNULL;
+						}
+					// if this is a new question, then we never delivered an ADD yet, so don't deliver the RMV.
+					continue;
+					}
+				}
+			LogInfo("CacheRecordResetDNSServer: resetting cache record %##s DNSServer address before:%#a,"
+				" after:%#a, CRActiveQuestion %p", rp->resrec.name->c, (rp->resrec.rDNSServer != mDNSNULL ?
+				&rp->resrec.rDNSServer->addr : mDNSNULL), (new != mDNSNULL ?  &new->addr : mDNSNULL),
+				rp->CRActiveQuestion);
+			// Though we set it to the new DNS server, the caller is *assumed* to do either a purge
+			// or reconfirm or send out questions to the "new" server to verify whether the cached
+			// RDATA is valid
+			rp->resrec.rDNSServer = new;
+			}
+		}
+
+	// Case 1 and Case 2
+	if ((found && foundNew) || (!found && foundNew))
+		{
+		if (newQuestion)
+			LogInfo("CacheRecordResetDNSServer: deliverAddEvents not set for question %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype));
+		else if (QuerySuppressed(q))
+			LogInfo("CacheRecordResetDNSServer: deliverAddEvents not set for suppressed question %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype));
+		else
+			{
+			LogInfo("CacheRecordResetDNSServer: deliverAddEvents set for %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype));
+			q->deliverAddEvents = mDNStrue;
+			for (qptr = q->next; qptr; qptr = qptr->next)
+				if (qptr->DuplicateOf == q) qptr->deliverAddEvents = mDNStrue;
+			}
+		return;
+		}
+
+	// Case 3 and Case 4
+	return;
+	}
+
+mDNSexport void DNSServerChangeForQuestion(mDNS *const m, DNSQuestion *q, DNSServer *new)
+	{
+	DNSQuestion *qptr;
+
+	// 1. Whenever we change the DNS server, we change the message identifier also so that response
+	// from the old server is not accepted as a response from the new server but only messages
+	// from the new server are accepted as valid responses. We do it irrespective of whether "new"
+	// is NULL or not. It is possible that we send two queries, no responses, pick a new DNS server
+	// which is NULL and now the response comes back and will try to penalize the DNS server which
+	// is NULL. By setting the messageID here, we will not accept that as a valid response.
+
+	q->TargetQID = mDNS_NewMessageID(m);
+		
+	// 2. Move the old cache records to point them at the new DNSServer so that we can deliver the ADD/RMV events
+	// appropriately. At any point in time, we want all the cache records point only to one DNSServer for a given
+	// question. "DNSServer" here is the DNSServer object and not the DNS server itself. It is possible to
+	// have the same DNS server address in two objects, one scoped and another not scoped. But, the cache is per
+	// DNSServer object. By maintaining the question and the cache entries point to the same DNSServer
+	// always, the cache maintenance and delivery of ADD/RMV events becomes simpler.
+	//
+	// CacheRecordResetDNSServer should be called only once for the non-duplicate question as once the cache
+	// entries are moved to point to the new DNSServer, we don't need to call it for the duplicate question
+	// and it is wrong to call for the duplicate question as it's decision to mark deliverAddevents will be
+	// incorrect.
+
+	if (q->DuplicateOf)
+		LogMsg("DNSServerChangeForQuestion: ERROR: Called for duplicate question %##s", q->qname.c);
+	else
+		CacheRecordResetDNSServer(m, q, new);
+
+	// 3. Make sure all the duplicate questions point to the same DNSServer so that delivery
+	// of events for all of them are consistent. Duplicates for a question are always inserted
+	// after in the list.
+	q->qDNSServer = new;
+	for (qptr = q->next ; qptr; qptr = qptr->next)
+		{
+		if (qptr->DuplicateOf == q) { qptr->validDNSServers = q->validDNSServers; qptr->qDNSServer = new; }
+		}
+	}
+	
+mDNSexport mStatus uDNS_SetupDNSConfig(mDNS *const m)
+	{
+	mDNSu32 slot;
+	CacheGroup *cg;
+	CacheRecord *cr;
+
+	mDNSAddr     v4, v6, r;
+	domainname   fqdn;
+	DNSServer   *ptr, **p = &m->DNSServers;
+	const DNSServer *oldServers = m->DNSServers;
+	DNSQuestion *q;
+	McastResolver *mr, **mres = &m->McastResolvers;
+	
+	debugf("uDNS_SetupDNSConfig: entry");
+
+	// Let the platform layer get the current DNS information
+	// The m->StartWABQueries is set when we get the first domain enumeration query (no need to hit the network
+	// with domain enumeration queries until we actually need that information). Even if it is not set, we still
+	// need to setup the search domains so that we can append them to queries that need them.
+
+	uDNS_SetupSearchDomains(m, m->StartWABQueries ? UDNS_START_WAB_QUERY : 0);
+
+	mDNS_Lock(m);
+
+	for (ptr = m->DNSServers; ptr; ptr = ptr->next)
+		{
+		ptr->penaltyTime = 0;
+		ptr->flags |= DNSServer_FlagDelete;
+		}
+
+	// We handle the mcast resolvers here itself as mDNSPlatformSetDNSConfig looks at
+	// mcast resolvers. Today we get both mcast and ucast configuration using the same
+	// API
+	for (mr = m->McastResolvers; mr; mr = mr->next)
+		mr->flags |= McastResolver_FlagDelete;
+
+	mDNSPlatformSetDNSConfig(m, mDNStrue, mDNSfalse, &fqdn, mDNSNULL, mDNSNULL);
+
+	// For now, we just delete the mcast resolvers. We don't deal with cache or
+	// questions here. Neither question nor cache point to mcast resolvers. Questions
+	// do inherit the timeout values from mcast resolvers. But we don't bother
+	// affecting them as they never change.
+	while (*mres)
+		{
+		if (((*mres)->flags & DNSServer_FlagDelete) != 0)
+			{
+			mr = *mres;
+			*mres = (*mres)->next;
+			debugf("uDNS_SetupDNSConfig: Deleting mcast resolver %##s", mr, mr->domain.c);
+			mDNSPlatformMemFree(mr);
+			}
+		else
+			{
+			(*mres)->flags &= ~McastResolver_FlagNew;
+			mres = &(*mres)->next;
+			}
+		}
+
+	// Mark the records to be flushed that match a new resolver. We need to do this before
+	// we walk the questions below where we change the DNSServer pointer of the cache
+	// record
+	FORALL_CACHERECORDS(slot, cg, cr)
+		{
+		if (cr->resrec.InterfaceID) continue;
+
+		// We just mark them for purge or reconfirm. We can't affect the DNSServer pointer
+		// here as the code below that calls CacheRecordResetDNSServer relies on this
+		//
+		// The new DNSServer may be a scoped or non-scoped one. We use the active question's
+		// InterfaceID for looking up the right DNS server
+		ptr = GetServerForName(m, cr->resrec.name, cr->CRActiveQuestion ? cr->CRActiveQuestion->InterfaceID : mDNSNULL);
+
+		// Purge or Reconfirm if this cache entry would use the new DNS server
+		if (ptr && (ptr != cr->resrec.rDNSServer))
+			{
+			// As the DNSServers for this cache record is not the same anymore, we don't
+			// want any new questions to pick this old value
+			if (cr->CRActiveQuestion == mDNSNULL)
+				{
+				LogInfo("uDNS_SetupDNSConfig: Purging Resourcerecord %s", CRDisplayString(m, cr));
+				mDNS_PurgeCacheResourceRecord(m, cr);
+				}
+			else
+				{
+				LogInfo("uDNS_SetupDNSConfig: Purging/Reconfirming Resourcerecord %s", CRDisplayString(m, cr));
+				PurgeOrReconfirmCacheRecord(m, cr, ptr, mDNSfalse);
+				}
+			}
+		}
+	// Update our qDNSServer pointers before we go and free the DNSServer object memory
+	for (q = m->Questions; q; q=q->next)
+		if (!mDNSOpaque16IsZero(q->TargetQID))
+			{
+			DNSServer *s, *t;
+			DNSQuestion *qptr;
+			if (q->DuplicateOf) continue;
+			SetValidDNSServers(m, q);
+			q->triedAllServersOnce = 0;
+			s = GetServerForQuestion(m, q);
+			t = q->qDNSServer;
+			if (t != s)
+				{
+				// If DNS Server for this question has changed, reactivate it
+				debugf("uDNS_SetupDNSConfig: Updating DNS Server from %p %#a:%d (%##s) to %p %#a:%d (%##s) for %##s (%s)",
+					t, t ? &t->addr : mDNSNULL, mDNSVal16(t ? t->port : zeroIPPort), t ? t->domain.c : (mDNSu8*)"",
+					s, s ? &s->addr : mDNSNULL, mDNSVal16(s ? s->port : zeroIPPort), s ? s->domain.c : (mDNSu8*)"",
+					q->qname.c, DNSTypeName(q->qtype));
+
+				// After we reset the DNSServer pointer on the cache records here, three things could happen:
+				//
+				// 1) The query gets sent out and when the actual response comes back later it is possible
+				// that the response has the same RDATA, in which case we update our cache entry.
+				// If the response is different, then the entry will expire and a new entry gets added.
+				// For the latter case to generate a RMV followed by ADD events, we need to reset the DNS
+				// server here to match the question and the cache record.
+				//
+				// 2) We might have marked the cache entries for purge above and for us to be able to generate the RMV
+				// events for the questions, the DNSServer on the question should match the Cache Record
+				//
+				// 3) We might have marked the cache entries for reconfirm above, for which we send the query out which is
+				// the same as the first case above.
+
+				DNSServerChangeForQuestion(m, q, s);
+				q->unansweredQueries = 0;
+				// We still need to pick a new DNSServer for the questions that have been
+				// suppressed, but it is wrong to activate the query as DNS server change
+				// could not possibly change the status of SuppressUnusable questions
+				if (!QuerySuppressed(q))
+					{
+					debugf("uDNS_SetupDNSConfig: Activating query %p %##s (%s)", q, q->qname.c, DNSTypeName(q->qtype));
+					ActivateUnicastQuery(m, q, mDNStrue);
+					// ActivateUnicastQuery is called for duplicate questions also as it does something
+					// special for AutoTunnel questions
+					for (qptr = q->next ; qptr; qptr = qptr->next)
+						{
+						if (qptr->DuplicateOf == q) ActivateUnicastQuery(m, qptr, mDNStrue);
+						}
+					}
+				}
+			else
+				{
+				debugf("uDNS_SetupDNSConfig: Not Updating DNS server question %p %##s (%s) DNS server %#a:%d %p %d",
+					q, q->qname.c, DNSTypeName(q->qtype), t ? &t->addr : mDNSNULL, mDNSVal16(t ? t->port : zeroIPPort), q->DuplicateOf, q->SuppressUnusable);
+				for (qptr = q->next ; qptr; qptr = qptr->next)
+					if (qptr->DuplicateOf == q) { qptr->validDNSServers = q->validDNSServers; qptr->qDNSServer = q->qDNSServer; }
+				}
+			}
+
+	while (*p)
+		{
+		if (((*p)->flags & DNSServer_FlagDelete) != 0)
+			{
+			// Scan our cache, looking for uDNS records that we would have queried this server for.
+			// We reconfirm any records that match, because in this world of split DNS, firewalls, etc.
+			// different DNS servers can give different answers to the same question.
+			ptr = *p;
+			FORALL_CACHERECORDS(slot, cg, cr)
+				{
+				if (cr->resrec.InterfaceID) continue;
+				if (cr->resrec.rDNSServer == ptr)
+					{
+					// If we don't have an active question for this cache record, neither Purge can
+					// generate RMV events nor Reconfirm can send queries out. Just set the DNSServer
+					// pointer on the record NULL so that we don't point to freed memory (We might dereference
+					// DNSServer pointers from resource record for logging purposes).
+					//
+					// If there is an active question, point to its DNSServer as long as it does not point to the
+					// freed one. We already went through the questions above and made them point at either the
+					// new server or NULL if there is no server and also affected the cache entries that match
+					// this question. Hence, whenever we hit a resource record with a DNSServer that is just
+					// about to be deleted, we should never have an active question. The code below just tries to
+					// be careful logging messages if we ever hit this case.
+
+					if (cr->CRActiveQuestion)
+						{
+						DNSQuestion *qptr = cr->CRActiveQuestion;
+						if (qptr->qDNSServer == mDNSNULL)
+							LogMsg("uDNS_SetupDNSConfig: Cache Record %s match: Active question %##s (%s) with DNSServer Address NULL, Server to be deleted %#a",
+								CRDisplayString(m, cr), qptr->qname.c, DNSTypeName(qptr->qtype), &ptr->addr);
+						else
+							LogMsg("uDNS_SetupDNSConfig: Cache Record %s match: Active question %##s (%s) DNSServer Address %#a, Server to be deleted %#a",
+								CRDisplayString(m, cr),  qptr->qname.c, DNSTypeName(qptr->qtype), &qptr->qDNSServer->addr, &ptr->addr);
+
+						if (qptr->qDNSServer == ptr)
+							{
+							qptr->validDNSServers = zeroOpaque64;
+							qptr->qDNSServer = mDNSNULL;
+							cr->resrec.rDNSServer = mDNSNULL;
+							}
+						else
+							{
+							cr->resrec.rDNSServer = qptr->qDNSServer;
+							}
+						}
+					else
+						{
+						LogInfo("uDNS_SetupDNSConfig: Cache Record %##s has no Active question, Record's DNSServer Address %#a, Server to be deleted %#a",
+							cr->resrec.name, &cr->resrec.rDNSServer->addr, &ptr->addr);
+						cr->resrec.rDNSServer = mDNSNULL;
+						}
+						
+					PurgeOrReconfirmCacheRecord(m, cr, ptr, mDNStrue);
+					}
+				}
+			*p = (*p)->next;
+			debugf("uDNS_SetupDNSConfig: Deleting server %p %#a:%d (%##s)", ptr, &ptr->addr, mDNSVal16(ptr->port), ptr->domain.c);
+			mDNSPlatformMemFree(ptr);
+			NumUnicastDNSServers--;
+			}
+		else
+			{
+			(*p)->flags &= ~DNSServer_FlagNew;
+			p = &(*p)->next;
+			}
+		}
+
+	// If we now have no DNS servers at all and we used to have some, then immediately purge all unicast cache records (including for LLQs).
+	// This is important for giving prompt remove events when the user disconnects the Ethernet cable or turns off wireless.
+	// Otherwise, stale data lingers for 5-10 seconds, which is not the user-experience people expect from Bonjour.
+	// Similarly, if we now have some DNS servers and we used to have none, we want to purge any fake negative results we may have generated.
+	if ((m->DNSServers != mDNSNULL) != (oldServers != mDNSNULL))
+		{
+		int count = 0;
+		FORALL_CACHERECORDS(slot, cg, cr) if (!cr->resrec.InterfaceID) { mDNS_PurgeCacheResourceRecord(m, cr); count++; }
+		LogInfo("uDNS_SetupDNSConfig: %s available; purged %d unicast DNS records from cache",
+			m->DNSServers ? "DNS server became" : "No DNS servers", count);
+
+		// Force anything that needs to get zone data to get that information again
+		RestartRecordGetZoneData(m);
+		}
+
+	// Did our FQDN change?
+	if (!SameDomainName(&fqdn, &m->FQDN))
+		{
+		if (m->FQDN.c[0]) mDNS_RemoveDynDNSHostName(m, &m->FQDN);
+
+		AssignDomainName(&m->FQDN, &fqdn);
+
+		if (m->FQDN.c[0])
+			{
+			mDNSPlatformDynDNSHostNameStatusChanged(&m->FQDN, 1);
+			mDNS_AddDynDNSHostName(m, &m->FQDN, DynDNSHostNameCallback, mDNSNULL);
+			}
+		}
+
+	mDNS_Unlock(m);
+
+	// handle router and primary interface changes
+	v4 = v6 = r = zeroAddr;
+	v4.type = r.type = mDNSAddrType_IPv4;
+
+	if (mDNSPlatformGetPrimaryInterface(m, &v4, &v6, &r) == mStatus_NoError && !mDNSv4AddressIsLinkLocal(&v4.ip.v4))
+		{
+		mDNS_SetPrimaryInterfaceInfo(m,
+			!mDNSIPv4AddressIsZero(v4.ip.v4) ? &v4 : mDNSNULL,
+			!mDNSIPv6AddressIsZero(v6.ip.v6) ? &v6 : mDNSNULL,
+			!mDNSIPv4AddressIsZero(r .ip.v4) ? &r  : mDNSNULL);
+		}
+	else
+		{
+		mDNS_SetPrimaryInterfaceInfo(m, mDNSNULL, mDNSNULL, mDNSNULL);
+		if (m->FQDN.c[0]) mDNSPlatformDynDNSHostNameStatusChanged(&m->FQDN, 1);	// Set status to 1 to indicate temporary failure
+		}
+
+	debugf("uDNS_SetupDNSConfig: number of unicast DNS servers %d", NumUnicastDNSServers);
+	return mStatus_NoError;
+	}
+
+mDNSexport void mDNSCoreInitComplete(mDNS *const m, mStatus result)
+	{
+	m->mDNSPlatformStatus = result;
+	if (m->MainCallback)
+		{
+		mDNS_Lock(m);
+		mDNS_DropLockBeforeCallback();		// Allow client to legally make mDNS API calls from the callback
+		m->MainCallback(m, mStatus_NoError);
+		mDNS_ReclaimLockAfterCallback();	// Decrement mDNS_reentrancy to block mDNS API calls again
+		mDNS_Unlock(m);
+		}
+	}
+
+mDNSlocal void DeregLoop(mDNS *const m, AuthRecord *const start)
+	{
+	m->CurrentRecord = start;
+	while (m->CurrentRecord)
+		{
+		AuthRecord *rr = m->CurrentRecord;
+		LogInfo("DeregLoop: %s deregistration for %p %02X %s",
+			(rr->resrec.RecordType != kDNSRecordTypeDeregistering) ? "Initiating  " : "Accelerating",
+			rr, rr->resrec.RecordType, ARDisplayString(m, rr));
+		if (rr->resrec.RecordType != kDNSRecordTypeDeregistering)
+			mDNS_Deregister_internal(m, rr, mDNS_Dereg_rapid);
+		else if (rr->AnnounceCount > 1)
+			{
+			rr->AnnounceCount = 1;
+			rr->LastAPTime = m->timenow - rr->ThisAPInterval;
+			}
+		// Mustn't advance m->CurrentRecord until *after* mDNS_Deregister_internal, because
+		// new records could have been added to the end of the list as a result of that call.
+		if (m->CurrentRecord == rr) // If m->CurrentRecord was not advanced for us, do it now
+			m->CurrentRecord = rr->next;
+		}
+	}
+
+mDNSexport void mDNS_StartExit(mDNS *const m)
+	{
+	NetworkInterfaceInfo *intf;
+	AuthRecord *rr;
+
+	mDNS_Lock(m);
+
+	LogInfo("mDNS_StartExit");
+	m->ShutdownTime = NonZeroTime(m->timenow + mDNSPlatformOneSecond * 5);
+
+	mDNSCoreBeSleepProxyServer_internal(m, 0, 0, 0, 0);
+
+#if APPLE_OSX_mDNSResponder
+#if ! NO_WCF
+	CHECK_WCF_FUNCTION(WCFConnectionDealloc)
+		{
+		if (m->WCF) WCFConnectionDealloc((WCFConnection *)m->WCF);
+		}
+#endif
+#endif
+
+#ifndef UNICAST_DISABLED
+	{
+	SearchListElem *s;
+	SuspendLLQs(m);
+	// Don't need to do SleepRecordRegistrations() here
+	// because we deregister all records and services later in this routine
+	while (m->Hostnames) mDNS_RemoveDynDNSHostName(m, &m->Hostnames->fqdn);
+
+	// For each member of our SearchList, deregister any records it may have created, and cut them from the list.
+	// Otherwise they'll be forcibly deregistered for us (without being cut them from the appropriate list)
+	// and we may crash because the list still contains dangling pointers.
+	for (s = SearchList; s; s = s->next)
+		while (s->AuthRecs)
+			{
+			ARListElem *dereg = s->AuthRecs;
+			s->AuthRecs = s->AuthRecs->next;
+			mDNS_Deregister_internal(m, &dereg->ar, mDNS_Dereg_normal);	// Memory will be freed in the FreeARElemCallback
+			}
+	}
+#endif
+
+	for (intf = m->HostInterfaces; intf; intf = intf->next)
+		if (intf->Advertise)
+			DeadvertiseInterface(m, intf);
+
+	// Shut down all our active NAT Traversals
+	while (m->NATTraversals)
+		{
+		NATTraversalInfo *t = m->NATTraversals;
+		mDNS_StopNATOperation_internal(m, t);		// This will cut 't' from the list, thereby advancing m->NATTraversals in the process
+
+		// After stopping the NAT Traversal, we zero out the fields.
+		// This has particularly important implications for our AutoTunnel records --
+		// when we deregister our AutoTunnel records below, we don't want their mStatus_MemFree
+		// handlers to just turn around and attempt to re-register those same records.
+		// Clearing t->ExternalPort/t->RequestedPort will cause the mStatus_MemFree callback handlers
+		// to not do this.
+		t->ExternalAddress = zerov4Addr;
+		t->ExternalPort    = zeroIPPort;
+		t->RequestedPort   = zeroIPPort;
+		t->Lifetime        = 0;
+		t->Result          = mStatus_NoError;
+		}
+
+	// Make sure there are nothing but deregistering records remaining in the list
+	if (m->CurrentRecord)
+		LogMsg("mDNS_StartExit: ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord));
+
+	// We're in the process of shutting down, so queries, etc. are no longer available.
+	// Consequently, determining certain information, e.g. the uDNS update server's IP
+	// address, will not be possible.  The records on the main list are more likely to
+	// already contain such information, so we deregister the duplicate records first.
+	LogInfo("mDNS_StartExit: Deregistering duplicate resource records");
+	DeregLoop(m, m->DuplicateRecords);
+	LogInfo("mDNS_StartExit: Deregistering resource records");
+	DeregLoop(m, m->ResourceRecords);
+	
+	// If we scheduled a response to send goodbye packets, we set NextScheduledResponse to now. Normally when deregistering records,
+	// we allow up to 100ms delay (to help improve record grouping) but when shutting down we don't want any such delay.
+	if (m->NextScheduledResponse - m->timenow < mDNSPlatformOneSecond)
+		{
+		m->NextScheduledResponse = m->timenow;
+		m->SuppressSending = 0;
+		}
+
+	if (m->ResourceRecords) LogInfo("mDNS_StartExit: Sending final record deregistrations");
+	else                    LogInfo("mDNS_StartExit: No deregistering records remain");
+
+	for (rr = m->DuplicateRecords; rr; rr = rr->next)
+		LogMsg("mDNS_StartExit: Should not still have Duplicate Records remaining: %02X %s", rr->resrec.RecordType, ARDisplayString(m, rr));
+
+	// Send responses to flush any pending deregistrations
+	SendResponses(m);
+
+	// If any deregistering records remain, send their deregistration announcements before we exit
+	if (m->mDNSPlatformStatus != mStatus_NoError) DiscardDeregistrations(m);
+
+	mDNS_Unlock(m);
+
+	LogInfo("mDNS_StartExit: done");
+	}
+
+mDNSexport void mDNS_FinalExit(mDNS *const m)
+	{
+	mDNSu32 rrcache_active = 0;
+	mDNSu32 rrcache_totalused = 0;
+	mDNSu32 slot;
+	AuthRecord *rr;
+
+	LogInfo("mDNS_FinalExit: mDNSPlatformClose");
+	mDNSPlatformClose(m);
+
+	rrcache_totalused = m->rrcache_totalused;
+	for (slot = 0; slot < CACHE_HASH_SLOTS; slot++)
+		{
+		while (m->rrcache_hash[slot])
+			{
+			CacheGroup *cg = m->rrcache_hash[slot];
+			while (cg->members)
+				{
+				CacheRecord *cr = cg->members;
+				cg->members = cg->members->next;
+				if (cr->CRActiveQuestion) rrcache_active++;
+				ReleaseCacheRecord(m, cr);
+				}
+			cg->rrcache_tail = &cg->members;
+			ReleaseCacheGroup(m, &m->rrcache_hash[slot]);
+			}
+		}
+	debugf("mDNS_FinalExit: RR Cache was using %ld records, %lu active", rrcache_totalused, rrcache_active);
+	if (rrcache_active != m->rrcache_active)
+		LogMsg("*** ERROR *** rrcache_active %lu != m->rrcache_active %lu", rrcache_active, m->rrcache_active);
+
+	for (rr = m->ResourceRecords; rr; rr = rr->next)
+		LogMsg("mDNS_FinalExit failed to send goodbye for: %p %02X %s", rr, rr->resrec.RecordType, ARDisplayString(m, rr));
+
+	LogInfo("mDNS_FinalExit: done");
+	}
diff --git a/mdnsresponder/mDNSCore/mDNSDebug.h b/mdnsresponder/mDNSCore/mDNSDebug.h
new file mode 100755
index 0000000..07647b5
--- /dev/null
+++ b/mdnsresponder/mDNSCore/mDNSDebug.h
@@ -0,0 +1,164 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+#ifndef __mDNSDebug_h
+#define __mDNSDebug_h
+
+// Set MDNS_DEBUGMSGS to 0 to optimize debugf() calls out of the compiled code
+// Set MDNS_DEBUGMSGS to 1 to generate normal debugging messages
+// Set MDNS_DEBUGMSGS to 2 to generate verbose debugging messages
+// MDNS_DEBUGMSGS is normally set in the project options (or makefile) but can also be set here if desired
+// (If you edit the file here to turn on MDNS_DEBUGMSGS while you're debugging some code, be careful
+// not to accidentally check-in that change by mistake when you check in your other changes.)
+
+//#undef MDNS_DEBUGMSGS
+//#define MDNS_DEBUGMSGS 2
+
+// Set MDNS_CHECK_PRINTF_STYLE_FUNCTIONS to 1 to enable extra GCC compiler warnings
+// Note: You don't normally want to do this, because it generates a bunch of
+// spurious warnings for the following custom extensions implemented by mDNS_vsnprintf:
+//    warning: `#' flag used with `%s' printf format    (for %#s              -- pascal string format)
+//    warning: repeated `#' flag in format              (for %##s             -- DNS name string format)
+//    warning: double format, pointer arg (arg 2)       (for %.4a, %.16a, %#a -- IP address formats)
+#define MDNS_CHECK_PRINTF_STYLE_FUNCTIONS 0
+
+typedef enum
+	{
+	MDNS_LOG_MSG,
+	MDNS_LOG_OPERATION,
+	MDNS_LOG_SPS,
+	MDNS_LOG_INFO,
+	MDNS_LOG_DEBUG,
+	} mDNSLogLevel_t;
+
+// Set this symbol to 1 to answer remote queries for our Address, reverse mapping PTR, and HINFO records
+#define ANSWER_REMOTE_HOSTNAME_QUERIES 0
+
+// Set this symbol to 1 to do extra debug checks on malloc() and free()
+// Set this symbol to 2 to write a log message for every malloc() and free()
+//#define MACOSX_MDNS_MALLOC_DEBUGGING 1
+
+//#define ForceAlerts 1
+//#define LogTimeStamps 1
+
+// Developer-settings section ends here
+
+#if MDNS_CHECK_PRINTF_STYLE_FUNCTIONS
+#define IS_A_PRINTF_STYLE_FUNCTION(F,A) __attribute__ ((format(printf,F,A)))
+#else
+#define IS_A_PRINTF_STYLE_FUNCTION(F,A)
+#endif
+
+#ifdef __cplusplus
+	extern "C" {
+#endif
+
+// Variable argument macro support. Use ANSI C99 __VA_ARGS__ where possible. Otherwise, use the next best thing.
+
+#if (defined(__GNUC__))
+	#if ((__GNUC__ > 3) || ((__GNUC__ == 3) && (__GNUC_MINOR__ >= 2)))
+		#define MDNS_C99_VA_ARGS        1
+		#define MDNS_GNU_VA_ARGS        0
+	#else
+		#define MDNS_C99_VA_ARGS        0
+		#define MDNS_GNU_VA_ARGS        1
+	#endif
+	#define MDNS_HAS_VA_ARG_MACROS      1
+#elif (_MSC_VER >= 1400) // Visual Studio 2005 and later
+	#define MDNS_C99_VA_ARGS            1
+	#define MDNS_GNU_VA_ARGS            0
+	#define MDNS_HAS_VA_ARG_MACROS      1
+#elif (defined(__MWERKS__))
+	#define MDNS_C99_VA_ARGS            1
+	#define MDNS_GNU_VA_ARGS            0
+	#define MDNS_HAS_VA_ARG_MACROS      1
+#else
+	#define MDNS_C99_VA_ARGS            0
+	#define MDNS_GNU_VA_ARGS            0
+	#define MDNS_HAS_VA_ARG_MACROS      0
+#endif
+
+#if (MDNS_HAS_VA_ARG_MACROS)
+	#if (MDNS_C99_VA_ARGS)
+		#define debug_noop( ... ) ((void)0)
+		#define LogMsg( ... )           LogMsgWithLevel(MDNS_LOG_MSG, __VA_ARGS__)
+		#define LogOperation( ... )     do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_OPERATION, __VA_ARGS__); } while (0)
+		#define LogSPS( ... )           do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_SPS,       __VA_ARGS__); } while (0)
+		#define LogInfo( ... )          do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_INFO,      __VA_ARGS__); } while (0)
+	#elif (MDNS_GNU_VA_ARGS)
+		#define	debug_noop( ARGS... ) ((void)0)
+		#define	LogMsg( ARGS... )       LogMsgWithLevel(MDNS_LOG_MSG, ARGS)
+		#define	LogOperation( ARGS... ) do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_OPERATION, ARGS); } while (0)
+		#define	LogSPS( ARGS... )       do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_SPS,       ARGS); } while (0)
+		#define	LogInfo( ARGS... )      do { if (mDNS_LoggingEnabled) LogMsgWithLevel(MDNS_LOG_INFO,      ARGS); } while (0)
+	#else
+		#error Unknown variadic macros
+	#endif
+#else
+	// If your platform does not support variadic macros, you need to define the following variadic functions.
+	// See mDNSShared/mDNSDebug.c for sample implementation
+	#define debug_noop 1 ? (void)0 : (void)
+	#define LogMsg LogMsg_
+	#define LogOperation (mDNS_LoggingEnabled == 0) ? ((void)0) : LogOperation_
+	#define LogSPS       (mDNS_LoggingEnabled == 0) ? ((void)0) : LogSPS_
+	#define LogInfo      (mDNS_LoggingEnabled == 0) ? ((void)0) : LogInfo_
+	extern void LogMsg_(const char *format, ...)       IS_A_PRINTF_STYLE_FUNCTION(1,2);
+	extern void LogOperation_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2);
+	extern void LogSPS_(const char *format, ...)       IS_A_PRINTF_STYLE_FUNCTION(1,2);
+	extern void LogInfo_(const char *format, ...)      IS_A_PRINTF_STYLE_FUNCTION(1,2);
+#endif
+
+#if MDNS_DEBUGMSGS
+#define debugf debugf_
+extern void debugf_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2);
+#else
+#define debugf debug_noop
+#endif
+
+#if MDNS_DEBUGMSGS > 1
+#define verbosedebugf verbosedebugf_
+extern void verbosedebugf_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2);
+#else
+#define verbosedebugf debug_noop
+#endif
+
+extern int	      mDNS_LoggingEnabled;
+extern int	      mDNS_PacketLoggingEnabled;
+extern int        mDNS_DebugMode;	// If non-zero, LogMsg() writes to stderr instead of syslog
+extern const char ProgramName[];
+
+extern void LogMsgWithLevel(mDNSLogLevel_t logLevel, const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(2,3);
+// LogMsgNoIdent needs to be fixed so that it logs without the ident prefix like it used to
+// (or completely overhauled to use the new "log to a separate file" facility)
+#define LogMsgNoIdent LogMsg
+
+#if APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING >= 1
+extern void *mallocL(char *msg, unsigned int size);
+extern void freeL(char *msg, void *x);
+extern void LogMemCorruption(const char *format, ...);
+extern void uds_validatelists(void);
+extern void udns_validatelists(void *const v);
+#else
+#define mallocL(X,Y) malloc(Y)
+#define freeL(X,Y) free(Y)
+#endif
+
+#ifdef __cplusplus
+	}
+#endif
+
+#endif
diff --git a/mdnsresponder/mDNSCore/mDNSEmbeddedAPI.h b/mdnsresponder/mDNSCore/mDNSEmbeddedAPI.h
new file mode 100755
index 0000000..44f7ec0
--- /dev/null
+++ b/mdnsresponder/mDNSCore/mDNSEmbeddedAPI.h
@@ -0,0 +1,2974 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+
+   NOTE:
+   If you're building an application that uses DNS Service Discovery
+   this is probably NOT the header file you're looking for.
+   In most cases you will want to use /usr/include/dns_sd.h instead.
+
+   This header file defines the lowest level raw interface to mDNSCore,
+   which is appropriate *only* on tiny embedded systems where everything
+   runs in a single address space and memory is extremely constrained.
+   All the APIs here are malloc-free, which means that the caller is
+   responsible for passing in a pointer to the relevant storage that
+   will be used in the execution of that call, and (when called with
+   correct parameters) all the calls are guaranteed to succeed. There
+   is never a case where a call can suffer intermittent failures because
+   the implementation calls malloc() and sometimes malloc() returns NULL
+   because memory is so limited that no more is available.
+   This is primarily for devices that need to have precisely known fixed
+   memory requirements, with absolutely no uncertainty or run-time variation,
+   but that certainty comes at a cost of more difficult programming.
+   
+   For applications running on general-purpose desktop operating systems
+   (Mac OS, Linux, Solaris, Windows, etc.) the API you should use is
+   /usr/include/dns_sd.h, which defines the API by which multiple
+   independent client processes communicate their DNS Service Discovery
+   requests to a single "mdnsd" daemon running in the background.
+   
+   Even on platforms that don't run multiple independent processes in
+   multiple independent address spaces, you can still use the preferred
+   dns_sd.h APIs by linking in "dnssd_clientshim.c", which implements
+   the standard "dns_sd.h" API calls, allocates any required storage
+   using malloc(), and then calls through to the low-level malloc-free
+   mDNSCore routines defined here. This has the benefit that even though
+   you're running on a small embedded system with a single address space,
+   you can still use the exact same client C code as you'd use on a
+   general-purpose desktop system.
+
+ */
+
+#ifndef __mDNSClientAPI_h
+#define __mDNSClientAPI_h
+
+/* MinGW thinks "#define interface struct" is a cute way to do ObjC
+ * compatibility. Everything is terrible.
+ */
+#ifdef _WIN32
+#ifndef interface
+#warning "MinGW no longer does weird things with 'interface'. "\
+         "You can remove this code."
+#endif /* ! interface */
+#undef interface
+#endif /* _WIN32 */
+
+#if defined(EFI32) || defined(EFI64) || defined(EFIX64)
+// EFI doesn't have stdarg.h unless it's building with GCC.
+#include "Tiano.h"
+#if !defined(__GNUC__)
+#define va_list         VA_LIST
+#define va_start(a, b)  VA_START(a, b)
+#define va_end(a)       VA_END(a)
+#define va_arg(a, b)    VA_ARG(a, b)
+#endif
+#else
+#include <stdarg.h>		// stdarg.h is required for for va_list support for the mDNS_vsnprintf declaration
+#endif
+
+#include "mDNSDebug.h"
+#if APPLE_OSX_mDNSResponder
+#include <uuid/uuid.h>
+#endif
+
+#ifdef __cplusplus
+	extern "C" {
+#endif
+
+// ***************************************************************************
+// Function scope indicators
+
+// If you see "mDNSlocal" before a function name in a C file, it means the function is not callable outside this file
+#ifndef mDNSlocal
+#define mDNSlocal static
+#endif
+// If you see "mDNSexport" before a symbol in a C file, it means the symbol is exported for use by clients
+// For every "mDNSexport" in a C file, there needs to be a corresponding "extern" declaration in some header file
+// (When a C file #includes a header file, the "extern" declarations tell the compiler:
+// "This symbol exists -- but not necessarily in this C file.")
+#ifndef mDNSexport
+#define mDNSexport
+#endif
+
+// Explanation: These local/export markers are a little habit of mine for signaling the programmers' intentions.
+// When "mDNSlocal" is just a synonym for "static", and "mDNSexport" is a complete no-op, you could be
+// forgiven for asking what purpose they serve. The idea is that if you see "mDNSexport" in front of a
+// function definition it means the programmer intended it to be exported and callable from other files
+// in the project. If you see "mDNSlocal" in front of a function definition it means the programmer
+// intended it to be private to that file. If you see neither in front of a function definition it
+// means the programmer forgot (so you should work out which it is supposed to be, and fix it).
+// Using "mDNSlocal" instead of "static" makes it easier to do a textual searches for one or the other.
+// For example you can do a search for "static" to find if any functions declare any local variables as "static"
+// (generally a bad idea unless it's also "const", because static storage usually risks being non-thread-safe)
+// without the results being cluttered with hundreds of matches for functions declared static.
+// - Stuart Cheshire
+
+// ***************************************************************************
+// Structure packing macro
+
+// If we're not using GNUC, it's not fatal.
+// Most compilers naturally pack the on-the-wire structures correctly anyway, so a plain "struct" is usually fine.
+// In the event that structures are not packed correctly, mDNS_Init() will detect this and report an error, so the
+// developer will know what's wrong, and can investigate what needs to be done on that compiler to provide proper packing.
+#ifndef packedstruct
+ #if ((__GNUC__ > 2) || ((__GNUC__ == 2) && (__GNUC_MINOR__ >= 9)))
+  #define packedstruct struct __attribute__((__packed__))
+  #define packedunion  union  __attribute__((__packed__))
+ #else
+  #define packedstruct struct
+  #define packedunion  union
+ #endif
+#endif
+
+// ***************************************************************************
+#if 0
+#pragma mark - DNS Resource Record class and type constants
+#endif
+
+typedef enum							// From RFC 1035
+	{
+	kDNSClass_IN               = 1,		// Internet
+	kDNSClass_CS               = 2,		// CSNET
+	kDNSClass_CH               = 3,		// CHAOS
+	kDNSClass_HS               = 4,		// Hesiod
+	kDNSClass_NONE             = 254,	// Used in DNS UPDATE [RFC 2136]
+
+	kDNSClass_Mask             = 0x7FFF,// Multicast DNS uses the bottom 15 bits to identify the record class...
+	kDNSClass_UniqueRRSet      = 0x8000,// ... and the top bit indicates that all other cached records are now invalid
+
+	kDNSQClass_ANY             = 255,	// Not a DNS class, but a DNS query class, meaning "all classes"
+	kDNSQClass_UnicastResponse = 0x8000	// Top bit set in a question means "unicast response acceptable"
+	} DNS_ClassValues;
+
+typedef enum				// From RFC 1035
+	{
+	kDNSType_A = 1,			//  1 Address
+	kDNSType_NS,			//  2 Name Server
+	kDNSType_MD,			//  3 Mail Destination
+	kDNSType_MF,			//  4 Mail Forwarder
+	kDNSType_CNAME,			//  5 Canonical Name
+	kDNSType_SOA,			//  6 Start of Authority
+	kDNSType_MB,			//  7 Mailbox
+	kDNSType_MG,			//  8 Mail Group
+	kDNSType_MR,			//  9 Mail Rename
+	kDNSType_NULL,			// 10 NULL RR
+	kDNSType_WKS,			// 11 Well-known-service
+	kDNSType_PTR,			// 12 Domain name pointer
+	kDNSType_HINFO,			// 13 Host information
+	kDNSType_MINFO,			// 14 Mailbox information
+	kDNSType_MX,			// 15 Mail Exchanger
+	kDNSType_TXT,			// 16 Arbitrary text string
+	kDNSType_RP,			// 17 Responsible person
+	kDNSType_AFSDB,			// 18 AFS cell database
+	kDNSType_X25,			// 19 X_25 calling address
+	kDNSType_ISDN,			// 20 ISDN calling address
+	kDNSType_RT,			// 21 Router
+	kDNSType_NSAP,			// 22 NSAP address
+	kDNSType_NSAP_PTR,		// 23 Reverse NSAP lookup (deprecated)
+	kDNSType_SIG,			// 24 Security signature
+	kDNSType_KEY,			// 25 Security key
+	kDNSType_PX,			// 26 X.400 mail mapping
+	kDNSType_GPOS,			// 27 Geographical position (withdrawn)
+	kDNSType_AAAA,			// 28 IPv6 Address
+	kDNSType_LOC,			// 29 Location Information
+	kDNSType_NXT,			// 30 Next domain (security)
+	kDNSType_EID,			// 31 Endpoint identifier
+	kDNSType_NIMLOC,		// 32 Nimrod Locator
+	kDNSType_SRV,			// 33 Service record
+	kDNSType_ATMA,			// 34 ATM Address
+	kDNSType_NAPTR,			// 35 Naming Authority PoinTeR
+	kDNSType_KX,			// 36 Key Exchange
+	kDNSType_CERT,			// 37 Certification record
+	kDNSType_A6,			// 38 IPv6 Address (deprecated)
+	kDNSType_DNAME,			// 39 Non-terminal DNAME (for IPv6)
+	kDNSType_SINK,			// 40 Kitchen sink (experimental)
+	kDNSType_OPT,			// 41 EDNS0 option (meta-RR)
+	kDNSType_APL,			// 42 Address Prefix List
+	kDNSType_DS,			// 43 Delegation Signer
+	kDNSType_SSHFP,			// 44 SSH Key Fingerprint
+	kDNSType_IPSECKEY,		// 45 IPSECKEY
+	kDNSType_RRSIG,			// 46 RRSIG
+	kDNSType_NSEC,			// 47 Denial of Existence
+	kDNSType_DNSKEY,		// 48 DNSKEY
+	kDNSType_DHCID,			// 49 DHCP Client Identifier
+	kDNSType_NSEC3,			// 50 Hashed Authenticated Denial of Existence
+	kDNSType_NSEC3PARAM,	// 51 Hashed Authenticated Denial of Existence
+
+	kDNSType_HIP = 55,		// 55 Host Identity Protocol
+
+	kDNSType_SPF = 99,		// 99 Sender Policy Framework for E-Mail
+	kDNSType_UINFO,			// 100 IANA-Reserved
+	kDNSType_UID,			// 101 IANA-Reserved
+	kDNSType_GID,			// 102 IANA-Reserved
+	kDNSType_UNSPEC,		// 103 IANA-Reserved
+
+	kDNSType_TKEY = 249,	// 249 Transaction key
+	kDNSType_TSIG,			// 250 Transaction signature
+	kDNSType_IXFR,			// 251 Incremental zone transfer
+	kDNSType_AXFR,			// 252 Transfer zone of authority
+	kDNSType_MAILB,			// 253 Transfer mailbox records
+	kDNSType_MAILA,			// 254 Transfer mail agent records
+	kDNSQType_ANY			// Not a DNS type, but a DNS query type, meaning "all types"
+	} DNS_TypeValues;
+
+// ***************************************************************************
+#if 0
+#pragma mark -
+#pragma mark - Simple types
+#endif
+
+// mDNS defines its own names for these common types to simplify portability across
+// multiple platforms that may each have their own (different) names for these types.
+typedef          int   mDNSBool;
+typedef   signed char  mDNSs8;
+typedef unsigned char  mDNSu8;
+typedef   signed short mDNSs16;
+typedef unsigned short mDNSu16;
+
+// <http://gcc.gnu.org/onlinedocs/gcc-3.3.3/cpp/Common-Predefined-Macros.html> says
+//   __LP64__ _LP64
+//   These macros are defined, with value 1, if (and only if) the compilation is
+//   for a target where long int and pointer both use 64-bits and int uses 32-bit.
+// <http://www.intel.com/software/products/compilers/clin/docs/ug/lin1077.htm> says
+//   Macro Name __LP64__ Value 1
+// A quick Google search for "defined(__LP64__)" OR "#ifdef __LP64__" gives 2590 hits and
+// a search for "#if __LP64__" gives only 12, so I think we'll go with the majority and use defined()
+#if defined(_ILP64) || defined(__ILP64__)
+typedef   signed int32 mDNSs32;
+typedef unsigned int32 mDNSu32;
+#elif defined(_LP64) || defined(__LP64__)
+typedef   signed int   mDNSs32;
+typedef unsigned int   mDNSu32;
+#else
+typedef   signed long  mDNSs32;
+typedef unsigned long  mDNSu32;
+//typedef   signed int mDNSs32;
+//typedef unsigned int mDNSu32;
+#endif
+
+// To enforce useful type checking, we make mDNSInterfaceID be a pointer to a dummy struct
+// This way, mDNSInterfaceIDs can be assigned, and compared with each other, but not with other types
+// Declaring the type to be the typical generic "void *" would lack this type checking
+typedef struct mDNSInterfaceID_dummystruct { void *dummy; } *mDNSInterfaceID;
+
+// These types are for opaque two- and four-byte identifiers.
+// The "NotAnInteger" fields of the unions allow the value to be conveniently passed around in a
+// register for the sake of efficiency, and compared for equality or inequality, but don't forget --
+// just because it is in a register doesn't mean it is an integer. Operations like greater than,
+// less than, add, multiply, increment, decrement, etc., are undefined for opaque identifiers,
+// and if you make the mistake of trying to do those using the NotAnInteger field, then you'll
+// find you get code that doesn't work consistently on big-endian and little-endian machines.
+#if defined(_WIN32)
+ #pragma pack(push,2)
+#endif
+typedef       union { mDNSu8 b[ 2]; mDNSu16 NotAnInteger; } mDNSOpaque16;
+typedef       union { mDNSu8 b[ 4]; mDNSu32 NotAnInteger; } mDNSOpaque32;
+typedef packedunion { mDNSu8 b[ 6]; mDNSu16 w[3]; mDNSu32 l[1]; } mDNSOpaque48;
+typedef       union { mDNSu8 b[ 8]; mDNSu16 w[4]; mDNSu32 l[2]; } mDNSOpaque64;
+typedef       union { mDNSu8 b[16]; mDNSu16 w[8]; mDNSu32 l[4]; } mDNSOpaque128;
+#if defined(_WIN32)
+ #pragma pack(pop)
+#endif
+
+typedef mDNSOpaque16  mDNSIPPort;		// An IP port is a two-byte opaque identifier (not an integer)
+typedef mDNSOpaque32  mDNSv4Addr;		// An IP address is a four-byte opaque identifier (not an integer)
+typedef mDNSOpaque128 mDNSv6Addr;		// An IPv6 address is a 16-byte opaque identifier (not an integer)
+typedef mDNSOpaque48  mDNSEthAddr;		// An Ethernet address is a six-byte opaque identifier (not an integer)
+
+// Bit operations for opaque 64 bit quantity. Uses the 32 bit quantity(l[2]) to set and clear bits
+#define mDNSNBBY 8
+#define bit_set_opaque64(op64, index) (op64.l[((index))/(sizeof(mDNSu32) * mDNSNBBY)] |= (1 << ((index) % (sizeof(mDNSu32) * mDNSNBBY))))
+#define bit_clr_opaque64(op64, index) (op64.l[((index))/(sizeof(mDNSu32) * mDNSNBBY)] &= ~(1 << ((index) % (sizeof(mDNSu32) * mDNSNBBY))))
+#define bit_get_opaque64(op64, index) (op64.l[((index))/(sizeof(mDNSu32) * mDNSNBBY)] & (1 << ((index) % (sizeof(mDNSu32) * mDNSNBBY))))
+
+enum
+	{
+	mDNSAddrType_None    = 0,
+	mDNSAddrType_IPv4    = 4,
+	mDNSAddrType_IPv6    = 6,
+	mDNSAddrType_Unknown = ~0	// Special marker value used in known answer list recording
+	};
+
+enum
+	{
+	mDNSTransport_None = 0,
+	mDNSTransport_UDP  = 1,
+	mDNSTransport_TCP  = 2
+	};
+
+typedef struct
+	{
+	mDNSs32 type;
+	union { mDNSv6Addr v6; mDNSv4Addr v4; } ip;
+	} mDNSAddr;
+
+enum { mDNSfalse = 0, mDNStrue = 1 };
+
+#define mDNSNULL 0L
+
+enum
+	{
+	mStatus_Waiting           = 1,
+	mStatus_NoError           = 0,
+
+	// mDNS return values are in the range FFFE FF00 (-65792) to FFFE FFFF (-65537)
+	// The top end of the range (FFFE FFFF) is used for error codes;
+	// the bottom end of the range (FFFE FF00) is used for non-error values;
+
+	// Error codes:
+	mStatus_UnknownErr                = -65537,		// First value: 0xFFFE FFFF
+	mStatus_NoSuchNameErr             = -65538,
+	mStatus_NoMemoryErr               = -65539,
+	mStatus_BadParamErr               = -65540,
+	mStatus_BadReferenceErr           = -65541,
+	mStatus_BadStateErr               = -65542,
+	mStatus_BadFlagsErr               = -65543,
+	mStatus_UnsupportedErr            = -65544,
+	mStatus_NotInitializedErr         = -65545,
+	mStatus_NoCache                   = -65546,
+	mStatus_AlreadyRegistered         = -65547,
+	mStatus_NameConflict              = -65548,
+	mStatus_Invalid                   = -65549,
+	mStatus_Firewall                  = -65550,
+	mStatus_Incompatible              = -65551,
+	mStatus_BadInterfaceErr           = -65552,
+	mStatus_Refused                   = -65553,
+	mStatus_NoSuchRecord              = -65554,
+	mStatus_NoAuth                    = -65555,
+	mStatus_NoSuchKey                 = -65556,
+	mStatus_NATTraversal              = -65557,
+	mStatus_DoubleNAT                 = -65558,
+	mStatus_BadTime                   = -65559,
+	mStatus_BadSig                    = -65560,     // while we define this per RFC 2845, BIND 9 returns Refused for bad/missing signatures
+	mStatus_BadKey                    = -65561,
+	mStatus_TransientErr              = -65562,     // transient failures, e.g. sending packets shortly after a network transition or wake from sleep
+	mStatus_ServiceNotRunning         = -65563,     // Background daemon not running
+	mStatus_NATPortMappingUnsupported = -65564,     // NAT doesn't support NAT-PMP or UPnP
+	mStatus_NATPortMappingDisabled    = -65565,     // NAT supports NAT-PMP or UPnP but it's disabled by the administrator
+	mStatus_NoRouter                  = -65566,
+	mStatus_PollingMode               = -65567,
+	mStatus_Timeout                   = -65568,
+	// -65568 to -65786 currently unused; available for allocation
+
+	// tcp connection status
+	mStatus_ConnPending       = -65787,
+	mStatus_ConnFailed        = -65788,
+	mStatus_ConnEstablished   = -65789,
+
+	// Non-error values:
+	mStatus_GrowCache         = -65790,
+	mStatus_ConfigChanged     = -65791,
+	mStatus_MemFree           = -65792		// Last value: 0xFFFE FF00
+	// mStatus_MemFree is the last legal mDNS error code, at the end of the range allocated for mDNS
+	};
+
+typedef mDNSs32 mStatus;
+
+// RFC 1034/1035 specify that a domain label consists of a length byte plus up to 63 characters
+#define MAX_DOMAIN_LABEL 63
+typedef struct { mDNSu8 c[ 64]; } domainlabel;		// One label: length byte and up to 63 characters
+
+// RFC 1034/1035/2181 specify that a domain name (length bytes and data bytes) may be up to 255 bytes long,
+// plus the terminating zero at the end makes 256 bytes total in the on-the-wire format.
+#define MAX_DOMAIN_NAME 256
+typedef struct { mDNSu8 c[256]; } domainname;		// Up to 256 bytes of length-prefixed domainlabels
+
+typedef struct { mDNSu8 c[256]; } UTF8str255;		// Null-terminated C string
+
+// The longest legal textual form of a DNS name is 1009 bytes, including the C-string terminating NULL at the end.
+// Explanation:
+// When a native domainname object is converted to printable textual form using ConvertDomainNameToCString(),
+// non-printing characters are represented in the conventional DNS way, as '\ddd', where ddd is a three-digit decimal number.
+// The longest legal domain name is 256 bytes, in the form of four labels as shown below:
+// Length byte, 63 data bytes, length byte, 63 data bytes, length byte, 63 data bytes, length byte, 62 data bytes, zero byte.
+// Each label is encoded textually as characters followed by a trailing dot.
+// If every character has to be represented as a four-byte escape sequence, then this makes the maximum textual form four labels
+// plus the C-string terminating NULL as shown below:
+// 63*4+1 + 63*4+1 + 63*4+1 + 62*4+1 + 1 = 1009.
+// Note that MAX_ESCAPED_DOMAIN_LABEL is not normally used: If you're only decoding a single label, escaping is usually not required.
+// It is for domain names, where dots are used as label separators, that proper escaping is vital.
+#define MAX_ESCAPED_DOMAIN_LABEL 254
+#define MAX_ESCAPED_DOMAIN_NAME 1009
+
+// MAX_REVERSE_MAPPING_NAME
+// For IPv4: "123.123.123.123.in-addr.arpa."  30 bytes including terminating NUL
+// For IPv6: "x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.x.ip6.arpa."  74 bytes including terminating NUL
+
+#define MAX_REVERSE_MAPPING_NAME_V4 30
+#define MAX_REVERSE_MAPPING_NAME_V6 74
+#define MAX_REVERSE_MAPPING_NAME    74
+
+// Most records have a TTL of 75 minutes, so that their 80% cache-renewal query occurs once per hour.
+// For records containing a hostname (in the name on the left, or in the rdata on the right),
+// like A, AAAA, reverse-mapping PTR, and SRV, we use a two-minute TTL by default, because we don't want
+// them to hang around for too long in the cache if the host in question crashes or otherwise goes away.
+
+#define kStandardTTL (3600UL * 100 / 80)
+#define kHostNameTTL 120UL
+
+// Some applications want to register their SRV records with a lower ttl so that in case the server
+// using a dynamic port number restarts, the clients will not have stale information for more than
+// 10 seconds
+
+#define kHostNameSmallTTL 10UL
+
+
+// Multicast DNS uses announcements (gratuitous responses) to update peer caches.
+// This means it is feasible to use relatively larger TTL values than we might otherwise
+// use, because we have a cache coherency protocol to keep the peer caches up to date.
+// With Unicast DNS, once an authoritative server gives a record with a certain TTL value to a client
+// or caching server, that client or caching server is entitled to hold onto the record until its TTL
+// expires, and has no obligation to contact the authoritative server again until that time arrives.
+// This means that whereas Multicast DNS can use announcements to pre-emptively update stale data
+// before it would otherwise have expired, standard Unicast DNS (not using LLQs) has no equivalent
+// mechanism, and TTL expiry is the *only* mechanism by which stale data gets deleted. Because of this,
+// we currently limit the TTL to ten seconds in such cases where no dynamic cache updating is possible.
+#define kStaticCacheTTL 10
+
+#define DefaultTTLforRRType(X) (((X) == kDNSType_A || (X) == kDNSType_AAAA || (X) == kDNSType_SRV) ? kHostNameTTL : kStandardTTL)
+
+typedef struct AuthRecord_struct AuthRecord;
+typedef struct ServiceRecordSet_struct ServiceRecordSet;
+typedef struct CacheRecord_struct CacheRecord;
+typedef struct CacheGroup_struct CacheGroup;
+typedef struct AuthGroup_struct AuthGroup;
+typedef struct DNSQuestion_struct DNSQuestion;
+typedef struct ZoneData_struct ZoneData;
+typedef struct mDNS_struct mDNS;
+typedef struct mDNS_PlatformSupport_struct mDNS_PlatformSupport;
+typedef struct NATTraversalInfo_struct NATTraversalInfo;
+
+// Structure to abstract away the differences between TCP/SSL sockets, and one for UDP sockets
+// The actual definition of these structures appear in the appropriate platform support code
+typedef struct TCPSocket_struct TCPSocket;
+typedef struct UDPSocket_struct UDPSocket;
+
+// ***************************************************************************
+#if 0
+#pragma mark -
+#pragma mark - DNS Message structures
+#endif
+
+#define mDNS_numZones   numQuestions
+#define mDNS_numPrereqs numAnswers
+#define mDNS_numUpdates numAuthorities
+
+typedef packedstruct
+	{
+	mDNSOpaque16 id;
+	mDNSOpaque16 flags;
+	mDNSu16 numQuestions;
+	mDNSu16 numAnswers;
+	mDNSu16 numAuthorities;
+	mDNSu16 numAdditionals;
+	} DNSMessageHeader;
+
+// We can send and receive packets up to 9000 bytes (Ethernet Jumbo Frame size, if that ever becomes widely used)
+// However, in the normal case we try to limit packets to 1500 bytes so that we don't get IP fragmentation on standard Ethernet
+// 40 (IPv6 header) + 8 (UDP header) + 12 (DNS message header) + 1440 (DNS message body) = 1500 total
+#define AbsoluteMaxDNSMessageData 8940
+#define NormalMaxDNSMessageData 1440
+typedef packedstruct
+	{
+	DNSMessageHeader h;						// Note: Size 12 bytes
+	mDNSu8 data[AbsoluteMaxDNSMessageData];	// 40 (IPv6) + 8 (UDP) + 12 (DNS header) + 8940 (data) = 9000
+	} DNSMessage;
+
+typedef struct tcpInfo_t
+	{
+	mDNS             *m;
+	TCPSocket        *sock;
+	DNSMessage        request;
+	int               requestLen;
+	DNSQuestion      *question;   // For queries
+	AuthRecord       *rr;         // For record updates
+	mDNSAddr          Addr;
+	mDNSIPPort        Port;
+	mDNSIPPort        SrcPort;
+	DNSMessage       *reply;
+	mDNSu16           replylen;
+	unsigned long     nread;
+	int               numReplies;
+	} tcpInfo_t;
+
+// ***************************************************************************
+#if 0
+#pragma mark -
+#pragma mark - Other Packet Format Structures
+#endif
+
+typedef packedstruct
+	{
+	mDNSEthAddr  dst;
+	mDNSEthAddr  src;
+	mDNSOpaque16 ethertype;
+	} EthernetHeader;		// 14 bytes
+
+typedef packedstruct
+	{
+	mDNSOpaque16 hrd;
+	mDNSOpaque16 pro;
+	mDNSu8       hln;
+	mDNSu8       pln;
+	mDNSOpaque16 op;
+	mDNSEthAddr  sha;
+	mDNSv4Addr   spa;
+	mDNSEthAddr  tha;
+	mDNSv4Addr   tpa;
+	} ARP_EthIP;			// 28 bytes
+
+typedef packedstruct
+	{
+	mDNSu8       vlen;
+	mDNSu8       tos;
+	mDNSu16      totlen;
+	mDNSOpaque16 id;
+	mDNSOpaque16 flagsfrags;
+	mDNSu8       ttl;
+	mDNSu8       protocol;	// Payload type: 0x06 = TCP, 0x11 = UDP
+	mDNSu16      checksum;
+	mDNSv4Addr   src;
+	mDNSv4Addr   dst;
+	} IPv4Header;			// 20 bytes
+
+typedef packedstruct
+	{
+	mDNSu32      vcf;		// Version, Traffic Class, Flow Label
+	mDNSu16      len;		// Payload Length
+	mDNSu8       pro;		// Type of next header: 0x06 = TCP, 0x11 = UDP, 0x3A = ICMPv6
+	mDNSu8       ttl;		// Hop Limit
+	mDNSv6Addr   src;
+	mDNSv6Addr   dst;
+	} IPv6Header;			// 40 bytes
+
+typedef packedstruct
+	{
+	mDNSv6Addr   src;
+	mDNSv6Addr   dst;
+	mDNSOpaque32 len;
+	mDNSOpaque32 pro;
+	} IPv6PseudoHeader;		// 40 bytes
+
+typedef union
+	{
+	mDNSu8       bytes[20];
+	ARP_EthIP    arp;
+	IPv4Header   v4;
+	IPv6Header   v6;
+	} NetworkLayerPacket;
+
+typedef packedstruct
+	{
+	mDNSIPPort   src;
+	mDNSIPPort   dst;
+	mDNSu32      seq;
+	mDNSu32      ack;
+	mDNSu8       offset;
+	mDNSu8       flags;
+	mDNSu16      window;
+	mDNSu16      checksum;
+	mDNSu16      urgent;
+	} TCPHeader;			// 20 bytes; IP protocol type 0x06
+
+typedef packedstruct
+	{
+	mDNSIPPort   src;
+	mDNSIPPort   dst;
+	mDNSu16      len;		// Length including UDP header (i.e. minimum value is 8 bytes)
+	mDNSu16      checksum;
+	} UDPHeader;			// 8 bytes; IP protocol type 0x11
+
+typedef packedstruct
+	{
+	mDNSu8       type;		// 0x87 == Neighbor Solicitation, 0x88 == Neighbor Advertisement
+	mDNSu8       code;
+	mDNSu16      checksum;
+	mDNSu32      flags_res;	// R/S/O flags and reserved bits
+	mDNSv6Addr   target;
+	// Typically 8 bytes of options are also present
+	} IPv6NDP;				// 24 bytes or more; IP protocol type 0x3A
+
+#define NDP_Sol 0x87
+#define NDP_Adv 0x88
+
+#define NDP_Router    0x80
+#define NDP_Solicited 0x40
+#define NDP_Override  0x20
+
+#define NDP_SrcLL 1
+#define NDP_TgtLL 2
+
+typedef union
+	{
+	mDNSu8       bytes[20];
+	TCPHeader    tcp;
+	UDPHeader    udp;
+	IPv6NDP      ndp;
+	} TransportLayerPacket;
+
+typedef packedstruct
+	{
+	mDNSOpaque64 InitiatorCookie;
+	mDNSOpaque64 ResponderCookie;
+	mDNSu8       NextPayload;
+	mDNSu8       Version;
+	mDNSu8       ExchangeType;
+	mDNSu8       Flags;
+	mDNSOpaque32 MessageID;
+	mDNSu32      Length;
+	} IKEHeader;			// 28 bytes
+
+// ***************************************************************************
+#if 0
+#pragma mark -
+#pragma mark - Resource Record structures
+#endif
+
+// Authoritative Resource Records:
+// There are four basic types: Shared, Advisory, Unique, Known Unique
+
+// * Shared Resource Records do not have to be unique
+// -- Shared Resource Records are used for DNS-SD service PTRs
+// -- It is okay for several hosts to have RRs with the same name but different RDATA
+// -- We use a random delay on responses to reduce collisions when all the hosts respond to the same query
+// -- These RRs typically have moderately high TTLs (e.g. one hour)
+// -- These records are announced on startup and topology changes for the benefit of passive listeners
+// -- These records send a goodbye packet when deregistering
+//
+// * Advisory Resource Records are like Shared Resource Records, except they don't send a goodbye packet
+//
+// * Unique Resource Records should be unique among hosts within any given mDNS scope
+// -- The majority of Resource Records are of this type
+// -- If two entities on the network have RRs with the same name but different RDATA, this is a conflict
+// -- Responses may be sent immediately, because only one host should be responding to any particular query
+// -- These RRs typically have low TTLs (e.g. a few minutes)
+// -- On startup and after topology changes, a host issues queries to verify uniqueness
+
+// * Known Unique Resource Records are treated like Unique Resource Records, except that mDNS does
+// not have to verify their uniqueness because this is already known by other means (e.g. the RR name
+// is derived from the host's IP or Ethernet address, which is already known to be a unique identifier).
+
+// Summary of properties of different record types:
+// Probe?    Does this record type send probes before announcing?
+// Conflict? Does this record type react if we observe an apparent conflict?
+// Goodbye?  Does this record type send a goodbye packet on departure?
+//
+//               Probe? Conflict? Goodbye? Notes
+// Unregistered                            Should not appear in any list (sanity check value)
+// Shared         No      No       Yes     e.g. Service PTR record
+// Deregistering  No      No       Yes     Shared record about to announce its departure and leave the list
+// Advisory       No      No       No
+// Unique         Yes     Yes      No      Record intended to be unique -- will probe to verify
+// Verified       Yes     Yes      No      Record has completed probing, and is verified unique
+// KnownUnique    No      Yes      No      Record is assumed by other means to be unique
+
+// Valid lifecycle of a record:
+// Unregistered ->                   Shared      -> Deregistering -(goodbye)-> Unregistered
+// Unregistered ->                   Advisory                               -> Unregistered
+// Unregistered -> Unique -(probe)-> Verified                               -> Unregistered
+// Unregistered ->                   KnownUnique                            -> Unregistered
+
+// Each Authoritative kDNSRecordType has only one bit set. This makes it easy to quickly see if a record
+// is one of a particular set of types simply by performing the appropriate bitwise masking operation.
+
+// Cache Resource Records (received from the network):
+// There are four basic types: Answer, Unique Answer, Additional, Unique Additional
+// Bit 7 (the top bit) of kDNSRecordType is always set for Cache Resource Records; always clear for Authoritative Resource Records
+// Bit 6 (value 0x40) is set for answer records; clear for authority/additional records
+// Bit 5 (value 0x20) is set for records received with the kDNSClass_UniqueRRSet
+
+enum
+	{
+	kDNSRecordTypeUnregistered     = 0x00,	// Not currently in any list
+	kDNSRecordTypeDeregistering    = 0x01,	// Shared record about to announce its departure and leave the list
+
+	kDNSRecordTypeUnique           = 0x02,	// Will become a kDNSRecordTypeVerified when probing is complete
+
+	kDNSRecordTypeAdvisory         = 0x04,	// Like Shared, but no goodbye packet
+	kDNSRecordTypeShared           = 0x08,	// Shared means record name does not have to be unique -- use random delay on responses
+
+	kDNSRecordTypeVerified         = 0x10,	// Unique means mDNS should check that name is unique (and then send immediate responses)
+	kDNSRecordTypeKnownUnique      = 0x20,	// Known Unique means mDNS can assume name is unique without checking
+	                                        // For Dynamic Update records, Known Unique means the record must already exist on the server.
+	kDNSRecordTypeUniqueMask       = (kDNSRecordTypeUnique | kDNSRecordTypeVerified | kDNSRecordTypeKnownUnique),
+	kDNSRecordTypeActiveSharedMask = (kDNSRecordTypeAdvisory         | kDNSRecordTypeShared),
+	kDNSRecordTypeActiveUniqueMask = (kDNSRecordTypeVerified         | kDNSRecordTypeKnownUnique),
+	kDNSRecordTypeActiveMask       = (kDNSRecordTypeActiveSharedMask | kDNSRecordTypeActiveUniqueMask),
+
+	kDNSRecordTypePacketAdd        = 0x80,	// Received in the Additional  Section of a DNS Response
+	kDNSRecordTypePacketAddUnique  = 0x90,	// Received in the Additional  Section of a DNS Response with kDNSClass_UniqueRRSet set
+	kDNSRecordTypePacketAuth       = 0xA0,	// Received in the Authorities Section of a DNS Response
+	kDNSRecordTypePacketAuthUnique = 0xB0,	// Received in the Authorities Section of a DNS Response with kDNSClass_UniqueRRSet set
+	kDNSRecordTypePacketAns        = 0xC0,	// Received in the Answer      Section of a DNS Response
+	kDNSRecordTypePacketAnsUnique  = 0xD0,	// Received in the Answer      Section of a DNS Response with kDNSClass_UniqueRRSet set
+
+	kDNSRecordTypePacketNegative   = 0xF0,	// Pseudo-RR generated to cache non-existence results like NXDomain
+
+	kDNSRecordTypePacketUniqueMask = 0x10	// True for PacketAddUnique, PacketAnsUnique, PacketAuthUnique, kDNSRecordTypePacketNegative
+	};
+
+typedef packedstruct { mDNSu16 priority; mDNSu16 weight; mDNSIPPort port; domainname target;   } rdataSRV;
+typedef packedstruct { mDNSu16 preference;                                domainname exchange; } rdataMX;
+typedef packedstruct { domainname mbox; domainname txt;                                        } rdataRP;
+typedef packedstruct { mDNSu16 preference; domainname map822; domainname mapx400;              } rdataPX;
+
+typedef packedstruct
+	{
+	domainname mname;
+	domainname rname;
+	mDNSs32 serial;		// Modular counter; increases when zone changes
+	mDNSu32 refresh;	// Time in seconds that a slave waits after successful replication of the database before it attempts replication again
+	mDNSu32 retry;		// Time in seconds that a slave waits after an unsuccessful replication attempt before it attempts replication again
+	mDNSu32 expire;		// Time in seconds that a slave holds on to old data while replication attempts remain unsuccessful
+	mDNSu32 min;		// Nominally the minimum record TTL for this zone, in seconds; also used for negative caching.
+	} rdataSOA;
+
+// EDNS Option Code registrations are recorded in the "DNS EDNS0 Options" section of
+// <http://www.iana.org/assignments/dns-parameters>
+
+#define kDNSOpt_LLQ   1
+#define kDNSOpt_Lease 2
+#define kDNSOpt_NSID  3
+#define kDNSOpt_Owner 4
+
+typedef struct
+	{
+	mDNSu16      vers;
+	mDNSu16      llqOp;
+	mDNSu16      err;	// Or UDP reply port, in setup request
+	// Note: In the in-memory form, there's typically a two-byte space here, so that the following 64-bit id is word-aligned
+	mDNSOpaque64 id;
+	mDNSu32      llqlease;
+	} LLQOptData;
+
+typedef struct
+	{
+	mDNSu8       vers;		// Version number of this Owner OPT record
+	mDNSs8       seq;		// Sleep/wake epoch
+	mDNSEthAddr  HMAC;		// Host's primary identifier (e.g. MAC of on-board Ethernet)
+	mDNSEthAddr  IMAC;		// Interface's MAC address (if different to primary MAC)
+	mDNSOpaque48 password;	// Optional password
+	} OwnerOptData;
+
+// Note: rdataOPT format may be repeated an arbitrary number of times in a single resource record
+typedef packedstruct
+	{
+	mDNSu16 opt;
+	mDNSu16 optlen;
+	union { LLQOptData llq; mDNSu32 updatelease; OwnerOptData owner; } u;
+	} rdataOPT;
+
+// Space needed to put OPT records into a packet:
+// Header      11 bytes (name 1, type 2, class 2, TTL 4, length 2)
+// LLQ rdata   18 bytes (opt 2, len 2, vers 2, op 2, err 2, id 8, lease 4)
+// Lease rdata  8 bytes (opt 2, len 2, lease 4)
+// Owner rdata 12-24    (opt 2, len 2, owner 8-20)
+
+#define DNSOpt_Header_Space                 11
+#define DNSOpt_LLQData_Space               (4 + 2 + 2 + 2 + 8 + 4)
+#define DNSOpt_LeaseData_Space             (4 + 4)
+#define DNSOpt_OwnerData_ID_Space          (4 + 2 + 6)
+#define DNSOpt_OwnerData_ID_Wake_Space     (4 + 2 + 6 + 6)
+#define DNSOpt_OwnerData_ID_Wake_PW4_Space (4 + 2 + 6 + 6 + 4)
+#define DNSOpt_OwnerData_ID_Wake_PW6_Space (4 + 2 + 6 + 6 + 6)
+
+#define ValidOwnerLength(X) (	(X) == DNSOpt_OwnerData_ID_Space          - 4 || \
+								(X) == DNSOpt_OwnerData_ID_Wake_Space     - 4 || \
+								(X) == DNSOpt_OwnerData_ID_Wake_PW4_Space - 4 || \
+								(X) == DNSOpt_OwnerData_ID_Wake_PW6_Space - 4    )
+
+#define DNSOpt_Owner_Space(A,B) (mDNSSameEthAddress((A),(B)) ? DNSOpt_OwnerData_ID_Space : DNSOpt_OwnerData_ID_Wake_Space)
+
+#define DNSOpt_Data_Space(O) (                                  \
+	(O)->opt == kDNSOpt_LLQ   ? DNSOpt_LLQData_Space   :        \
+	(O)->opt == kDNSOpt_Lease ? DNSOpt_LeaseData_Space :        \
+	(O)->opt == kDNSOpt_Owner ? DNSOpt_Owner_Space(&(O)->u.owner.HMAC, &(O)->u.owner.IMAC) : 0x10000)
+
+// A maximal NSEC record is:
+//   256 bytes domainname 'nextname'
+// + 256 * 34 = 8704 bytes of bitmap data
+// = 8960 bytes total
+// For now we only support NSEC records encoding DNS types 0-255 and ignore the nextname (we always set it to be the same as the rrname),
+// which gives us a fixed in-memory size of 32 bytes (256 bits)
+typedef struct
+	{
+	mDNSu8 bitmap[32];
+	} rdataNSEC;
+
+// StandardAuthRDSize is 264 (256+8), which is large enough to hold a maximum-sized SRV record (6 + 256 bytes)
+// MaximumRDSize is 8K the absolute maximum we support (at least for now)
+#define StandardAuthRDSize 264
+#define MaximumRDSize 8192
+
+// InlineCacheRDSize is 68
+// Records received from the network with rdata this size or less have their rdata stored right in the CacheRecord object
+// Records received from the network with rdata larger than this have additional storage allocated for the rdata
+// A quick unscientific sample from a busy network at Apple with lots of machines revealed this:
+// 1461 records in cache
+// 292 were one-byte TXT records
+// 136 were four-byte A records
+// 184 were sixteen-byte AAAA records
+// 780 were various PTR, TXT and SRV records from 12-64 bytes
+// Only 69 records had rdata bigger than 64 bytes
+// Note that since CacheRecord object and a CacheGroup object are allocated out of the same pool, it's sensible to
+// have them both be the same size. Making one smaller without making the other smaller won't actually save any memory.
+#define InlineCacheRDSize 68
+
+// On 64-bit, the pointers in a CacheRecord are bigger, and that creates 8 bytes more space for the name in a CacheGroup
+#if ENABLE_MULTI_PACKET_QUERY_SNOOPING
+	#if defined(_ILP64) || defined(__ILP64__) || defined(_LP64) || defined(__LP64__) || defined(_WIN64)
+	#define InlineCacheGroupNameSize 160
+	#else
+	#define InlineCacheGroupNameSize 148
+	#endif
+#else
+	#if defined(_ILP64) || defined(__ILP64__) || defined(_LP64) || defined(__LP64__) || defined(_WIN64)
+	#define InlineCacheGroupNameSize 144
+	#else
+	#define InlineCacheGroupNameSize 132
+	#endif
+#endif
+
+// The RDataBody union defines the common rdata types that fit into our 264-byte limit
+typedef union
+	{
+	mDNSu8      data[StandardAuthRDSize];
+	mDNSv4Addr  ipv4;		// For 'A' record
+	domainname  name;		// For PTR, NS, CNAME, DNAME
+	UTF8str255  txt;
+	rdataMX     mx;
+	mDNSv6Addr  ipv6;		// For 'AAAA' record
+	rdataSRV    srv;
+	rdataOPT    opt[2];		// For EDNS0 OPT record; RDataBody may contain multiple variable-length rdataOPT objects packed together
+	rdataNSEC   nsec;
+	} RDataBody;
+
+// The RDataBody2 union is the same as above, except it includes fields for the larger types like soa, rp, px
+typedef union
+	{
+	mDNSu8      data[StandardAuthRDSize];
+	mDNSv4Addr  ipv4;		// For 'A' record
+	domainname  name;		// For PTR, NS, CNAME, DNAME
+	rdataSOA    soa;		// This is large; not included in the normal RDataBody definition
+	UTF8str255  txt;
+	rdataMX     mx;
+	rdataRP     rp;			// This is large; not included in the normal RDataBody definition
+	rdataPX     px;			// This is large; not included in the normal RDataBody definition
+	mDNSv6Addr  ipv6;		// For 'AAAA' record
+	rdataSRV    srv;
+	rdataOPT    opt[2];		// For EDNS0 OPT record; RDataBody may contain multiple variable-length rdataOPT objects packed together
+	rdataNSEC   nsec;
+	} RDataBody2;
+
+typedef struct
+	{
+	mDNSu16    MaxRDLength;	// Amount of storage allocated for rdata (usually sizeof(RDataBody))
+	mDNSu16    padding;		// So that RDataBody is aligned on 32-bit boundary
+	RDataBody  u;
+	} RData;
+
+// sizeofRDataHeader should be 4 bytes
+#define sizeofRDataHeader (sizeof(RData) - sizeof(RDataBody))
+
+// RData_small is a smaller version of the RData object, used for inline data storage embedded in a CacheRecord_struct
+typedef struct
+	{
+	mDNSu16    MaxRDLength;	// Storage allocated for data (may be greater than InlineCacheRDSize if additional storage follows this object)
+	mDNSu16    padding;		// So that data is aligned on 32-bit boundary
+	mDNSu8     data[InlineCacheRDSize];
+	} RData_small;
+
+// Note: Within an mDNSRecordCallback mDNS all API calls are legal except mDNS_Init(), mDNS_Exit(), mDNS_Execute()
+typedef void mDNSRecordCallback(mDNS *const m, AuthRecord *const rr, mStatus result);
+
+// Note:
+// Restrictions: An mDNSRecordUpdateCallback may not make any mDNS API calls.
+// The intent of this callback is to allow the client to free memory, if necessary.
+// The internal data structures of the mDNS code may not be in a state where mDNS API calls may be made safely.
+typedef void mDNSRecordUpdateCallback(mDNS *const m, AuthRecord *const rr, RData *OldRData, mDNSu16 OldRDLen);
+
+// ***************************************************************************
+#if 0
+#pragma mark -
+#pragma mark - NAT Traversal structures and constants
+#endif
+
+#define NATMAP_MAX_RETRY_INTERVAL    ((mDNSPlatformOneSecond * 60) * 15)    // Max retry interval is 15 minutes
+#define NATMAP_MIN_RETRY_INTERVAL     (mDNSPlatformOneSecond * 2)           // Min retry interval is 2 seconds
+#define NATMAP_INIT_RETRY             (mDNSPlatformOneSecond / 4)           // start at 250ms w/ exponential decay
+#define NATMAP_DEFAULT_LEASE          (60 * 60 * 2)                         // 2 hour lease life in seconds
+#define NATMAP_VERS 0
+
+typedef enum
+	{
+	NATOp_AddrRequest    = 0,
+	NATOp_MapUDP         = 1,
+	NATOp_MapTCP         = 2,
+	
+	NATOp_AddrResponse   = 0x80 | 0,
+	NATOp_MapUDPResponse = 0x80 | 1,
+	NATOp_MapTCPResponse = 0x80 | 2,
+	} NATOp_t;
+
+enum
+	{
+	NATErr_None    = 0,
+	NATErr_Vers    = 1,
+	NATErr_Refused = 2,
+	NATErr_NetFail = 3,
+	NATErr_Res     = 4,
+	NATErr_Opcode  = 5
+	};
+
+typedef mDNSu16 NATErr_t;
+
+typedef packedstruct
+	{
+	mDNSu8 vers;
+	mDNSu8 opcode;
+	} NATAddrRequest;
+
+typedef packedstruct
+	{
+	mDNSu8     vers;
+	mDNSu8     opcode;
+	mDNSu16    err;
+	mDNSu32    upseconds;		// Time since last NAT engine reboot, in seconds
+	mDNSv4Addr ExtAddr;
+	} NATAddrReply;
+
+typedef packedstruct
+	{
+	mDNSu8 vers;
+	mDNSu8 opcode;
+	mDNSOpaque16 unused;
+	mDNSIPPort intport;
+	mDNSIPPort extport;
+	mDNSu32    NATReq_lease;
+	} NATPortMapRequest;
+
+typedef packedstruct
+	{
+	mDNSu8     vers;
+	mDNSu8     opcode;
+	mDNSu16    err;
+	mDNSu32    upseconds;		// Time since last NAT engine reboot, in seconds
+	mDNSIPPort intport;
+	mDNSIPPort extport;
+	mDNSu32    NATRep_lease;
+	} NATPortMapReply;
+
+typedef enum
+	{
+	LNTDiscoveryOp      = 1,
+	LNTExternalAddrOp   = 2,
+	LNTPortMapOp        = 3,
+	LNTPortMapDeleteOp  = 4
+	} LNTOp_t;
+
+#define LNT_MAXBUFSIZE 8192
+typedef struct tcpLNTInfo_struct tcpLNTInfo;
+struct tcpLNTInfo_struct
+	{
+	tcpLNTInfo       *next;
+	mDNS             *m;
+	NATTraversalInfo *parentNATInfo;	// pointer back to the parent NATTraversalInfo
+	TCPSocket        *sock;
+	LNTOp_t           op;				// operation performed using this connection
+	mDNSAddr          Address;			// router address
+	mDNSIPPort        Port;				// router port
+	mDNSu8           *Request;			// xml request to router
+	int               requestLen;
+	mDNSu8           *Reply;			// xml reply from router
+	int               replyLen;
+	unsigned long     nread;			// number of bytes read so far
+	int               retries;			// number of times we've tried to do this port mapping
+	};
+
+typedef void (*NATTraversalClientCallback)(mDNS *m, NATTraversalInfo *n);
+
+// if m->timenow <  ExpiryTime then we have an active mapping, and we'll renew halfway to expiry
+// if m->timenow >= ExpiryTime then our mapping has expired, and we're trying to create one
+
+struct NATTraversalInfo_struct
+	{
+	// Internal state fields. These are used internally by mDNSCore; the client layer needn't be concerned with them.
+	NATTraversalInfo           *next;
+
+	mDNSs32                     ExpiryTime;			// Time this mapping expires, or zero if no mapping
+	mDNSs32                     retryInterval;		// Current interval, between last packet we sent and the next one
+	mDNSs32                     retryPortMap;		// If Protocol is nonzero, time to send our next mapping packet
+	mStatus                     NewResult;			// New error code; will be copied to Result just prior to invoking callback
+
+#ifdef _LEGACY_NAT_TRAVERSAL_
+	tcpLNTInfo                  tcpInfo;			// Legacy NAT traversal (UPnP) TCP connection
+#endif
+
+	// Result fields: When the callback is invoked these fields contain the answers the client is looking for
+	// When the callback is invoked ExternalPort is *usually* set to be the same the same as RequestedPort, except:
+	// (a) When we're behind a NAT gateway with port mapping disabled, ExternalPort is reported as zero to
+	//     indicate that we don't currently have a working mapping (but RequestedPort retains the external port
+	//     we'd like to get, the next time we meet an accomodating NAT gateway willing to give us one).
+	// (b) When we have a routable non-RFC1918 address, we don't *need* a port mapping, so ExternalPort
+	//     is reported as the same as our InternalPort, since that is effectively our externally-visible port too.
+	//     Again, RequestedPort retains the external port we'd like to get the next time we find ourself behind a NAT gateway.
+	// To improve stability of port mappings, RequestedPort is updated any time we get a successful
+	// mapping response from the NAT-PMP or UPnP gateway. For example, if we ask for port 80, and
+	// get assigned port 81, then thereafter we'll contine asking for port 81.
+	mDNSInterfaceID             InterfaceID;
+	mDNSv4Addr                  ExternalAddress;	// Initially set to onesIPv4Addr, until first callback
+	mDNSIPPort                  ExternalPort;
+	mDNSu32                     Lifetime;
+	mStatus                     Result;
+
+	// Client API fields: The client must set up these fields *before* making any NAT traversal API calls
+	mDNSu8                      Protocol;			// NATOp_MapUDP or NATOp_MapTCP, or zero if just requesting the external IP address
+	mDNSIPPort                  IntPort;			// Client's internal port number (doesn't change)
+	mDNSIPPort                  RequestedPort;		// Requested external port; may be updated with actual value assigned by gateway
+	mDNSu32                     NATLease;			// Requested lifetime in seconds (doesn't change)
+	NATTraversalClientCallback  clientCallback;
+	void                       *clientContext;
+	};
+
+enum
+	{
+	DNSServer_Untested = 0,
+	DNSServer_Passed   = 1,
+	DNSServer_Failed   = 2,
+	DNSServer_Disabled = 3
+	};
+
+enum
+	{
+	DNSServer_FlagDelete = 1,
+	DNSServer_FlagNew    = 2
+	};
+
+enum
+	{
+	McastResolver_FlagDelete = 1,
+	McastResolver_FlagNew    = 2
+	};
+
+typedef struct McastResolver
+	{
+	struct McastResolver *next;
+	mDNSInterfaceID interface;
+	mDNSu32         flags;		// Set when we're planning to delete this from the list
+	domainname      domain;
+	mDNSu32         timeout;	// timeout value for questions
+	} McastResolver;
+
+typedef struct DNSServer
+	{
+	struct DNSServer *next;
+	mDNSInterfaceID interface;	// For specialized uses; we can have DNS servers reachable over specific interfaces
+	mDNSAddr        addr;
+	mDNSIPPort      port;
+	mDNSOpaque16    testid;
+	mDNSu32         flags;		// Set when we're planning to delete this from the list
+	mDNSu32         teststate;	// Have we sent bug-detection query to this server?
+	mDNSs32         lasttest;	// Time we sent last bug-detection query to this server
+	domainname      domain;		// name->server matching for "split dns"
+	mDNSs32			penaltyTime; // amount of time this server is penalized
+	mDNSBool		scoped;		// interface should be matched against question only
+								// if scoped is set
+	mDNSu32			timeout;	// timeout value for questions
+	} DNSServer;
+
+typedef struct							// Size is 36 bytes when compiling for 32-bit; 48 when compiling for 64-bit
+	{
+	mDNSu8           RecordType;		// See enum above
+	mDNSu16          rrtype;
+	mDNSu16          rrclass;
+	mDNSu32          rroriginalttl;		// In seconds
+	mDNSu16          rdlength;			// Size of the raw rdata, in bytes, in the on-the-wire format
+										// (In-memory storage may be larger, for structures containing 'holes', like SOA,
+										// or smaller, for NSEC where we don't bother storing the nextname field)
+	mDNSu16          rdestimate;		// Upper bound on on-the-wire size of rdata after name compression
+	mDNSu32          namehash;			// Name-based (i.e. case-insensitive) hash of name
+	mDNSu32          rdatahash;			// For rdata containing domain name (e.g. PTR, SRV, CNAME etc.), case-insensitive name hash
+										// else, for all other rdata, 32-bit hash of the raw rdata
+										// Note: This requirement is important. Various routines like AddAdditionalsToResponseList(),
+										// ReconfirmAntecedents(), etc., use rdatahash as a pre-flight check to see
+										// whether it's worth doing a full SameDomainName() call. If the rdatahash
+										// is not a correct case-insensitive name hash, they'll get false negatives.
+
+	// Grouping pointers together at the end of the structure improves the memory layout efficiency
+	mDNSInterfaceID  InterfaceID;		// Set if this RR is specific to one interface
+										// For records received off the wire, InterfaceID is *always* set to the receiving interface
+										// For our authoritative records, InterfaceID is usually zero, except for those few records
+										// that are interface-specific (e.g. address records, especially linklocal addresses)
+	const domainname *name;
+	RData           *rdata;				// Pointer to storage for this rdata
+	DNSServer       *rDNSServer;		// Unicast DNS server authoritative for this entry;null for multicast
+	} ResourceRecord;
+
+// Unless otherwise noted, states may apply to either independent record registrations or service registrations
+typedef enum
+	{
+	regState_Zero              = 0,
+	regState_Pending           = 1,     // update sent, reply not received
+	regState_Registered        = 2,     // update sent, reply received
+	regState_DeregPending      = 3,     // dereg sent, reply not received
+	regState_Unregistered      = 4,     // not in any list
+	regState_Refresh           = 5,     // outstanding refresh (or target change) message
+	regState_NATMap            = 6,     // establishing NAT port mapping 
+	regState_UpdatePending     = 7,     // update in flight as result of mDNS_Update call
+	regState_NoTarget          = 8,     // SRV Record registration pending registration of hostname 
+	regState_NATError          = 9     // unable to complete NAT traversal
+	} regState_t;
+
+enum
+	{
+	Target_Manual = 0,
+	Target_AutoHost = 1,
+	Target_AutoHostAndNATMAP = 2
+	};
+
+typedef enum
+	{
+	mergeState_Zero = 0,
+	mergeState_DontMerge = 1  // Set on fatal error conditions to disable merging
+	} mergeState_t;
+
+struct AuthGroup_struct				// Header object for a list of AuthRecords with the same name
+	{
+	AuthGroup      *next;				// Next AuthGroup object in this hash table bucket
+	mDNSu32         namehash;			// Name-based (i.e. case insensitive) hash of name
+	AuthRecord     *members;			// List of CacheRecords with this same name
+	AuthRecord    **rrauth_tail;		// Tail end of that list
+	domainname     *name;				// Common name for all AuthRecords in this list
+	AuthRecord     *NewLocalOnlyRecords;
+	// Size to here is 20 bytes when compiling 32-bit; 40 bytes when compiling 64-bit
+	mDNSu8          namestorage[InlineCacheGroupNameSize];
+	};
+
+#define AUTH_HASH_SLOTS 499
+#define FORALL_AUTHRECORDS(SLOT,AG,AR)                           	\
+	for ((SLOT) = 0; (SLOT) < AUTH_HASH_SLOTS; (SLOT)++)         	\
+		for ((AG)=m->rrauth.rrauth_hash[(SLOT)]; (AG); (AG)=(AG)->next) \
+			for ((AR) = (AG)->members; (AR); (AR)=(AR)->next)
+
+typedef union AuthEntity_union AuthEntity;
+union AuthEntity_union { AuthEntity *next; AuthGroup ag; };
+typedef struct {
+	mDNSu32 rrauth_size;				// Total number of available auth entries
+	mDNSu32 rrauth_totalused;			// Number of auth entries currently occupied
+	mDNSu32 rrauth_report;
+	mDNSu8  rrauth_lock;				// For debugging: Set at times when these lists may not be modified
+	AuthEntity *rrauth_free;
+	AuthGroup *rrauth_hash[AUTH_HASH_SLOTS];
+}AuthHash;
+
+// AuthRecordAny includes mDNSInterface_Any and interface specific auth records (anything
+// other than P2P or LocalOnly)
+typedef enum 
+	{
+	AuthRecordAny, 				// registered for *Any, NOT including P2P interfaces
+	AuthRecordAnyIncludeP2P, 	// registered for *Any, including P2P interfaces
+	AuthRecordLocalOnly, 
+	AuthRecordP2P				// discovered over D2D/P2P framework
+	} AuthRecType;
+
+struct AuthRecord_struct
+	{
+	// For examples of how to set up this structure for use in mDNS_Register(),
+	// see mDNS_AdvertiseInterface() or mDNS_RegisterService().
+	// Basically, resrec and persistent metadata need to be set up before calling mDNS_Register().
+	// mDNS_SetupResourceRecord() is avaliable as a helper routine to set up most fields to sensible default values for you
+
+	AuthRecord     *next;				// Next in list; first element of structure for efficiency reasons
+	// Field Group 1: Common ResourceRecord fields
+	ResourceRecord  resrec;				// 36 bytes when compiling for 32-bit; 48 when compiling for 64-bit
+
+	// Field Group 2: Persistent metadata for Authoritative Records
+	AuthRecord     *Additional1;		// Recommended additional record to include in response (e.g. SRV for PTR record)
+	AuthRecord     *Additional2;		// Another additional (e.g. TXT for PTR record)
+	AuthRecord     *DependentOn;		// This record depends on another for its uniqueness checking
+	AuthRecord     *RRSet;				// This unique record is part of an RRSet
+	mDNSRecordCallback *RecordCallback;	// Callback function to call for state changes, and to free memory asynchronously on deregistration
+	void           *RecordContext;		// Context parameter for the callback function
+	mDNSu8          AutoTarget;			// Set if the target of this record (PTR, CNAME, SRV, etc.) is our host name
+	mDNSu8          AllowRemoteQuery;	// Set if we allow hosts not on the local link to query this record
+	mDNSu8          ForceMCast;			// Set by client to advertise solely via multicast, even for apparently unicast names
+
+	OwnerOptData    WakeUp;				// WakeUp.HMAC.l[0] nonzero indicates that this is a Sleep Proxy record
+	mDNSAddr        AddressProxy;		// For reverse-mapping Sleep Proxy PTR records, address in question
+	mDNSs32         TimeRcvd;			// In platform time units
+	mDNSs32         TimeExpire;			// In platform time units
+	AuthRecType     ARType;             // LocalOnly, P2P or Normal ?
+
+	// Field Group 3: Transient state for Authoritative Records
+	mDNSu8          Acknowledged;		// Set if we've given the success callback to the client
+	mDNSu8          ProbeCount;			// Number of probes remaining before this record is valid (kDNSRecordTypeUnique)
+	mDNSu8          AnnounceCount;		// Number of announcements remaining (kDNSRecordTypeShared)
+	mDNSu8          RequireGoodbye;		// Set if this RR has been announced on the wire and will require a goodbye packet
+	mDNSu8          AnsweredLocalQ;		// Set if this AuthRecord has been delivered to any local question (LocalOnly or mDNSInterface_Any)
+	mDNSu8          IncludeInProbe;		// Set if this RR is being put into a probe right now
+	mDNSu8          ImmedUnicast;		// Set if we may send our response directly via unicast to the requester
+	mDNSInterfaceID SendNSECNow;		// Set if we need to generate associated NSEC data for this rrname
+	mDNSInterfaceID ImmedAnswer;		// Someone on this interface issued a query we need to answer (all-ones for all interfaces)
+#if MDNS_LOG_ANSWER_SUPPRESSION_TIMES
+	mDNSs32         ImmedAnswerMarkTime;
+#endif
+	mDNSInterfaceID ImmedAdditional;	// Hint that we might want to also send this record, just to be helpful
+	mDNSInterfaceID SendRNow;			// The interface this query is being sent on right now
+	mDNSv4Addr      v4Requester;		// Recent v4 query for this record, or all-ones if more than one recent query
+	mDNSv6Addr      v6Requester;		// Recent v6 query for this record, or all-ones if more than one recent query
+	AuthRecord     *NextResponse;		// Link to the next element in the chain of responses to generate
+	const mDNSu8   *NR_AnswerTo;		// Set if this record was selected by virtue of being a direct answer to a question
+	AuthRecord     *NR_AdditionalTo;	// Set if this record was selected by virtue of being additional to another
+	mDNSs32         ThisAPInterval;		// In platform time units: Current interval for announce/probe
+	mDNSs32         LastAPTime;			// In platform time units: Last time we sent announcement/probe
+	mDNSs32         LastMCTime;			// Last time we multicast this record (used to guard against packet-storm attacks)
+	mDNSInterfaceID LastMCInterface;	// Interface this record was multicast on at the time LastMCTime was recorded
+	RData          *NewRData;			// Set if we are updating this record with new rdata
+	mDNSu16         newrdlength;		// ... and the length of the new RData
+	mDNSRecordUpdateCallback *UpdateCallback;
+	mDNSu32         UpdateCredits;		// Token-bucket rate limiting of excessive updates
+	mDNSs32         NextUpdateCredit;	// Time next token is added to bucket
+	mDNSs32         UpdateBlocked;		// Set if update delaying is in effect
+
+	// Field Group 4: Transient uDNS state for Authoritative Records
+	regState_t   state;			// Maybe combine this with resrec.RecordType state? Right now it's ambiguous and confusing.
+								// e.g. rr->resrec.RecordType can be kDNSRecordTypeUnregistered,
+								// and rr->state can be regState_Unregistered
+								// What if we find one of those statements is true and the other false? What does that mean?
+	mDNSBool     uselease;		// dynamic update contains (should contain) lease option
+	mDNSs32      expire;		// In platform time units: expiration of lease (-1 for static)
+	mDNSBool     Private;		// If zone is private, DNS updates may have to be encrypted to prevent eavesdropping
+	mDNSOpaque16 updateid;		// Identifier to match update request and response -- also used when transferring records to Sleep Proxy
+	const domainname *zone;		// the zone that is updated
+	ZoneData  *nta;
+	struct tcpInfo_t *tcp;
+	NATTraversalInfo  NATinfo;
+	mDNSBool SRVChanged;       // temporarily deregistered service because its SRV target or port changed
+	mergeState_t  mState;      // Unicast Record Registrations merge state
+	mDNSu8		  refreshCount; // Number of refreshes to the server
+	mStatus		  updateError;  // Record update resulted in Error ?
+
+	// uDNS_UpdateRecord support fields
+	// Do we really need all these in *addition* to NewRData and newrdlength above?
+	void *UpdateContext;	// Context parameter for the update callback function
+	mDNSu16 OrigRDLen;		// previously registered, being deleted
+	mDNSu16 InFlightRDLen;	// currently being registered
+	mDNSu16 QueuedRDLen;	// pending operation (re-transmitting if necessary) THEN register the queued update
+	RData *OrigRData;
+	RData *InFlightRData;
+	RData *QueuedRData;
+
+	// Field Group 5: Large data objects go at the end
+	domainname      namestorage;
+	RData           rdatastorage;		// Normally the storage is right here, except for oversized records
+	// rdatastorage MUST be the last thing in the structure -- when using oversized AuthRecords, extra bytes
+	// are appended after the end of the AuthRecord, logically augmenting the size of the rdatastorage
+	// DO NOT ADD ANY MORE FIELDS HERE
+	};
+
+// IsLocalDomain alone is not sufficient to determine that a record is mDNS or uDNS. By default domain names within
+// the "local" pseudo-TLD (and within the IPv4 and IPv6 link-local reverse mapping domains) are automatically treated
+// as mDNS records, but it is also possible to force any record (even those not within one of the inherently local
+// domains) to be handled as an mDNS record by setting the ForceMCast flag, or by setting a non-zero InterfaceID.
+// For example, the reverse-mapping PTR record created in AdvertiseInterface sets the ForceMCast flag, since it points to
+// a dot-local hostname, and therefore it would make no sense to register this record with a wide-area Unicast DNS server.
+// The same applies to Sleep Proxy records, which we will answer for when queried via mDNS, but we never want to try
+// to register them with a wide-area Unicast DNS server -- and we probably don't have the required credentials anyway.
+// Currently we have no concept of a wide-area uDNS record scoped to a particular interface, so if the InterfaceID is
+// nonzero we treat this the same as ForceMCast.
+// Note: Question_uDNS(Q) is used in *only* one place -- on entry to mDNS_StartQuery_internal, to decide whether to set TargetQID.
+// Everywhere else in the code, the determination of whether a question is unicast is made by checking to see if TargetQID is nonzero.
+#define AuthRecord_uDNS(R) ((R)->resrec.InterfaceID == mDNSInterface_Any && !(R)->ForceMCast && !IsLocalDomain((R)->resrec.name))
+#define Question_uDNS(Q)   ((Q)->InterfaceID == mDNSInterface_Unicast || \
+	((Q)->InterfaceID != mDNSInterface_LocalOnly && (Q)->InterfaceID != mDNSInterface_P2P && !(Q)->ForceMCast && !IsLocalDomain(&(Q)->qname)))
+
+#define RRLocalOnly(rr) ((rr)->ARType == AuthRecordLocalOnly || (rr)->ARType == AuthRecordP2P)
+
+#define RRAny(rr) ((rr)->ARType == AuthRecordAny || (rr)->ARType == AuthRecordAnyIncludeP2P)
+
+// Question (A or AAAA) that is suppressed currently because IPv4 or IPv6 address
+// is not available locally for A or AAAA question respectively
+#define QuerySuppressed(Q) ((Q)->SuppressUnusable && (Q)->SuppressQuery)
+
+#define PrivateQuery(Q) ((Q)->AuthInfo && (Q)->AuthInfo->AutoTunnel)
+
+// Normally we always lookup the cache and /etc/hosts before sending the query on the wire. For single label
+// queries (A and AAAA) that are unqualified (indicated by AppendSearchDomains), we want to append search
+// domains before we try them as such
+#define ApplySearchDomainsFirst(q) ((q)->AppendSearchDomains && (CountLabels(&((q)->qname))) == 1)
+
+// Wrapper struct for Auth Records for higher-level code that cannot use the AuthRecord's ->next pointer field
+typedef struct ARListElem
+	{
+	struct ARListElem *next;
+	AuthRecord ar;          // Note: Must be last element of structure, to accomodate oversized AuthRecords
+	} ARListElem;
+
+struct CacheGroup_struct				// Header object for a list of CacheRecords with the same name
+	{
+	CacheGroup     *next;				// Next CacheGroup object in this hash table bucket
+	mDNSu32         namehash;			// Name-based (i.e. case insensitive) hash of name
+	CacheRecord    *members;			// List of CacheRecords with this same name
+	CacheRecord   **rrcache_tail;		// Tail end of that list
+	domainname     *name;				// Common name for all CacheRecords in this list
+	// Size to here is 20 bytes when compiling 32-bit; 40 bytes when compiling 64-bit
+	mDNSu8          namestorage[InlineCacheGroupNameSize];
+	};
+
+
+struct CacheRecord_struct
+	{
+	CacheRecord    *next;				// Next in list; first element of structure for efficiency reasons
+	ResourceRecord  resrec;				// 36 bytes when compiling for 32-bit; 48 when compiling for 64-bit
+
+	// Transient state for Cache Records
+	CacheRecord    *NextInKAList;		// Link to the next element in the chain of known answers to send
+	mDNSs32         TimeRcvd;			// In platform time units
+	mDNSs32         DelayDelivery;		// Set if we want to defer delivery of this answer to local clients
+	mDNSs32         NextRequiredQuery;	// In platform time units
+	mDNSs32         LastUsed;			// In platform time units
+	DNSQuestion    *CRActiveQuestion;	// Points to an active question referencing this answer. Can never point to a NewQuestion.
+	mDNSu32         UnansweredQueries;	// Number of times we've issued a query for this record without getting an answer
+	mDNSs32         LastUnansweredTime;	// In platform time units; last time we incremented UnansweredQueries
+#if ENABLE_MULTI_PACKET_QUERY_SNOOPING
+	mDNSu32         MPUnansweredQ;		// Multi-packet query handling: Number of times we've seen a query for this record
+	mDNSs32         MPLastUnansweredQT;	// Multi-packet query handling: Last time we incremented MPUnansweredQ
+	mDNSu32         MPUnansweredKA;		// Multi-packet query handling: Number of times we've seen this record in a KA list
+	mDNSBool        MPExpectingKA;		// Multi-packet query handling: Set when we increment MPUnansweredQ; allows one KA
+#endif
+	CacheRecord    *NextInCFList;		// Set if this is in the list of records we just received with the cache flush bit set
+	// Size to here is 76 bytes when compiling 32-bit; 104 bytes when compiling 64-bit
+	RData_small     smallrdatastorage;	// Storage for small records is right here (4 bytes header + 68 bytes data = 72 bytes)
+	};
+
+// Storage sufficient to hold either a CacheGroup header or a CacheRecord
+// -- for best efficiency (to avoid wasted unused storage) they should be the same size
+typedef union CacheEntity_union CacheEntity;
+union CacheEntity_union { CacheEntity *next; CacheGroup cg; CacheRecord cr; };
+
+typedef struct
+	{
+	CacheRecord r;
+	mDNSu8 _extradata[MaximumRDSize-InlineCacheRDSize];		// Glue on the necessary number of extra bytes
+	domainname namestorage;									// Needs to go *after* the extra rdata bytes
+	} LargeCacheRecord;
+
+typedef struct HostnameInfo
+	{
+	struct HostnameInfo *next;
+	NATTraversalInfo natinfo;
+	domainname fqdn;
+	AuthRecord arv4;                          // registered IPv4 address record
+	AuthRecord arv6;                          // registered IPv6 address record
+	mDNSRecordCallback *StatusCallback;       // callback to deliver success or error code to client layer
+	const void *StatusContext;                // Client Context
+	} HostnameInfo;
+
+typedef struct ExtraResourceRecord_struct ExtraResourceRecord;
+struct ExtraResourceRecord_struct
+	{
+	ExtraResourceRecord *next;
+	mDNSu32 ClientID;  // Opaque ID field to be used by client to map an AddRecord call to a set of Extra records
+	AuthRecord r;
+	// Note: Add any additional fields *before* the AuthRecord in this structure, not at the end.
+	// In some cases clients can allocate larger chunks of memory and set r->rdata->MaxRDLength to indicate
+	// that this extra memory is available, which would result in any fields after the AuthRecord getting smashed
+	};
+
+// Note: Within an mDNSServiceCallback mDNS all API calls are legal except mDNS_Init(), mDNS_Exit(), mDNS_Execute()
+typedef void mDNSServiceCallback(mDNS *const m, ServiceRecordSet *const sr, mStatus result);
+
+// A ServiceRecordSet has no special meaning to the core code of the Multicast DNS protocol engine;
+// it is just a convenience structure to group together the records that make up a standard service
+// registration so that they can be allocted and deallocted together as a single memory object.
+// It contains its own ServiceCallback+ServiceContext to report aggregate results up to the next layer of software above.
+// It also contains:
+//  * the basic PTR/SRV/TXT triplet used to represent any DNS-SD service
+//  * the "_services" PTR record for service enumeration
+//  * the optional list of SubType PTR records
+//  * the optional list of additional records attached to the service set (e.g. iChat pictures)
+
+struct ServiceRecordSet_struct
+	{
+	// These internal state fields are used internally by mDNSCore; the client layer needn't be concerned with them.
+	// No fields need to be set up by the client prior to calling mDNS_RegisterService();
+	// all required data is passed as parameters to that function.
+	mDNSServiceCallback *ServiceCallback;
+	void                *ServiceContext;
+	mDNSBool             Conflict;	// Set if this record set was forcibly deregistered because of a conflict
+
+	ExtraResourceRecord *Extras;	// Optional list of extra AuthRecords attached to this service registration
+	mDNSu32              NumSubTypes;
+	AuthRecord          *SubTypes;
+	AuthRecord           RR_ADV;	// e.g. _services._dns-sd._udp.local. PTR _printer._tcp.local.
+	AuthRecord           RR_PTR;	// e.g. _printer._tcp.local.        PTR Name._printer._tcp.local.
+	AuthRecord           RR_SRV;	// e.g. Name._printer._tcp.local.   SRV 0 0 port target
+	AuthRecord           RR_TXT;	// e.g. Name._printer._tcp.local.   TXT PrintQueueName
+	// Don't add any fields after AuthRecord RR_TXT.
+	// This is where the implicit extra space goes if we allocate a ServiceRecordSet containing an oversized RR_TXT record
+	};
+
+// ***************************************************************************
+#if 0
+#pragma mark -
+#pragma mark - Question structures
+#endif
+
+// We record the last eight instances of each duplicate query
+// This gives us v4/v6 on each of Ethernet, AirPort and Firewire, and two free slots "for future expansion"
+// If the host has more active interfaces that this it is not fatal -- duplicate question suppression will degrade gracefully.
+// Since we will still remember the last eight, the busiest interfaces will still get the effective duplicate question suppression.
+#define DupSuppressInfoSize 8
+
+typedef struct
+	{
+	mDNSs32               Time;
+	mDNSInterfaceID       InterfaceID;
+	mDNSs32               Type;				// v4 or v6?
+	} DupSuppressInfo;
+
+typedef enum
+	{
+	LLQ_InitialRequest    = 1,
+	LLQ_SecondaryRequest  = 2,
+	LLQ_Established       = 3,
+	LLQ_Poll              = 4
+	} LLQ_State;
+
+// LLQ constants
+#define kLLQ_Vers      1
+#define kLLQ_DefLease  7200 // 2 hours
+#define kLLQ_MAX_TRIES 3    // retry an operation 3 times max
+#define kLLQ_INIT_RESEND 2 // resend an un-ack'd packet after 2 seconds, then double for each additional
+// LLQ Operation Codes
+#define kLLQOp_Setup     1
+#define kLLQOp_Refresh   2
+#define kLLQOp_Event     3
+
+// LLQ Errror Codes
+enum
+	{
+	LLQErr_NoError    = 0,
+	LLQErr_ServFull   = 1,
+	LLQErr_Static     = 2,
+	LLQErr_FormErr    = 3,
+	LLQErr_NoSuchLLQ  = 4,
+	LLQErr_BadVers    = 5,
+	LLQErr_UnknownErr = 6
+	};
+
+enum { NoAnswer_Normal = 0, NoAnswer_Suspended = 1, NoAnswer_Fail = 2 };
+
+#define HMAC_LEN    64
+#define HMAC_IPAD   0x36
+#define HMAC_OPAD   0x5c
+#define MD5_LEN     16
+
+#define AutoTunnelUnregistered(X) (                                              \
+	(X)->AutoTunnelHostRecord.resrec.RecordType == kDNSRecordTypeUnregistered && \
+	(X)->AutoTunnelDeviceInfo.resrec.RecordType == kDNSRecordTypeUnregistered && \
+	(X)->AutoTunnelService.   resrec.RecordType == kDNSRecordTypeUnregistered && \
+	(X)->AutoTunnel6Record.   resrec.RecordType == kDNSRecordTypeUnregistered    )
+
+// Internal data structure to maintain authentication information
+typedef struct DomainAuthInfo
+	{
+	struct DomainAuthInfo *next;
+	mDNSs32          deltime;				// If we're planning to delete this DomainAuthInfo, the time we want it deleted
+	const char*      AutoTunnel;            // If NULL, this is not an AutoTunnel DAI. Otherwise, this is prepended to the IPSec identifier
+	AuthRecord       AutoTunnelHostRecord;	// User-visible hostname; used as SRV target for AutoTunnel services
+	AuthRecord       AutoTunnelTarget;		// Opaque hostname of tunnel endpoint; used as SRV target for AutoTunnelService record
+	AuthRecord       AutoTunnelDeviceInfo;	// Device info of tunnel endpoint
+	AuthRecord       AutoTunnelService;		// Service record (possibly NAT-Mapped) of IKE daemon implementing tunnel endpoint
+	AuthRecord       AutoTunnel6Record;     // AutoTunnel AAAA Record obtained from Connectivityd
+	NATTraversalInfo AutoTunnelNAT;
+	domainname       domain;
+	domainname       keyname;
+	domainname       hostname;
+	mDNSIPPort       port;
+	char             b64keydata[32];
+	mDNSu8           keydata_ipad[HMAC_LEN];	// padded key for inner hash rounds
+	mDNSu8           keydata_opad[HMAC_LEN];	// padded key for outer hash rounds
+	} DomainAuthInfo;
+
+// Note: Within an mDNSQuestionCallback mDNS all API calls are legal except mDNS_Init(), mDNS_Exit(), mDNS_Execute()
+typedef enum { QC_rmv = 0, QC_add = 1, QC_addnocache = 2 } QC_result;
+typedef void mDNSQuestionCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord);
+
+#define NextQSendTime(Q)  ((Q)->LastQTime + (Q)->ThisQInterval)
+#define ActiveQuestion(Q) ((Q)->ThisQInterval > 0 && !(Q)->DuplicateOf)
+#define TimeToSendThisQuestion(Q,time) (ActiveQuestion(Q) && (time) - NextQSendTime(Q) >= 0)
+
+struct DNSQuestion_struct
+	{
+	// Internal state fields. These are used internally by mDNSCore; the client layer needn't be concerned with them.
+	DNSQuestion          *next;
+	mDNSu32               qnamehash;
+	mDNSs32               DelayAnswering;	// Set if we want to defer answering this question until the cache settles
+	mDNSs32               LastQTime;		// Last scheduled transmission of this Q on *all* applicable interfaces
+	mDNSs32               ThisQInterval;	// LastQTime + ThisQInterval is the next scheduled transmission of this Q
+											// ThisQInterval > 0 for an active question;
+											// ThisQInterval = 0 for a suspended question that's still in the list
+											// ThisQInterval = -1 for a cancelled question (should not still be in list)
+	mDNSs32               ExpectUnicastResp;// Set when we send a query with the kDNSQClass_UnicastResponse bit set
+	mDNSs32               LastAnswerPktNum;	// The sequence number of the last response packet containing an answer to this Q
+	mDNSu32               RecentAnswerPkts;	// Number of answers since the last time we sent this query
+	mDNSu32               CurrentAnswers;	// Number of records currently in the cache that answer this question
+	mDNSu32               LargeAnswers;		// Number of answers with rdata > 1024 bytes
+	mDNSu32               UniqueAnswers;	// Number of answers received with kDNSClass_UniqueRRSet bit set
+	mDNSInterfaceID       FlappingInterface1;// Set when an interface goes away, to flag if remove events are delivered for this Q
+	mDNSInterfaceID       FlappingInterface2;// Set when an interface goes away, to flag if remove events are delivered for this Q
+	DomainAuthInfo       *AuthInfo;			// Non-NULL if query is currently being done using Private DNS
+	DNSQuestion          *DuplicateOf;
+	DNSQuestion          *NextInDQList;
+	DupSuppressInfo       DupSuppress[DupSuppressInfoSize];
+	mDNSInterfaceID       SendQNow;			// The interface this query is being sent on right now
+	mDNSBool              SendOnAll;		// Set if we're sending this question on all active interfaces
+	mDNSu32               RequestUnicast;	// Non-zero if we want to send query with kDNSQClass_UnicastResponse bit set
+	mDNSs32               LastQTxTime;		// Last time this Q was sent on one (but not necessarily all) interfaces
+	mDNSu32               CNAMEReferrals;	// Count of how many CNAME redirections we've done
+	mDNSBool              SuppressQuery;    // This query should be suppressed and not sent on the wire 
+	mDNSu8                LOAddressAnswers; // Number of answers from the local only auth records that are
+		                                    // answering A, AAAA and CNAME (/etc/hosts)
+	mDNSu8                WakeOnResolveCount; // Number of wakes that should be sent on resolve
+	mDNSs32               StopTime;			// Time this question should be stopped by giving them a negative answer
+
+	// Wide Area fields. These are used internally by the uDNS core
+	UDPSocket            *LocalSocket;
+	mDNSBool             deliverAddEvents;  // Change in DNSSserver requiring to deliver ADD events
+	DNSServer            *qDNSServer;		// Caching server for this query (in the absence of an SRV saying otherwise)
+	mDNSOpaque64          validDNSServers;  // Valid DNSServers for this question
+	mDNSu16              noServerResponse;  // At least one server did not respond.
+	mDNSu16              triedAllServersOnce; // Tried all DNS servers once
+	mDNSu8               unansweredQueries;// The number of unanswered queries to this server
+
+	ZoneData             *nta;				// Used for getting zone data for private or LLQ query
+	mDNSAddr              servAddr;			// Address and port learned from _dns-llq, _dns-llq-tls or _dns-query-tls SRV query
+	mDNSIPPort            servPort;
+	struct tcpInfo_t *tcp;
+	mDNSIPPort            tcpSrcPort;		// Local Port TCP packet received on;need this as tcp struct is disposed
+											// by tcpCallback before calling into mDNSCoreReceive
+	mDNSu8                NoAnswer;			// Set if we want to suppress answers until tunnel setup has completed
+
+	// LLQ-specific fields. These fields are only meaningful when LongLived flag is set
+	LLQ_State             state;
+	mDNSu32               ReqLease;			// seconds (relative)
+	mDNSs32               expire;			// ticks (absolute)
+	mDNSs16               ntries;           // for UDP: the number of packets sent for this LLQ state
+	                                       // for TCP: there is some ambiguity in the use of this variable, but in general, it is
+	                                       //          the number of TCP/TLS connection attempts for this LLQ state, or
+	                                       //          the number of packets sent for this TCP/TLS connection
+	mDNSOpaque64          id;
+
+	// Client API fields: The client must set up these fields *before* calling mDNS_StartQuery()
+	mDNSInterfaceID       InterfaceID;		// Non-zero if you want to issue queries only on a single specific IP interface
+	mDNSAddr              Target;			// Non-zero if you want to direct queries to a specific unicast target address
+	mDNSIPPort            TargetPort;		// Must be set if Target is set
+	mDNSOpaque16          TargetQID;		// Must be set if Target is set
+	domainname            qname;
+	mDNSu16               qtype;
+	mDNSu16               qclass;
+	mDNSBool              LongLived;        // Set by client for calls to mDNS_StartQuery to indicate LLQs to unicast layer.
+	mDNSBool              ExpectUnique;		// Set by client if it's expecting unique RR(s) for this question, not shared RRs
+	mDNSBool              ForceMCast;		// Set by client to force mDNS query, even for apparently uDNS names
+	mDNSBool              ReturnIntermed;	// Set by client to request callbacks for intermediate CNAME/NXDOMAIN results
+	mDNSBool              SuppressUnusable; // Set by client to suppress unusable queries to be sent on the wire
+	mDNSBool              RetryWithSearchDomains;	// Retry with search domains if there is no entry in the cache or AuthRecords
+	mDNSu8                TimeoutQuestion; // Timeout this question if there is no reply in configured time
+	mDNSu8                WakeOnResolve; // Send wakeup on resolve
+	mDNSs8                SearchListIndex;  // Index into SearchList; Used by the client layer but not touched by core
+	mDNSs8                AppendSearchDomains; // Search domains can be appended for this query
+	mDNSs8                AppendLocalSearchDomains; // Search domains ending in .local can be appended for this query
+	domainname           *qnameOrig;       // Copy of the original question name if it is not fully qualified
+	mDNSQuestionCallback *QuestionCallback;
+	void                 *QuestionContext;
+	};
+
+typedef struct
+	{
+	// Client API fields: The client must set up name and InterfaceID *before* calling mDNS_StartResolveService()
+	// When the callback is invoked, ip, port, TXTlen and TXTinfo will have been filled in with the results learned from the network.
+	domainname      name;
+	mDNSInterfaceID InterfaceID;		// ID of the interface the response was received on
+	mDNSAddr        ip;					// Remote (destination) IP address where this service can be accessed
+	mDNSIPPort      port;				// Port where this service can be accessed
+	mDNSu16         TXTlen;
+	mDNSu8          TXTinfo[2048];		// Additional demultiplexing information (e.g. LPR queue name)
+	} ServiceInfo;
+
+// Note: Within an mDNSServiceInfoQueryCallback mDNS all API calls are legal except mDNS_Init(), mDNS_Exit(), mDNS_Execute()
+typedef struct ServiceInfoQuery_struct ServiceInfoQuery;
+typedef void mDNSServiceInfoQueryCallback(mDNS *const m, ServiceInfoQuery *query);
+struct ServiceInfoQuery_struct
+	{
+	// Internal state fields. These are used internally by mDNSCore; the client layer needn't be concerned with them.
+	// No fields need to be set up by the client prior to calling mDNS_StartResolveService();
+	// all required data is passed as parameters to that function.
+	// The ServiceInfoQuery structure memory is working storage for mDNSCore to discover the requested information
+	// and place it in the ServiceInfo structure. After the client has called mDNS_StopResolveService(), it may
+	// dispose of the ServiceInfoQuery structure while retaining the results in the ServiceInfo structure.
+	DNSQuestion                   qSRV;
+	DNSQuestion                   qTXT;
+	DNSQuestion                   qAv4;
+	DNSQuestion                   qAv6;
+	mDNSu8                        GotSRV;
+	mDNSu8                        GotTXT;
+	mDNSu8                        GotADD;
+	mDNSu32                       Answers;
+	ServiceInfo                  *info;
+	mDNSServiceInfoQueryCallback *ServiceInfoQueryCallback;
+	void                         *ServiceInfoQueryContext;
+	};
+
+typedef enum { ZoneServiceUpdate, ZoneServiceQuery, ZoneServiceLLQ } ZoneService;
+
+typedef void ZoneDataCallback(mDNS *const m, mStatus err, const ZoneData *result);
+
+struct ZoneData_struct
+	{
+	domainname       ChildName;			// Name for which we're trying to find the responsible server
+	ZoneService      ZoneService;		// Which service we're seeking for this zone (update, query, or LLQ)
+	domainname       *CurrentSOA;		// Points to somewhere within ChildName
+	domainname       ZoneName;			// Discovered result: Left-hand-side of SOA record
+	mDNSu16          ZoneClass;			// Discovered result: DNS Class from SOA record
+	domainname       Host;				// Discovered result: Target host from SRV record
+	mDNSIPPort       Port;				// Discovered result: Update port, query port, or LLQ port from SRV record
+	mDNSAddr         Addr;				// Discovered result: Address of Target host from SRV record
+	mDNSBool         ZonePrivate;		// Discovered result: Does zone require encrypted queries?
+	ZoneDataCallback *ZoneDataCallback;	// Caller-specified function to be called upon completion
+	void             *ZoneDataContext;
+	DNSQuestion      question;			// Storage for any active question
+	};
+
+extern ZoneData *StartGetZoneData(mDNS *const m, const domainname *const name, const ZoneService target, ZoneDataCallback callback, void *callbackInfo);
+extern void CancelGetZoneData(mDNS *const m, ZoneData *nta);
+extern mDNSBool IsGetZoneDataQuestion(DNSQuestion *q);
+
+typedef struct DNameListElem
+	{
+	struct DNameListElem *next;
+	mDNSu32 uid;
+	domainname name;
+	} DNameListElem;
+
+#if APPLE_OSX_mDNSResponder
+// Different states that we go through locating the peer
+#define TC_STATE_AAAA_PEER			0x000000001		/* Peer's BTMM IPv6 address */
+#define TC_STATE_AAAA_PEER_RELAY	0x000000002		/* Peer's IPv6 Relay address */
+#define TC_STATE_SRV_PEER			0x000000003		/* Peer's SRV Record corresponding to IPv4 address */
+#define TC_STATE_ADDR_PEER			0x000000004		/* Peer's IPv4 address */
+
+typedef struct ClientTunnel
+	{
+	struct ClientTunnel *next;
+	const char *prefix;
+	domainname dstname;
+	mDNSBool   MarkedForDeletion;
+	mDNSv6Addr loc_inner;
+	mDNSv4Addr loc_outer;
+	mDNSv6Addr loc_outer6;
+	mDNSv6Addr rmt_inner;
+	mDNSv4Addr rmt_outer;
+	mDNSv6Addr rmt_outer6;
+	mDNSIPPort rmt_outer_port;
+	mDNSu16	tc_state;
+	DNSQuestion q;
+	} ClientTunnel;
+#endif
+
+// ***************************************************************************
+#if 0
+#pragma mark -
+#pragma mark - NetworkInterfaceInfo_struct
+#endif
+
+typedef struct NetworkInterfaceInfo_struct NetworkInterfaceInfo;
+
+// A NetworkInterfaceInfo_struct serves two purposes:
+// 1. It holds the address, PTR and HINFO records to advertise a given IP address on a given physical interface
+// 2. It tells mDNSCore which physical interfaces are available; each physical interface has its own unique InterfaceID.
+//    Since there may be multiple IP addresses on a single physical interface,
+//    there may be multiple NetworkInterfaceInfo_structs with the same InterfaceID.
+//    In this case, to avoid sending the same packet n times, when there's more than one
+//    struct with the same InterfaceID, mDNSCore picks one member of the set to be the
+//    active representative of the set; all others have the 'InterfaceActive' flag unset.
+
+struct NetworkInterfaceInfo_struct
+	{
+	// Internal state fields. These are used internally by mDNSCore; the client layer needn't be concerned with them.
+	NetworkInterfaceInfo *next;
+
+	mDNSu8          InterfaceActive;	// Set if interface is sending & receiving packets (see comment above)
+	mDNSu8          IPv4Available;		// If InterfaceActive, set if v4 available on this InterfaceID
+	mDNSu8          IPv6Available;		// If InterfaceActive, set if v6 available on this InterfaceID
+
+	DNSQuestion     NetWakeBrowse;
+	DNSQuestion     NetWakeResolve[3];	// For fault-tolerance, we try up to three Sleep Proxies
+	mDNSAddr        SPSAddr[3];
+	mDNSIPPort      SPSPort[3];
+	mDNSs32         NextSPSAttempt;		// -1 if we're not currently attempting to register with any Sleep Proxy
+	mDNSs32         NextSPSAttemptTime;
+
+	// Standard AuthRecords that every Responder host should have (one per active IP address)
+	AuthRecord RR_A;					// 'A' or 'AAAA' (address) record for our ".local" name
+	AuthRecord RR_PTR;					// PTR (reverse lookup) record
+	AuthRecord RR_HINFO;
+
+	// Client API fields: The client must set up these fields *before* calling mDNS_RegisterInterface()
+	mDNSInterfaceID InterfaceID;		// Identifies physical interface; MUST NOT be 0, -1, or -2
+	mDNSAddr        ip;					// The IPv4 or IPv6 address to advertise
+	mDNSAddr        mask;
+	mDNSEthAddr     MAC;
+	char            ifname[64];			// Windows uses a GUID string for the interface name, which doesn't fit in 16 bytes
+	mDNSu8          Advertise;			// False if you are only searching on this interface
+	mDNSu8          McastTxRx;			// Send/Receive multicast on this { InterfaceID, address family } ?
+	mDNSu8          NetWake;			// Set if Wake-On-Magic-Packet is enabled on this interface
+	mDNSu8          Loopback;			// Set if this is the loopback interface
+	};
+
+#define SLE_DELETE              0x00000001
+#define SLE_WAB_QUERY_STARTED   0x00000002
+
+typedef struct SearchListElem
+	{
+	struct SearchListElem *next;
+	domainname domain;
+	int flag;
+	mDNSInterfaceID InterfaceID;
+	DNSQuestion BrowseQ;
+	DNSQuestion DefBrowseQ;
+	DNSQuestion AutomaticBrowseQ;
+	DNSQuestion RegisterQ;
+	DNSQuestion DefRegisterQ;
+	int	numCfAnswers;
+	ARListElem *AuthRecs;
+	} SearchListElem;
+
+// For domain enumeration and automatic browsing
+// This is the user's DNS search list.
+// In each of these domains we search for our special pointer records (lb._dns-sd._udp.<domain>, etc.)
+// to discover recommended domains for domain enumeration (browse, default browse, registration,
+// default registration) and possibly one or more recommended automatic browsing domains.
+extern SearchListElem *SearchList;		// This really ought to be part of mDNS_struct -- SC
+
+// ***************************************************************************
+#if 0
+#pragma mark -
+#pragma mark - Main mDNS object, used to hold all the mDNS state
+#endif
+
+typedef void mDNSCallback(mDNS *const m, mStatus result);
+
+#define CACHE_HASH_SLOTS 499
+
+enum		// Bit flags -- i.e. values should be 1, 2, 4, 8, etc.
+	{
+	mDNS_KnownBug_LimitedIPv6       = 1,
+	mDNS_KnownBug_LossySyslog       = 2		// <rdar://problem/6561888>
+	};
+
+enum
+	{
+	SleepState_Awake = 0,
+	SleepState_Transferring = 1,
+	SleepState_Sleeping = 2
+	};
+
+struct mDNS_struct
+	{
+	// Internal state fields. These hold the main internal state of mDNSCore;
+	// the client layer needn't be concerned with them.
+	// No fields need to be set up by the client prior to calling mDNS_Init();
+	// all required data is passed as parameters to that function.
+
+	mDNS_PlatformSupport *p;			// Pointer to platform-specific data of indeterminite size
+	mDNSu32  KnownBugs;
+	mDNSBool CanReceiveUnicastOn5353;
+	mDNSBool AdvertiseLocalAddresses;
+	mDNSBool DivertMulticastAdvertisements; // from interfaces that do not advertise local addresses to local-only
+	mStatus mDNSPlatformStatus;
+	mDNSIPPort UnicastPort4;
+	mDNSIPPort UnicastPort6;
+	mDNSEthAddr PrimaryMAC;				// Used as unique host ID
+	mDNSCallback *MainCallback;
+	void         *MainContext;
+
+	// For debugging: To catch and report locking failures
+	mDNSu32 mDNS_busy;					// Incremented between mDNS_Lock/mDNS_Unlock section
+	mDNSu32 mDNS_reentrancy;			// Incremented when calling a client callback
+	mDNSu8  lock_rrcache;				// For debugging: Set at times when these lists may not be modified
+	mDNSu8  lock_Questions;
+	mDNSu8  lock_Records;
+#ifndef MaxMsg
+	#define MaxMsg 160
+#endif
+	char MsgBuffer[MaxMsg];				// Temp storage used while building error log messages
+
+	// Task Scheduling variables
+	mDNSs32  timenow_adjust;			// Correction applied if we ever discover time went backwards
+	mDNSs32  timenow;					// The time that this particular activation of the mDNS code started
+	mDNSs32  timenow_last;				// The time the last time we ran
+	mDNSs32  NextScheduledEvent;		// Derived from values below
+	mDNSs32  ShutdownTime;				// Set when we're shutting down; allows us to skip some unnecessary steps
+	mDNSs32  SuppressSending;			// Don't send local-link mDNS packets during this time
+	mDNSs32  NextCacheCheck;			// Next time to refresh cache record before it expires
+	mDNSs32  NextScheduledQuery;		// Next time to send query in its exponential backoff sequence
+	mDNSs32  NextScheduledProbe;		// Next time to probe for new authoritative record
+	mDNSs32  NextScheduledResponse;		// Next time to send authoritative record(s) in responses
+	mDNSs32  NextScheduledNATOp;		// Next time to send NAT-traversal packets
+	mDNSs32  NextScheduledSPS;			// Next time to purge expiring Sleep Proxy records
+	mDNSs32  RandomQueryDelay;			// For de-synchronization of query packets on the wire
+	mDNSu32  RandomReconfirmDelay;		// For de-synchronization of reconfirmation queries on the wire
+	mDNSs32  PktNum;					// Unique sequence number assigned to each received packet
+	mDNSu8   LocalRemoveEvents;			// Set if we may need to deliver remove events for local-only questions and/or local-only records
+	mDNSu8   SleepState;				// Set if we're sleeping
+	mDNSu8   SleepSeqNum;				// "Epoch number" of our current period of wakefulness
+	mDNSu8   SystemWakeOnLANEnabled;	// Set if we want to register with a Sleep Proxy before going to sleep
+	mDNSu8   SentSleepProxyRegistration;// Set if we registered (or tried to register) with a Sleep Proxy
+	mDNSu8   SystemSleepOnlyIfWakeOnLAN;// Set if we may only sleep if we managed to register with a Sleep Proxy
+	mDNSs32  AnnounceOwner;				// After waking from sleep, include OWNER option in packets until this time
+	mDNSs32  DelaySleep;				// To inhibit re-sleeping too quickly right after wake
+	mDNSs32  SleepLimit;				// Time window to allow deregistrations, etc.,
+										// during which underying platform layer should inhibit system sleep
+	mDNSs32  NextScheduledSPRetry;		// Time next sleep proxy registration action is required.
+										// Only valid if SleepLimit is nonzero and DelaySleep is zero.
+
+	mDNSs32 NextScheduledStopTime;      // Next time to stop a question
+
+	// These fields only required for mDNS Searcher...
+	DNSQuestion *Questions;				// List of all registered questions, active and inactive
+	DNSQuestion *NewQuestions;			// Fresh questions not yet answered from cache
+	DNSQuestion *CurrentQuestion;		// Next question about to be examined in AnswerLocalQuestions()
+	DNSQuestion *LocalOnlyQuestions;	// Questions with InterfaceID set to mDNSInterface_LocalOnly or mDNSInterface_P2P
+	DNSQuestion *NewLocalOnlyQuestions;	// Fresh local-only or P2P questions not yet answered
+	DNSQuestion *RestartQuestion;		// Questions that are being restarted (stop followed by start)
+	mDNSu32 rrcache_size;				// Total number of available cache entries
+	mDNSu32 rrcache_totalused;			// Number of cache entries currently occupied
+	mDNSu32 rrcache_active;				// Number of cache entries currently occupied by records that answer active questions
+	mDNSu32 rrcache_report;
+	CacheEntity *rrcache_free;
+	CacheGroup *rrcache_hash[CACHE_HASH_SLOTS];
+	mDNSs32  rrcache_nextcheck[CACHE_HASH_SLOTS];
+
+	AuthHash rrauth;
+
+	// Fields below only required for mDNS Responder...
+	domainlabel nicelabel;				// Rich text label encoded using canonically precomposed UTF-8
+	domainlabel hostlabel;				// Conforms to RFC 1034 "letter-digit-hyphen" ARPANET host name rules
+	domainname  MulticastHostname;		// Fully Qualified "dot-local" Host Name, e.g. "Foo.local."
+	UTF8str255  HIHardware;
+	UTF8str255  HISoftware;
+	AuthRecord  DeviceInfo;
+	AuthRecord *ResourceRecords;
+	AuthRecord *DuplicateRecords;		// Records currently 'on hold' because they are duplicates of existing records
+	AuthRecord *NewLocalRecords;		// Fresh AuthRecords (public) not yet delivered to our local-only questions
+	AuthRecord *CurrentRecord;			// Next AuthRecord about to be examined
+	mDNSBool    NewLocalOnlyRecords;	// Fresh AuthRecords (local only) not yet delivered to our local questions
+	NetworkInterfaceInfo *HostInterfaces;
+	mDNSs32 ProbeFailTime;
+	mDNSu32 NumFailedProbes;
+	mDNSs32 SuppressProbes;
+
+	// Unicast-specific data
+	mDNSs32           NextuDNSEvent;		// uDNS next event
+	mDNSs32           NextSRVUpdate;        // Time to perform delayed update
+
+	DNSServer        *DNSServers;           // list of DNS servers
+	McastResolver    *McastResolvers;       // list of Mcast Resolvers
+
+	mDNSAddr          Router;
+	mDNSAddr          AdvertisedV4;         // IPv4 address pointed to by hostname
+	mDNSAddr          AdvertisedV6;         // IPv6 address pointed to by hostname
+
+	DomainAuthInfo   *AuthInfoList;         // list of domains requiring authentication for updates
+
+	DNSQuestion       ReverseMap;           // Reverse-map query to find static hostname for service target
+	DNSQuestion       AutomaticBrowseDomainQ;
+	domainname        StaticHostname;       // Current answer to reverse-map query
+	domainname        FQDN;
+	HostnameInfo     *Hostnames;            // List of registered hostnames + hostname metadata
+	mDNSv6Addr        AutoTunnelHostAddr;	// IPv6 address advertised for AutoTunnel services on this machine
+	mDNSBool          AutoTunnelHostAddrActive;
+	// AutoTunnel Relay address has two distinct uses
+	// AutoTunnelRelayAddrIn: If non-zero, it means that this host can be reached (inbound connection) through the relay
+	// AutoTunnelRelayAddrOut: If non-zero, it means that this host can use the relay to reach (outbound connection) the
+	// other hosts through the relay
+	mDNSv6Addr        AutoTunnelRelayAddrIn;
+	mDNSv6Addr        AutoTunnelRelayAddrOut;
+	domainlabel       AutoTunnelLabel;		// Used to construct hostname for *IPv4* address of tunnel endpoints
+
+	mDNSBool          StartWABQueries;		// Start WAB queries for the purpose of domain enumeration
+	mDNSBool          RegisterAutoTunnel6;
+
+	// NAT-Traversal fields
+	NATTraversalInfo  LLQNAT;					// Single shared NAT Traversal to receive inbound LLQ notifications
+	NATTraversalInfo *NATTraversals;
+	NATTraversalInfo *CurrentNATTraversal;
+	mDNSs32           retryIntervalGetAddr;		// delta between time sent and retry
+	mDNSs32           retryGetAddr;				// absolute time when we retry
+	mDNSv4Addr        ExternalAddress;
+
+	UDPSocket        *NATMcastRecvskt;			// For receiving NAT-PMP AddrReply multicasts from router on port 5350
+	mDNSu32           LastNATupseconds;			// NAT engine uptime in seconds, from most recent NAT packet
+	mDNSs32           LastNATReplyLocalTime;	// Local time in ticks when most recent NAT packet was received
+	mDNSu16           LastNATMapResultCode;		// Most recent error code for mappings
+
+	tcpLNTInfo        tcpAddrInfo;				// legacy NAT traversal TCP connection info for external address
+	tcpLNTInfo        tcpDeviceInfo;			// legacy NAT traversal TCP connection info for device info
+	tcpLNTInfo       *tcpInfoUnmapList;			// list of pending unmap requests
+	mDNSInterfaceID   UPnPInterfaceID;
+	UDPSocket        *SSDPSocket;               // For SSDP request/response
+	mDNSBool          SSDPWANPPPConnection;     // whether we should send the SSDP query for WANIPConnection or WANPPPConnection
+	mDNSIPPort        UPnPRouterPort;			// port we send discovery messages to
+	mDNSIPPort        UPnPSOAPPort;				// port we send SOAP messages to
+	mDNSu8           *UPnPRouterURL;			// router's URL string
+	mDNSBool          UPnPWANPPPConnection;     // whether we're using WANIPConnection or WANPPPConnection
+	mDNSu8           *UPnPSOAPURL;				// router's SOAP control URL string
+	mDNSu8           *UPnPRouterAddressString;	// holds both the router's address and port
+	mDNSu8           *UPnPSOAPAddressString;	// holds both address and port for SOAP messages
+
+	// Sleep Proxy Server fields
+	mDNSu8            SPSType;					// 0 = off, 10-99 encodes desirability metric
+	mDNSu8            SPSPortability;			// 10-99
+	mDNSu8            SPSMarginalPower;			// 10-99
+	mDNSu8            SPSTotalPower;			// 10-99
+	mDNSu8            SPSState;					// 0 = off, 1 = running, 2 = shutting down, 3 = suspended during sleep
+	mDNSInterfaceID   SPSProxyListChanged;
+	UDPSocket        *SPSSocket;
+	ServiceRecordSet  SPSRecords;
+	mDNSQuestionCallback *SPSBrowseCallback;    // So the platform layer can do something useful with SPS browse results
+	int               ProxyRecords;				// Total number of records we're holding as proxy
+	#define           MAX_PROXY_RECORDS 10000	/* DOS protection: 400 machines at 25 records each */
+
+#if APPLE_OSX_mDNSResponder
+	ClientTunnel     *TunnelClients;
+	uuid_t           asl_uuid;					// uuid for ASL logging
+	void		    *WCF;
+#endif
+
+	// Fixed storage, to avoid creating large objects on the stack
+	// The imsg is declared as a union with a pointer type to enforce CPU-appropriate alignment
+	union { DNSMessage m; void *p; } imsg;  // Incoming message received from wire
+	DNSMessage        omsg;                 // Outgoing message we're building
+	LargeCacheRecord  rec;                  // Resource Record extracted from received message
+	};
+
+#define FORALL_CACHERECORDS(SLOT,CG,CR)                           \
+	for ((SLOT) = 0; (SLOT) < CACHE_HASH_SLOTS; (SLOT)++)         \
+		for ((CG)=m->rrcache_hash[(SLOT)]; (CG); (CG)=(CG)->next) \
+			for ((CR) = (CG)->members; (CR); (CR)=(CR)->next)
+
+// ***************************************************************************
+#if 0
+#pragma mark -
+#pragma mark - Useful Static Constants
+#endif
+
+extern const mDNSInterfaceID mDNSInterface_Any;				// Zero
+extern const mDNSInterfaceID mDNSInterface_LocalOnly;		// Special value
+extern const mDNSInterfaceID mDNSInterface_Unicast;			// Special value
+extern const mDNSInterfaceID mDNSInterfaceMark;				// Special value
+extern const mDNSInterfaceID mDNSInterface_P2P;				// Special value
+
+extern const mDNSIPPort   DiscardPort;
+extern const mDNSIPPort   SSHPort;
+extern const mDNSIPPort   UnicastDNSPort;
+extern const mDNSIPPort   SSDPPort;
+extern const mDNSIPPort   IPSECPort;
+extern const mDNSIPPort   NSIPCPort;
+extern const mDNSIPPort   NATPMPAnnouncementPort;
+extern const mDNSIPPort   NATPMPPort;
+extern const mDNSIPPort   DNSEXTPort;
+extern const mDNSIPPort   MulticastDNSPort;
+extern const mDNSIPPort   LoopbackIPCPort;
+extern const mDNSIPPort   PrivateDNSPort;
+
+extern const OwnerOptData    zeroOwner;
+
+extern const mDNSIPPort      zeroIPPort;
+extern const mDNSv4Addr      zerov4Addr;
+extern const mDNSv6Addr      zerov6Addr;
+extern const mDNSEthAddr     zeroEthAddr;
+extern const mDNSv4Addr      onesIPv4Addr;
+extern const mDNSv6Addr      onesIPv6Addr;
+extern const mDNSEthAddr     onesEthAddr;
+extern const mDNSAddr        zeroAddr;
+
+extern const mDNSv4Addr   AllDNSAdminGroup;
+extern const mDNSv4Addr   AllHosts_v4;
+extern const mDNSv6Addr   AllHosts_v6;
+extern const mDNSv6Addr   NDP_prefix;
+extern const mDNSEthAddr  AllHosts_v6_Eth;
+extern const mDNSAddr     AllDNSLinkGroup_v4;
+extern const mDNSAddr     AllDNSLinkGroup_v6;
+
+extern const mDNSOpaque16 zeroID;
+extern const mDNSOpaque16 onesID;
+extern const mDNSOpaque16 QueryFlags;
+extern const mDNSOpaque16 uQueryFlags;
+extern const mDNSOpaque16 ResponseFlags;
+extern const mDNSOpaque16 UpdateReqFlags;
+extern const mDNSOpaque16 UpdateRespFlags;
+
+extern const mDNSOpaque64 zeroOpaque64;
+
+extern mDNSBool StrictUnicastOrdering;
+extern mDNSu8 NumUnicastDNSServers;
+
+#define localdomain           (*(const domainname *)"\x5" "local")
+#define DeviceInfoName        (*(const domainname *)"\xC" "_device-info" "\x4" "_tcp")
+#define SleepProxyServiceType (*(const domainname *)"\xC" "_sleep-proxy" "\x4" "_udp")
+
+// ***************************************************************************
+#if 0
+#pragma mark -
+#pragma mark - Inline functions
+#endif
+
+#if (defined(_MSC_VER))
+	#define mDNSinline static __inline
+#elif ((__GNUC__ > 2) || ((__GNUC__ == 2) && (__GNUC_MINOR__ >= 9)))
+	#define mDNSinline static inline
+#endif
+
+// If we're not doing inline functions, then this header needs to have the extern declarations
+#if !defined(mDNSinline)
+extern mDNSs32      NonZeroTime(mDNSs32 t);
+extern mDNSu16      mDNSVal16(mDNSOpaque16 x);
+extern mDNSOpaque16 mDNSOpaque16fromIntVal(mDNSu16 v);
+#endif
+
+// If we're compiling the particular C file that instantiates our inlines, then we
+// define "mDNSinline" (to empty string) so that we generate code in the following section
+#if (!defined(mDNSinline) && mDNS_InstantiateInlines)
+#define mDNSinline
+#endif
+
+#ifdef mDNSinline
+
+mDNSinline mDNSs32 NonZeroTime(mDNSs32 t) { if (t) return(t); else return(1); }
+
+mDNSinline mDNSu16 mDNSVal16(mDNSOpaque16 x) { return((mDNSu16)((mDNSu16)x.b[0] <<  8 | (mDNSu16)x.b[1])); }
+
+mDNSinline mDNSOpaque16 mDNSOpaque16fromIntVal(mDNSu16 v)
+	{
+	mDNSOpaque16 x;
+	x.b[0] = (mDNSu8)(v >> 8);
+	x.b[1] = (mDNSu8)(v & 0xFF);
+	return(x);
+	}
+
+#endif
+
+// ***************************************************************************
+#if 0
+#pragma mark -
+#pragma mark - Main Client Functions
+#endif
+
+// Every client should call mDNS_Init, passing in storage for the mDNS object and the mDNS_PlatformSupport object.
+//
+// Clients that are only advertising services should use mDNS_Init_NoCache and mDNS_Init_ZeroCacheSize.
+// Clients that plan to perform queries (mDNS_StartQuery, mDNS_StartBrowse, mDNS_StartResolveService, etc.)
+// need to provide storage for the resource record cache, or the query calls will return 'mStatus_NoCache'.
+// The rrcachestorage parameter is the address of memory for the resource record cache, and
+// the rrcachesize parameter is the number of entries in the CacheRecord array passed in.
+// (i.e. the size of the cache memory needs to be sizeof(CacheRecord) * rrcachesize).
+// OS X 10.3 Panther uses an initial cache size of 64 entries, and then mDNSCore sends an
+// mStatus_GrowCache message if it needs more.
+//
+// Most clients should use mDNS_Init_AdvertiseLocalAddresses. This causes mDNSCore to automatically
+// create the correct address records for all the hosts interfaces. If you plan to advertise
+// services being offered by the local machine, this is almost always what you want.
+// There are two cases where you might use mDNS_Init_DontAdvertiseLocalAddresses:
+// 1. A client-only device, that browses for services but doesn't advertise any of its own.
+// 2. A proxy-registration service, that advertises services being offered by other machines, and takes
+//    the appropriate steps to manually create the correct address records for those other machines.
+// In principle, a proxy-like registration service could manually create address records for its own machine too,
+// but this would be pointless extra effort when using mDNS_Init_AdvertiseLocalAddresses does that for you.
+//
+// Note that a client-only device that wishes to prohibit multicast advertisements (e.g. from
+// higher-layer API calls) must also set DivertMulticastAdvertisements in the mDNS structure and
+// advertise local address(es) on a loopback interface.
+//
+// When mDNS has finished setting up the client's callback is called
+// A client can also spin and poll the mDNSPlatformStatus field to see when it changes from mStatus_Waiting to mStatus_NoError
+//
+// Call mDNS_StartExit to tidy up before exiting
+// Because exiting may be an asynchronous process (e.g. if unicast records need to be deregistered)
+// client layer may choose to wait until mDNS_ExitNow() returns true before calling mDNS_FinalExit().
+//
+// Call mDNS_Register with a completed AuthRecord object to register a resource record
+// If the resource record type is kDNSRecordTypeUnique (or kDNSknownunique) then if a conflicting resource record is discovered,
+// the resource record's mDNSRecordCallback will be called with error code mStatus_NameConflict. The callback should deregister
+// the record, and may then try registering the record again after picking a new name (e.g. by automatically appending a number).
+// Following deregistration, the RecordCallback will be called with result mStatus_MemFree to signal that it is safe to deallocate
+// the record's storage (memory must be freed asynchronously to allow for goodbye packets and dynamic update deregistration).
+//
+// Call mDNS_StartQuery to initiate a query. mDNS will proceed to issue Multicast DNS query packets, and any time a response
+// is received containing a record which matches the question, the DNSQuestion's mDNSAnswerCallback function will be called
+// Call mDNS_StopQuery when no more answers are required
+//
+// Care should be taken on multi-threaded or interrupt-driven environments.
+// The main mDNS routines call mDNSPlatformLock() on entry and mDNSPlatformUnlock() on exit;
+// each platform layer needs to implement these appropriately for its respective platform.
+// For example, if the support code on a particular platform implements timer callbacks at interrupt time, then
+// mDNSPlatformLock/Unlock need to disable interrupts or do similar concurrency control to ensure that the mDNS
+// code is not entered by an interrupt-time timer callback while in the middle of processing a client call.
+
+extern mStatus mDNS_Init      (mDNS *const m, mDNS_PlatformSupport *const p,
+								CacheEntity *rrcachestorage, mDNSu32 rrcachesize,
+								mDNSBool AdvertiseLocalAddresses,
+								mDNSCallback *Callback, void *Context);
+// See notes above on use of NoCache/ZeroCacheSize
+#define mDNS_Init_NoCache                     mDNSNULL
+#define mDNS_Init_ZeroCacheSize               0
+// See notes above on use of Advertise/DontAdvertiseLocalAddresses
+#define mDNS_Init_AdvertiseLocalAddresses     mDNStrue
+#define mDNS_Init_DontAdvertiseLocalAddresses mDNSfalse
+#define mDNS_Init_NoInitCallback              mDNSNULL
+#define mDNS_Init_NoInitCallbackContext       mDNSNULL
+
+extern void    mDNS_ConfigChanged(mDNS *const m);
+extern void    mDNS_GrowCache (mDNS *const m, CacheEntity *storage, mDNSu32 numrecords);
+extern void    mDNS_GrowAuth (mDNS *const m, AuthEntity *storage, mDNSu32 numrecords);
+extern void    mDNS_StartExit (mDNS *const m);
+extern void    mDNS_FinalExit (mDNS *const m);
+#define mDNS_Close(m) do { mDNS_StartExit(m); mDNS_FinalExit(m); } while(0)
+#define mDNS_ExitNow(m, now) ((now) - (m)->ShutdownTime >= 0 || (!(m)->ResourceRecords))
+
+extern mDNSs32 mDNS_Execute   (mDNS *const m);
+
+extern mStatus mDNS_Register  (mDNS *const m, AuthRecord *const rr);
+extern mStatus mDNS_Update    (mDNS *const m, AuthRecord *const rr, mDNSu32 newttl,
+								const mDNSu16 newrdlength, RData *const newrdata, mDNSRecordUpdateCallback *Callback);
+extern mStatus mDNS_Deregister(mDNS *const m, AuthRecord *const rr);
+
+extern mStatus mDNS_StartQuery(mDNS *const m, DNSQuestion *const question);
+extern mStatus mDNS_StopQuery (mDNS *const m, DNSQuestion *const question);
+extern mStatus mDNS_StopQueryWithRemoves(mDNS *const m, DNSQuestion *const question);
+extern mStatus mDNS_Reconfirm (mDNS *const m, CacheRecord *const cacherr);
+extern mStatus mDNS_ReconfirmByValue(mDNS *const m, ResourceRecord *const rr);
+extern void    mDNS_PurgeCacheResourceRecord(mDNS *const m, CacheRecord *rr);
+extern mDNSs32 mDNS_TimeNow(const mDNS *const m);
+
+extern mStatus mDNS_StartNATOperation(mDNS *const m, NATTraversalInfo *traversal);
+extern mStatus mDNS_StopNATOperation(mDNS *const m, NATTraversalInfo *traversal);
+extern mStatus mDNS_StopNATOperation_internal(mDNS *m, NATTraversalInfo *traversal);
+
+extern DomainAuthInfo *GetAuthInfoForName(mDNS *m, const domainname *const name);
+
+extern void    mDNS_UpdateAllowSleep(mDNS *const m);
+
+// ***************************************************************************
+#if 0
+#pragma mark -
+#pragma mark - Platform support functions that are accessible to the client layer too
+#endif
+
+extern mDNSs32  mDNSPlatformOneSecond;
+
+// ***************************************************************************
+#if 0
+#pragma mark -
+#pragma mark - General utility and helper functions
+#endif
+
+// mDNS_Dereg_normal is used for most calls to mDNS_Deregister_internal
+// mDNS_Dereg_rapid is used to send one goodbye instead of three, when we want the memory available for reuse sooner
+// mDNS_Dereg_conflict is used to indicate that this record is being forcibly deregistered because of a conflict
+// mDNS_Dereg_repeat is used when cleaning up, for records that may have already been forcibly deregistered
+typedef enum { mDNS_Dereg_normal, mDNS_Dereg_rapid, mDNS_Dereg_conflict, mDNS_Dereg_repeat } mDNS_Dereg_type;
+
+// mDNS_RegisterService is a single call to register the set of resource records associated with a given named service.
+//
+// mDNS_StartResolveService is single call which is equivalent to multiple calls to mDNS_StartQuery,
+// to find the IP address, port number, and demultiplexing information for a given named service.
+// As with mDNS_StartQuery, it executes asynchronously, and calls the ServiceInfoQueryCallback when the answer is
+// found. After the service is resolved, the client should call mDNS_StopResolveService to complete the transaction.
+// The client can also call mDNS_StopResolveService at any time to abort the transaction.
+//
+// mDNS_AddRecordToService adds an additional record to a Service Record Set.  This record may be deregistered
+// via mDNS_RemoveRecordFromService, or by deregistering the service.  mDNS_RemoveRecordFromService is passed a
+// callback to free the memory associated with the extra RR when it is safe to do so.  The ExtraResourceRecord
+// object can be found in the record's context pointer.
+
+// mDNS_GetBrowseDomains is a special case of the mDNS_StartQuery call, where the resulting answers
+// are a list of PTR records indicating (in the rdata) domains that are recommended for browsing.
+// After getting the list of domains to browse, call mDNS_StopQuery to end the search.
+// mDNS_GetDefaultBrowseDomain returns the name of the domain that should be highlighted by default.
+//
+// mDNS_GetRegistrationDomains and mDNS_GetDefaultRegistrationDomain are the equivalent calls to get the list
+// of one or more domains that should be offered to the user as choices for where they may register their service,
+// and the default domain in which to register in the case where the user has made no selection.
+
+extern void    mDNS_SetupResourceRecord(AuthRecord *rr, RData *RDataStorage, mDNSInterfaceID InterfaceID,
+               mDNSu16 rrtype, mDNSu32 ttl, mDNSu8 RecordType, AuthRecType artype, mDNSRecordCallback Callback, void *Context);
+
+// mDNS_RegisterService() flags parameter bit definitions
+enum
+	{
+		regFlagIncludeP2P	= 0x1,	// include P2P interfaces when using mDNSInterface_Any
+		regFlagKnownUnique	= 0x2	// client guarantees that SRV and TXT record names are unique
+	};
+
+extern mStatus mDNS_RegisterService  (mDNS *const m, ServiceRecordSet *sr,
+               const domainlabel *const name, const domainname *const type, const domainname *const domain,
+               const domainname *const host, mDNSIPPort port, const mDNSu8 txtinfo[], mDNSu16 txtlen,
+               AuthRecord *SubTypes, mDNSu32 NumSubTypes,
+               mDNSInterfaceID InterfaceID, mDNSServiceCallback Callback, void *Context, mDNSu32 flags);
+extern mStatus mDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra, RData *rdata, mDNSu32 ttl,  mDNSu32 includeP2P);
+extern mStatus mDNS_RemoveRecordFromService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra, mDNSRecordCallback MemFreeCallback, void *Context);
+extern mStatus mDNS_RenameAndReregisterService(mDNS *const m, ServiceRecordSet *const sr, const domainlabel *newname);
+extern mStatus mDNS_DeregisterService_drt(mDNS *const m, ServiceRecordSet *sr, mDNS_Dereg_type drt);
+#define mDNS_DeregisterService(M,S) mDNS_DeregisterService_drt((M), (S), mDNS_Dereg_normal)
+
+extern mStatus mDNS_RegisterNoSuchService(mDNS *const m, AuthRecord *const rr,
+               const domainlabel *const name, const domainname *const type, const domainname *const domain,
+               const domainname *const host,
+               const mDNSInterfaceID InterfaceID, mDNSRecordCallback Callback, void *Context, mDNSBool includeP2P);
+#define        mDNS_DeregisterNoSuchService mDNS_Deregister
+
+extern void mDNS_SetupQuestion(DNSQuestion *const q, const mDNSInterfaceID InterfaceID, const domainname *const name,
+               const mDNSu16 qtype, mDNSQuestionCallback *const callback, void *const context);
+
+extern mStatus mDNS_StartBrowse(mDNS *const m, DNSQuestion *const question,
+               const domainname *const srv, const domainname *const domain,
+               const mDNSInterfaceID InterfaceID, mDNSBool ForceMCast, mDNSQuestionCallback *Callback, void *Context);
+#define        mDNS_StopBrowse mDNS_StopQuery
+
+extern mStatus mDNS_StartResolveService(mDNS *const m, ServiceInfoQuery *query, ServiceInfo *info, mDNSServiceInfoQueryCallback *Callback, void *Context);
+extern void    mDNS_StopResolveService (mDNS *const m, ServiceInfoQuery *query);
+
+typedef enum
+	{
+	mDNS_DomainTypeBrowse              = 0,
+	mDNS_DomainTypeBrowseDefault       = 1,
+	mDNS_DomainTypeBrowseAutomatic     = 2,
+	mDNS_DomainTypeRegistration        = 3,
+	mDNS_DomainTypeRegistrationDefault = 4,
+
+	mDNS_DomainTypeMax = 4
+	} mDNS_DomainType;
+
+extern const char *const mDNS_DomainTypeNames[];
+
+extern mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, mDNS_DomainType DomainType, const domainname *dom,
+								const mDNSInterfaceID InterfaceID, mDNSQuestionCallback *Callback, void *Context);
+#define        mDNS_StopGetDomains mDNS_StopQuery
+extern mStatus mDNS_AdvertiseDomains(mDNS *const m, AuthRecord *rr, mDNS_DomainType DomainType, const mDNSInterfaceID InterfaceID, char *domname);
+#define        mDNS_StopAdvertiseDomains mDNS_Deregister
+
+extern mDNSOpaque16 mDNS_NewMessageID(mDNS *const m);
+extern mDNSBool mDNS_AddressIsLocalSubnet(mDNS *const m, const mDNSInterfaceID InterfaceID, const mDNSAddr *addr);
+
+extern DNSServer *GetServerForName(mDNS *m, const domainname *name, mDNSInterfaceID InterfaceID);
+extern DNSServer *GetServerForQuestion(mDNS *m, DNSQuestion *question);
+extern mDNSu32 SetValidDNSServers(mDNS *m, DNSQuestion *question);
+
+// ***************************************************************************
+#if 0
+#pragma mark -
+#pragma mark - DNS name utility functions
+#endif
+
+// In order to expose the full capabilities of the DNS protocol (which allows any arbitrary eight-bit values
+// in domain name labels, including unlikely characters like ascii nulls and even dots) all the mDNS APIs
+// work with DNS's native length-prefixed strings. For convenience in C, the following utility functions
+// are provided for converting between C's null-terminated strings and DNS's length-prefixed strings.
+
+// Assignment
+// A simple C structure assignment of a domainname can cause a protection fault by accessing unmapped memory,
+// because that object is defined to be 256 bytes long, but not all domainname objects are truly the full size.
+// This macro uses mDNSPlatformMemCopy() to make sure it only touches the actual bytes that are valid.
+#define AssignDomainName(DST, SRC) do { mDNSu16 len__ = DomainNameLength((SRC)); \
+	if (len__ <= MAX_DOMAIN_NAME) mDNSPlatformMemCopy((DST)->c, (SRC)->c, len__); else (DST)->c[0] = 0; } while(0)
+
+// Comparison functions
+#define SameDomainLabelCS(A,B) ((A)[0] == (B)[0] && mDNSPlatformMemSame((A)+1, (B)+1, (A)[0]))
+extern mDNSBool SameDomainLabel(const mDNSu8 *a, const mDNSu8 *b);
+extern mDNSBool SameDomainName(const domainname *const d1, const domainname *const d2);
+extern mDNSBool SameDomainNameCS(const domainname *const d1, const domainname *const d2);
+typedef mDNSBool DomainNameComparisonFn(const domainname *const d1, const domainname *const d2);
+extern mDNSBool IsLocalDomain(const domainname *d);     // returns true for domains that by default should be looked up using link-local multicast
+
+#define StripFirstLabel(X) ((const domainname *)&(X)->c[(X)->c[0] ? 1 + (X)->c[0] : 0])
+
+#define FirstLabel(X)  ((const domainlabel *)(X))
+#define SecondLabel(X) ((const domainlabel *)StripFirstLabel(X))
+#define ThirdLabel(X)  ((const domainlabel *)StripFirstLabel(StripFirstLabel(X)))
+
+extern const mDNSu8 *LastLabel(const domainname *d);
+
+// Get total length of domain name, in native DNS format, including terminal root label
+//   (e.g. length of "com." is 5 (length byte, three data bytes, final zero)
+extern mDNSu16  DomainNameLengthLimit(const domainname *const name, const mDNSu8 *limit);
+#define DomainNameLength(name) DomainNameLengthLimit((name), (name)->c + MAX_DOMAIN_NAME)
+
+// Append functions to append one or more labels to an existing native format domain name:
+//   AppendLiteralLabelString adds a single label from a literal C string, with no escape character interpretation.
+//   AppendDNSNameString      adds zero or more labels from a C string using conventional DNS dots-and-escaping interpretation
+//   AppendDomainLabel        adds a single label from a native format domainlabel
+//   AppendDomainName         adds zero or more labels from a native format domainname
+extern mDNSu8  *AppendLiteralLabelString(domainname *const name, const char *cstr);
+extern mDNSu8  *AppendDNSNameString     (domainname *const name, const char *cstr);
+extern mDNSu8  *AppendDomainLabel       (domainname *const name, const domainlabel *const label);
+extern mDNSu8  *AppendDomainName        (domainname *const name, const domainname *const append);
+
+// Convert from null-terminated string to native DNS format:
+//   The DomainLabel form makes a single label from a literal C string, with no escape character interpretation.
+//   The DomainName form makes native format domain name from a C string using conventional DNS interpretation:
+//     dots separate labels, and within each label, '\.' represents a literal dot, '\\' represents a literal
+//     backslash and backslash with three decimal digits (e.g. \000) represents an arbitrary byte value.
+extern mDNSBool MakeDomainLabelFromLiteralString(domainlabel *const label, const char *cstr);
+extern mDNSu8  *MakeDomainNameFromDNSNameString (domainname  *const name,  const char *cstr);
+
+// Convert native format domainlabel or domainname back to C string format
+// IMPORTANT:
+// When using ConvertDomainLabelToCString, the target buffer must be MAX_ESCAPED_DOMAIN_LABEL (254) bytes long
+// to guarantee there will be no buffer overrun. It is only safe to use a buffer shorter than this in rare cases
+// where the label is known to be constrained somehow (for example, if the label is known to be either "_tcp" or "_udp").
+// Similarly, when using ConvertDomainNameToCString, the target buffer must be MAX_ESCAPED_DOMAIN_NAME (1009) bytes long.
+// See definitions of MAX_ESCAPED_DOMAIN_LABEL and MAX_ESCAPED_DOMAIN_NAME for more detailed explanation.
+extern char    *ConvertDomainLabelToCString_withescape(const domainlabel *const name, char *cstr, char esc);
+#define         ConvertDomainLabelToCString_unescaped(D,C) ConvertDomainLabelToCString_withescape((D), (C), 0)
+#define         ConvertDomainLabelToCString(D,C)           ConvertDomainLabelToCString_withescape((D), (C), '\\')
+extern char    *ConvertDomainNameToCString_withescape(const domainname *const name, char *cstr, char esc);
+#define         ConvertDomainNameToCString_unescaped(D,C) ConvertDomainNameToCString_withescape((D), (C), 0)
+#define         ConvertDomainNameToCString(D,C)           ConvertDomainNameToCString_withescape((D), (C), '\\')
+
+extern void     ConvertUTF8PstringToRFC1034HostLabel(const mDNSu8 UTF8Name[], domainlabel *const hostlabel);
+
+extern mDNSu8  *ConstructServiceName(domainname *const fqdn, const domainlabel *name, const domainname *type, const domainname *const domain);
+extern mDNSBool DeconstructServiceName(const domainname *const fqdn, domainlabel *const name, domainname *const type, domainname *const domain);
+
+// Note: Some old functions have been replaced by more sensibly-named versions.
+// You can uncomment the hash-defines below if you don't want to have to change your source code right away.
+// When updating your code, note that (unlike the old versions) *all* the new routines take the target object
+// as their first parameter.
+//#define ConvertCStringToDomainName(SRC,DST)  MakeDomainNameFromDNSNameString((DST),(SRC))
+//#define ConvertCStringToDomainLabel(SRC,DST) MakeDomainLabelFromLiteralString((DST),(SRC))
+//#define AppendStringLabelToName(DST,SRC)     AppendLiteralLabelString((DST),(SRC))
+//#define AppendStringNameToName(DST,SRC)      AppendDNSNameString((DST),(SRC))
+//#define AppendDomainLabelToName(DST,SRC)     AppendDomainLabel((DST),(SRC))
+//#define AppendDomainNameToName(DST,SRC)      AppendDomainName((DST),(SRC))
+
+// ***************************************************************************
+#if 0
+#pragma mark -
+#pragma mark - Other utility functions and macros
+#endif
+
+// mDNS_vsnprintf/snprintf return the number of characters written, excluding the final terminating null.
+// The output is always null-terminated: for example, if the output turns out to be exactly buflen long,
+// then the output will be truncated by one character to allow space for the terminating null.
+// Unlike standard C vsnprintf/snprintf, they return the number of characters *actually* written,
+// not the number of characters that *would* have been printed were buflen unlimited.
+extern mDNSu32 mDNS_vsnprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, va_list arg);
+extern mDNSu32 mDNS_snprintf(char *sbuffer, mDNSu32 buflen, const char *fmt, ...) IS_A_PRINTF_STYLE_FUNCTION(3,4);
+extern mDNSu32 NumCacheRecordsForInterfaceID(const mDNS *const m, mDNSInterfaceID id);
+extern char *DNSTypeName(mDNSu16 rrtype);
+extern char *GetRRDisplayString_rdb(const ResourceRecord *const rr, const RDataBody *const rd1, char *const buffer);
+#define RRDisplayString(m, rr) GetRRDisplayString_rdb(rr, &(rr)->rdata->u, (m)->MsgBuffer)
+#define ARDisplayString(m, rr) GetRRDisplayString_rdb(&(rr)->resrec, &(rr)->resrec.rdata->u, (m)->MsgBuffer)
+#define CRDisplayString(m, rr) GetRRDisplayString_rdb(&(rr)->resrec, &(rr)->resrec.rdata->u, (m)->MsgBuffer)
+extern mDNSBool mDNSSameAddress(const mDNSAddr *ip1, const mDNSAddr *ip2);
+extern void IncrementLabelSuffix(domainlabel *name, mDNSBool RichText);
+extern mDNSBool mDNSv4AddrIsRFC1918(mDNSv4Addr *addr);  // returns true for RFC1918 private addresses
+#define mDNSAddrIsRFC1918(X) ((X)->type == mDNSAddrType_IPv4 && mDNSv4AddrIsRFC1918(&(X)->ip.v4))
+
+#define mDNSSameIPPort(A,B)      ((A).NotAnInteger == (B).NotAnInteger)
+#define mDNSSameOpaque16(A,B)    ((A).NotAnInteger == (B).NotAnInteger)
+#define mDNSSameOpaque32(A,B)    ((A).NotAnInteger == (B).NotAnInteger)
+#define mDNSSameOpaque64(A,B)    ((A)->l[0] == (B)->l[0] && (A)->l[1] == (B)->l[1])
+
+#define mDNSSameIPv4Address(A,B) ((A).NotAnInteger == (B).NotAnInteger)
+#define mDNSSameIPv6Address(A,B) ((A).l[0] == (B).l[0] && (A).l[1] == (B).l[1] && (A).l[2] == (B).l[2] && (A).l[3] == (B).l[3])
+#define mDNSSameEthAddress(A,B)  ((A)->w[0] == (B)->w[0] && (A)->w[1] == (B)->w[1] && (A)->w[2] == (B)->w[2])
+
+#define mDNSIPPortIsZero(A)      ((A).NotAnInteger                            == 0)
+#define mDNSOpaque16IsZero(A)    ((A).NotAnInteger                            == 0)
+#define mDNSOpaque64IsZero(A)    (((A)->l[0] | (A)->l[1]                    ) == 0)
+#define mDNSIPv4AddressIsZero(A) ((A).NotAnInteger                            == 0)
+#define mDNSIPv6AddressIsZero(A) (((A).l[0] | (A).l[1] | (A).l[2] | (A).l[3]) == 0)
+#define mDNSEthAddressIsZero(A)  (((A).w[0] | (A).w[1] | (A).w[2]           ) == 0)
+
+#define mDNSIPv4AddressIsOnes(A) ((A).NotAnInteger == 0xFFFFFFFF)
+#define mDNSIPv6AddressIsOnes(A) (((A).l[0] & (A).l[1] & (A).l[2] & (A).l[3]) == 0xFFFFFFFF)
+
+#define mDNSAddressIsAllDNSLinkGroup(X) (                                                            \
+	((X)->type == mDNSAddrType_IPv4 && mDNSSameIPv4Address((X)->ip.v4, AllDNSLinkGroup_v4.ip.v4)) || \
+	((X)->type == mDNSAddrType_IPv6 && mDNSSameIPv6Address((X)->ip.v6, AllDNSLinkGroup_v6.ip.v6))    )
+
+#define mDNSAddressIsZero(X) (                                                \
+	((X)->type == mDNSAddrType_IPv4 && mDNSIPv4AddressIsZero((X)->ip.v4))  || \
+	((X)->type == mDNSAddrType_IPv6 && mDNSIPv6AddressIsZero((X)->ip.v6))     )
+
+#define mDNSAddressIsValidNonZero(X) (                                        \
+	((X)->type == mDNSAddrType_IPv4 && !mDNSIPv4AddressIsZero((X)->ip.v4)) || \
+	((X)->type == mDNSAddrType_IPv6 && !mDNSIPv6AddressIsZero((X)->ip.v6))    )
+
+#define mDNSAddressIsOnes(X) (                                                \
+	((X)->type == mDNSAddrType_IPv4 && mDNSIPv4AddressIsOnes((X)->ip.v4))  || \
+	((X)->type == mDNSAddrType_IPv6 && mDNSIPv6AddressIsOnes((X)->ip.v6))     )
+
+#define mDNSAddressIsValid(X) (                                                                                             \
+	((X)->type == mDNSAddrType_IPv4) ? !(mDNSIPv4AddressIsZero((X)->ip.v4) || mDNSIPv4AddressIsOnes((X)->ip.v4)) :          \
+	((X)->type == mDNSAddrType_IPv6) ? !(mDNSIPv6AddressIsZero((X)->ip.v6) || mDNSIPv6AddressIsOnes((X)->ip.v6)) : mDNSfalse)
+
+#define mDNSv4AddressIsLinkLocal(X) ((X)->b[0] ==  169 &&  (X)->b[1]         ==  254)
+#define mDNSv6AddressIsLinkLocal(X) ((X)->b[0] == 0xFE && ((X)->b[1] & 0xC0) == 0x80)
+
+#define mDNSAddressIsLinkLocal(X)  (                                                    \
+	((X)->type == mDNSAddrType_IPv4) ? mDNSv4AddressIsLinkLocal(&(X)->ip.v4) :          \
+	((X)->type == mDNSAddrType_IPv6) ? mDNSv6AddressIsLinkLocal(&(X)->ip.v6) : mDNSfalse)
+
+#define mDNSv4AddressIsLoopback(X) ((X)->b[0] == 127 && (X)->b[1] == 0 && (X)->b[2] == 0 && (X)->b[3] == 1)
+#define mDNSv6AddressIsLoopback(X) ((((X)->l[0] | (X)->l[1] | (X)->l[2]) == 0) && ((X)->b[12] == 0 && (X)->b[13] == 0 && (X)->b[14] == 0 && (X)->b[15] == 1))
+
+// ***************************************************************************
+#if 0
+#pragma mark -
+#pragma mark - Authentication Support
+#endif
+
+// Unicast DNS and Dynamic Update specific Client Calls
+//
+// mDNS_SetSecretForDomain tells the core to authenticate (via TSIG with an HMAC_MD5 hash of the shared secret)
+// when dynamically updating a given zone (and its subdomains).  The key used in authentication must be in
+// domain name format.  The shared secret must be a null-terminated base64 encoded string.  A minimum size of
+// 16 bytes (128 bits) is recommended for an MD5 hash as per RFC 2485.
+// Calling this routine multiple times for a zone replaces previously entered values.  Call with a NULL key
+// to disable authentication for the zone.  A non-NULL autoTunnelPrefix means this is an AutoTunnel domain,
+// and the value is prepended to the IPSec identifier (used for key lookup)
+
+extern mStatus mDNS_SetSecretForDomain(mDNS *m, DomainAuthInfo *info,
+	const domainname *domain, const domainname *keyname, const char *b64keydata, const domainname *hostname, mDNSIPPort *port, const char *autoTunnelPrefix);
+
+extern void RecreateNATMappings(mDNS *const m);
+
+// Hostname/Unicast Interface Configuration
+
+// All hostnames advertised point to one IPv4 address and/or one IPv6 address, set via SetPrimaryInterfaceInfo.  Invoking this routine
+// updates all existing hostnames to point to the new address.
+
+// A hostname is added via AddDynDNSHostName, which points to the primary interface's v4 and/or v6 addresss
+
+// The status callback is invoked to convey success or failure codes - the callback should not modify the AuthRecord or free memory.
+// Added hostnames may be removed (deregistered) via mDNS_RemoveDynDNSHostName.
+
+// Host domains added prior to specification of the primary interface address and computer name will be deferred until
+// these values are initialized.
+
+// DNS servers used to resolve unicast queries are specified by mDNS_AddDNSServer.
+// For "split" DNS configurations, in which queries for different domains are sent to different servers (e.g. VPN and external),
+// a domain may be associated with a DNS server.  For standard configurations, specify the root label (".") or NULL.
+
+extern void mDNS_AddDynDNSHostName(mDNS *m, const domainname *fqdn, mDNSRecordCallback *StatusCallback, const void *StatusContext);
+extern void mDNS_RemoveDynDNSHostName(mDNS *m, const domainname *fqdn);
+extern void mDNS_SetPrimaryInterfaceInfo(mDNS *m, const mDNSAddr *v4addr,  const mDNSAddr *v6addr, const mDNSAddr *router);
+extern DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, const mDNSAddr *addr, const mDNSIPPort port, mDNSBool scoped, mDNSu32 timeout);
+extern void PenalizeDNSServer(mDNS *const m, DNSQuestion *q);
+extern void mDNS_AddSearchDomain(const domainname *const domain, mDNSInterfaceID InterfaceID);
+
+extern McastResolver *mDNS_AddMcastResolver(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, mDNSu32 timeout);
+
+// We use ((void *)0) here instead of mDNSNULL to avoid compile warnings on gcc 4.2
+#define mDNS_AddSearchDomain_CString(X, I) \
+	do { domainname d__; if (((X) != (void*)0) && MakeDomainNameFromDNSNameString(&d__, (X)) && d__.c[0]) mDNS_AddSearchDomain(&d__, I); } while(0)
+
+// Routines called by the core, exported by DNSDigest.c
+
+// Convert an arbitrary base64 encoded key key into an HMAC key (stored in AuthInfo struct)
+extern mDNSs32 DNSDigest_ConstructHMACKeyfromBase64(DomainAuthInfo *info, const char *b64key);
+
+// sign a DNS message.  The message must be complete, with all values in network byte order.  end points to the end
+// of the message, and is modified by this routine.  numAdditionals is a pointer to the number of additional
+// records in HOST byte order, which is incremented upon successful completion of this routine.  The function returns
+// the new end pointer on success, and NULL on failure.
+extern void DNSDigest_SignMessage(DNSMessage *msg, mDNSu8 **end, DomainAuthInfo *info, mDNSu16 tcode);
+
+#define SwapDNSHeaderBytes(M) do { \
+    (M)->h.numQuestions   = (mDNSu16)((mDNSu8 *)&(M)->h.numQuestions  )[0] << 8 | ((mDNSu8 *)&(M)->h.numQuestions  )[1]; \
+    (M)->h.numAnswers     = (mDNSu16)((mDNSu8 *)&(M)->h.numAnswers    )[0] << 8 | ((mDNSu8 *)&(M)->h.numAnswers    )[1]; \
+    (M)->h.numAuthorities = (mDNSu16)((mDNSu8 *)&(M)->h.numAuthorities)[0] << 8 | ((mDNSu8 *)&(M)->h.numAuthorities)[1]; \
+    (M)->h.numAdditionals = (mDNSu16)((mDNSu8 *)&(M)->h.numAdditionals)[0] << 8 | ((mDNSu8 *)&(M)->h.numAdditionals)[1]; \
+    } while (0)
+
+#define DNSDigest_SignMessageHostByteOrder(M,E,INFO) \
+	do { SwapDNSHeaderBytes(M); DNSDigest_SignMessage((M), (E), (INFO), 0); SwapDNSHeaderBytes(M); } while (0)
+
+// verify a DNS message.  The message must be complete, with all values in network byte order.  end points to the
+// end of the record.  tsig is a pointer to the resource record that contains the TSIG OPT record.  info is
+// the matching key to use for verifying the message.  This function expects that the additionals member
+// of the DNS message header has already had one subtracted from it.
+extern mDNSBool DNSDigest_VerifyMessage(DNSMessage *msg, mDNSu8 *end, LargeCacheRecord *tsig, DomainAuthInfo *info, mDNSu16 *rcode, mDNSu16 *tcode);
+
+// ***************************************************************************
+#if 0
+#pragma mark -
+#pragma mark - PlatformSupport interface
+#endif
+
+// This section defines the interface to the Platform Support layer.
+// Normal client code should not use any of types defined here, or directly call any of the functions defined here.
+// The definitions are placed here because sometimes clients do use these calls indirectly, via other supported client operations.
+// For example, AssignDomainName is a macro defined using mDNSPlatformMemCopy()
+
+// Every platform support module must provide the following functions.
+// mDNSPlatformInit() typically opens a communication endpoint, and starts listening for mDNS packets.
+// When Setup is complete, the platform support layer calls mDNSCoreInitComplete().
+// mDNSPlatformSendUDP() sends one UDP packet
+// When a packet is received, the PlatformSupport code calls mDNSCoreReceive()
+// mDNSPlatformClose() tidies up on exit
+//
+// Note: mDNSPlatformMemAllocate/mDNSPlatformMemFree are only required for handling oversized resource records and unicast DNS.
+// If your target platform has a well-defined specialized application, and you know that all the records it uses
+// are InlineCacheRDSize or less, then you can just make a simple mDNSPlatformMemAllocate() stub that always returns
+// NULL. InlineCacheRDSize is a compile-time constant, which is set by default to 68. If you need to handle records
+// a little larger than this and you don't want to have to implement run-time allocation and freeing, then you
+// can raise the value of this constant to a suitable value (at the expense of increased memory usage).
+//
+// USE CAUTION WHEN CALLING mDNSPlatformRawTime: The m->timenow_adjust correction factor needs to be added
+// Generally speaking:
+// Code that's protected by the main mDNS lock should just use the m->timenow value
+// Code outside the main mDNS lock should use mDNS_TimeNow(m) to get properly adjusted time
+// In certain cases there may be reasons why it's necessary to get the time without taking the lock first
+// (e.g. inside the routines that are doing the locking and unlocking, where a call to get the lock would result in a
+// recursive loop); in these cases use mDNS_TimeNow_NoLock(m) to get mDNSPlatformRawTime with the proper correction factor added.
+//
+// mDNSPlatformUTC returns the time, in seconds, since Jan 1st 1970 UTC and is required for generating TSIG records
+
+extern mStatus  mDNSPlatformInit        (mDNS *const m);
+extern void     mDNSPlatformClose       (mDNS *const m);
+extern mStatus  mDNSPlatformSendUDP(const mDNS *const m, const void *const msg, const mDNSu8 *const end,
+mDNSInterfaceID InterfaceID, UDPSocket *src, const mDNSAddr *dst, mDNSIPPort dstport);
+
+extern void     mDNSPlatformLock        (const mDNS *const m);
+extern void     mDNSPlatformUnlock      (const mDNS *const m);
+
+mDNSexport void     mDNSPlatformStrCopy(      void *dst, const void *src);
+mDNSexport mDNSu32  mDNSPlatformStrLCopy(     void *dst, const void *src, mDNSu32 dstlen);
+mDNSexport mDNSu32  mDNSPlatformStrLen (                 const void *src);
+extern void     mDNSPlatformMemCopy     (      void *dst, const void *src, mDNSu32 len);
+extern mDNSBool mDNSPlatformMemSame     (const void *dst, const void *src, mDNSu32 len);
+extern void     mDNSPlatformMemZero     (      void *dst,                  mDNSu32 len);
+#if APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING
+#define         mDNSPlatformMemAllocate(X) mallocL(#X, X)
+#else
+extern void *   mDNSPlatformMemAllocate (mDNSu32 len);
+#endif
+extern void     mDNSPlatformMemFree     (void *mem);
+
+// If the platform doesn't have a strong PRNG, we define a naive multiply-and-add based on a seed
+// from the platform layer.  Long-term, we should embed an arc4 implementation, but the strength
+// will still depend on the randomness of the seed.
+#if !defined(_PLATFORM_HAS_STRONG_PRNG_) && (_BUILDING_XCODE_PROJECT_ || defined(_WIN32))
+#define _PLATFORM_HAS_STRONG_PRNG_ 1
+#endif
+#if _PLATFORM_HAS_STRONG_PRNG_
+extern mDNSu32  mDNSPlatformRandomNumber(void);
+#else
+extern mDNSu32  mDNSPlatformRandomSeed  (void);
+#endif // _PLATFORM_HAS_STRONG_PRNG_
+
+extern mStatus  mDNSPlatformTimeInit    (void);
+extern mDNSs32  mDNSPlatformRawTime     (void);
+extern mDNSs32  mDNSPlatformUTC         (void);
+#define mDNS_TimeNow_NoLock(m) (mDNSPlatformRawTime() + (m)->timenow_adjust)
+
+#if MDNS_DEBUGMSGS
+extern void	mDNSPlatformWriteDebugMsg(const char *msg);
+#endif
+extern void	mDNSPlatformWriteLogMsg(const char *ident, const char *msg, mDNSLogLevel_t loglevel);
+
+#if APPLE_OSX_mDNSResponder
+// Utility function for ASL logging
+mDNSexport void mDNSASLLog(uuid_t *uuid, const char *subdomain, const char *result, const char *signature, const char *fmt, ...);
+#endif
+
+// Platform support modules should provide the following functions to map between opaque interface IDs
+// and interface indexes in order to support the DNS-SD API. If your target platform does not support
+// multiple interfaces and/or does not support the DNS-SD API, these functions can be empty.
+extern mDNSInterfaceID mDNSPlatformInterfaceIDfromInterfaceIndex(mDNS *const m, mDNSu32 ifindex);
+extern mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID(mDNS *const m, mDNSInterfaceID id, mDNSBool suppressNetworkChange);
+
+// Every platform support module must provide the following functions if it is to support unicast DNS
+// and Dynamic Update.
+// All TCP socket operations implemented by the platform layer MUST NOT BLOCK.
+// mDNSPlatformTCPConnect initiates a TCP connection with a peer, adding the socket descriptor to the
+// main event loop.  The return value indicates whether the connection succeeded, failed, or is pending
+// (i.e. the call would block.)  On return, the descriptor parameter is set to point to the connected socket.
+// The TCPConnectionCallback is subsequently invoked when the connection
+// completes (in which case the ConnectionEstablished parameter is true), or data is available for
+// reading on the socket (indicated by the ConnectionEstablished parameter being false.)  If the connection
+// asynchronously fails, the TCPConnectionCallback should be invoked as usual, with the error being
+// returned in subsequent calls to PlatformReadTCP or PlatformWriteTCP.  (This allows for platforms
+// with limited asynchronous error detection capabilities.)  PlatformReadTCP and PlatformWriteTCP must
+// return the number of bytes read/written, 0 if the call would block, and -1 if an error.  PlatformReadTCP
+// should set the closed argument if the socket has been closed.
+// PlatformTCPCloseConnection must close the connection to the peer and remove the descriptor from the
+// event loop.  CloseConnectin may be called at any time, including in a ConnectionCallback.
+
+typedef enum
+	{
+	kTCPSocketFlags_Zero   = 0,
+	kTCPSocketFlags_UseTLS = (1 << 0)
+	} TCPSocketFlags;
+
+typedef void (*TCPConnectionCallback)(TCPSocket *sock, void *context, mDNSBool ConnectionEstablished, mStatus err);
+extern TCPSocket *mDNSPlatformTCPSocket(mDNS *const m, TCPSocketFlags flags, mDNSIPPort *port);	// creates a TCP socket
+extern TCPSocket *mDNSPlatformTCPAccept(TCPSocketFlags flags, int sd);
+extern int        mDNSPlatformTCPGetFD(TCPSocket *sock);
+extern mStatus    mDNSPlatformTCPConnect(TCPSocket *sock, const mDNSAddr *dst, mDNSOpaque16 dstport, domainname *hostname,
+										mDNSInterfaceID InterfaceID, TCPConnectionCallback callback, void *context);
+extern void       mDNSPlatformTCPCloseConnection(TCPSocket *sock);
+extern long       mDNSPlatformReadTCP(TCPSocket *sock, void *buf, unsigned long buflen, mDNSBool *closed);
+extern long       mDNSPlatformWriteTCP(TCPSocket *sock, const char *msg, unsigned long len);
+extern UDPSocket *mDNSPlatformUDPSocket(mDNS *const m, const mDNSIPPort requestedport);
+extern void       mDNSPlatformUDPClose(UDPSocket *sock);
+extern void       mDNSPlatformReceiveBPF_fd(mDNS *const m, int fd);
+extern void       mDNSPlatformUpdateProxyList(mDNS *const m, const mDNSInterfaceID InterfaceID);
+extern void       mDNSPlatformSendRawPacket(const void *const msg, const mDNSu8 *const end, mDNSInterfaceID InterfaceID);
+extern void       mDNSPlatformSetLocalAddressCacheEntry(mDNS *const m, const mDNSAddr *const tpa, const mDNSEthAddr *const tha, mDNSInterfaceID InterfaceID);
+extern void       mDNSPlatformSourceAddrForDest(mDNSAddr *const src, const mDNSAddr *const dst);
+
+// mDNSPlatformTLSSetupCerts/mDNSPlatformTLSTearDownCerts used by dnsextd
+extern mStatus    mDNSPlatformTLSSetupCerts(void);
+extern void       mDNSPlatformTLSTearDownCerts(void);
+
+// Platforms that support unicast browsing and dynamic update registration for clients who do not specify a domain
+// in browse/registration calls must implement these routines to get the "default" browse/registration list.
+
+extern void       mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, mDNSBool setsearch, domainname *const fqdn, DNameListElem **RegDomains, DNameListElem **BrowseDomains);
+extern mStatus    mDNSPlatformGetPrimaryInterface(mDNS *const m, mDNSAddr *v4, mDNSAddr *v6, mDNSAddr *router);
+extern void       mDNSPlatformDynDNSHostNameStatusChanged(const domainname *const dname, const mStatus status);
+
+extern void       mDNSPlatformSetAllowSleep(mDNS *const m, mDNSBool allowSleep, const char *reason);
+extern void       mDNSPlatformSendWakeupPacket(mDNS *const m, mDNSInterfaceID InterfaceID, char *EthAddr, char *IPAddr, int iteration);
+extern mDNSBool   mDNSPlatformValidRecordForInterface(AuthRecord *rr, const NetworkInterfaceInfo *intf);
+
+#ifdef _LEGACY_NAT_TRAVERSAL_
+// Support for legacy NAT traversal protocols, implemented by the platform layer and callable by the core.
+extern void     LNT_SendDiscoveryMsg(mDNS *m);
+extern void     LNT_ConfigureRouterInfo(mDNS *m, const mDNSInterfaceID InterfaceID, const mDNSu8 *const data, const mDNSu16 len);
+extern mStatus  LNT_GetExternalAddress(mDNS *m);
+extern mStatus  LNT_MapPort(mDNS *m, NATTraversalInfo *n);
+extern mStatus  LNT_UnmapPort(mDNS *m, NATTraversalInfo *n);
+extern void     LNT_ClearState(mDNS *const m);
+#endif // _LEGACY_NAT_TRAVERSAL_
+
+// The core mDNS code provides these functions, for the platform support code to call at appropriate times
+//
+// mDNS_SetFQDN() is called once on startup (typically from mDNSPlatformInit())
+// and then again on each subsequent change of the host name.
+//
+// mDNS_RegisterInterface() is used by the platform support layer to inform mDNSCore of what
+// physical and/or logical interfaces are available for sending and receiving packets.
+// Typically it is called on startup for each available interface, but register/deregister may be
+// called again later, on multiple occasions, to inform the core of interface configuration changes.
+// If set->Advertise is set non-zero, then mDNS_RegisterInterface() also registers the standard
+// resource records that should be associated with every publicised IP address/interface:
+// -- Name-to-address records (A/AAAA)
+// -- Address-to-name records (PTR)
+// -- Host information (HINFO)
+// IMPORTANT: The specified mDNSInterfaceID MUST NOT be 0, -1, or -2; these values have special meaning
+// mDNS_RegisterInterface does not result in the registration of global hostnames via dynamic update -
+// see mDNS_SetPrimaryInterfaceInfo, mDNS_AddDynDNSHostName, etc. for this purpose.
+// Note that the set may be deallocated immediately after it is deregistered via mDNS_DeegisterInterface.
+//
+// mDNS_RegisterDNS() is used by the platform support layer to provide the core with the addresses of
+// available domain name servers for unicast queries/updates.  RegisterDNS() should be called once for
+// each name server, typically at startup, or when a new name server becomes available.  DeregiterDNS()
+// must be called whenever a registered name server becomes unavailable.  DeregisterDNSList deregisters
+// all registered servers.  mDNS_DNSRegistered() returns true if one or more servers are registered in the core.
+//
+// mDNSCoreInitComplete() is called when the platform support layer is finished.
+// Typically this is at the end of mDNSPlatformInit(), but may be later
+// (on platforms like OT that allow asynchronous initialization of the networking stack).
+//
+// mDNSCoreReceive() is called when a UDP packet is received
+//
+// mDNSCoreMachineSleep() is called when the machine sleeps or wakes
+// (This refers to heavyweight laptop-style sleep/wake that disables network access,
+// not lightweight second-by-second CPU power management modes.)
+
+extern void     mDNS_SetFQDN(mDNS *const m);
+extern void     mDNS_ActivateNetWake_internal  (mDNS *const m, NetworkInterfaceInfo *set);
+extern void     mDNS_DeactivateNetWake_internal(mDNS *const m, NetworkInterfaceInfo *set);
+extern mStatus  mDNS_RegisterInterface  (mDNS *const m, NetworkInterfaceInfo *set, mDNSBool flapping);
+extern void     mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *set, mDNSBool flapping);
+extern void     mDNSCoreInitComplete(mDNS *const m, mStatus result);
+extern void     mDNSCoreReceive(mDNS *const m, void *const msg, const mDNSu8 *const end,
+								const mDNSAddr *const srcaddr, const mDNSIPPort srcport,
+								const mDNSAddr *dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID);
+extern void		mDNSCoreRestartQueries(mDNS *const m);
+typedef void    (*FlushCache)(mDNS *const m);
+typedef void    (*CallbackBeforeStartQuery)(mDNS *const m, void *context);
+extern void		mDNSCoreRestartAddressQueries(mDNS *const m, mDNSBool SearchDomainsChanged, FlushCache flushCacheRecords,
+											  CallbackBeforeStartQuery beforeQueryStart, void *context);
+extern mDNSBool mDNSCoreHaveAdvertisedMulticastServices(mDNS *const m);
+extern void     mDNSCoreMachineSleep(mDNS *const m, mDNSBool wake);
+extern mDNSBool mDNSCoreReadyForSleep(mDNS *m, mDNSs32 now);
+extern mDNSs32  mDNSCoreIntervalToNextWake(mDNS *const m, mDNSs32 now);
+
+extern void     mDNSCoreReceiveRawPacket  (mDNS *const m, const mDNSu8 *const p, const mDNSu8 *const end, const mDNSInterfaceID InterfaceID);
+
+extern mDNSBool mDNSAddrIsDNSMulticast(const mDNSAddr *ip);
+
+extern CacheRecord *CreateNewCacheEntry(mDNS *const m, const mDNSu32 slot, CacheGroup *cg, mDNSs32 delay);
+extern void ScheduleNextCacheCheckTime(mDNS *const m, const mDNSu32 slot, const mDNSs32 event);
+extern void GrantCacheExtensions(mDNS *const m, DNSQuestion *q, mDNSu32 lease);
+extern void MakeNegativeCacheRecord(mDNS *const m, CacheRecord *const cr,
+	const domainname *const name, const mDNSu32 namehash, const mDNSu16 rrtype, const mDNSu16 rrclass, mDNSu32 ttl_seconds,
+	mDNSInterfaceID InterfaceID, DNSServer *dnsserver);
+extern void CompleteDeregistration(mDNS *const m, AuthRecord *rr);
+extern void AnswerCurrentQuestionWithResourceRecord(mDNS *const m, CacheRecord *const rr, const QC_result AddRecord);
+extern char *InterfaceNameForID(mDNS *const m, const mDNSInterfaceID InterfaceID);
+extern void DNSServerChangeForQuestion(mDNS *const m, DNSQuestion *q, DNSServer *newServer);
+extern void ActivateUnicastRegistration(mDNS *const m, AuthRecord *const rr);
+extern void CheckSuppressUnusableQuestions(mDNS *const m);
+extern void RetrySearchDomainQuestions(mDNS *const m);
+
+// Used only in logging to restrict the number of /etc/hosts entries printed
+extern void FreeEtcHosts(mDNS *const m, AuthRecord *const rr, mStatus result);
+// exported for using the hash for /etc/hosts AuthRecords
+extern AuthGroup *AuthGroupForName(AuthHash *r, const mDNSu32 slot, const mDNSu32 namehash, const domainname *const name);
+extern AuthGroup *AuthGroupForRecord(AuthHash *r, const mDNSu32 slot, const ResourceRecord *const rr);
+extern AuthGroup *InsertAuthRecord(mDNS *const m, AuthHash *r, AuthRecord *rr);
+extern AuthGroup *RemoveAuthRecord(mDNS *const m, AuthHash *r, AuthRecord *rr);
+
+// For now this AutoTunnel stuff is specific to Mac OS X.
+// In the future, if there's demand, we may see if we can abstract it out cleanly into the platform layer
+#if APPLE_OSX_mDNSResponder
+extern void AutoTunnelCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord);
+extern void AddNewClientTunnel(mDNS *const m, DNSQuestion *const q);
+extern void SetupLocalAutoTunnelInterface_internal(mDNS *const m, mDNSBool servicesStarting);
+extern void UpdateAutoTunnelDomainStatuses(const mDNS *const m);
+extern mStatus ActivateLocalProxy(mDNS *const m, char *ifname);
+extern void RemoveAutoTunnel6Record(mDNS *const m);
+extern mDNSBool RecordReadyForSleep(mDNS *const m, AuthRecord *rr);
+#endif
+
+// ***************************************************************************
+#if 0
+#pragma mark -
+#pragma mark - Sleep Proxy
+#endif
+
+// Sleep Proxy Server Property Encoding
+//
+// Sleep Proxy Servers are advertised using a structured service name, consisting of four
+// metrics followed by a human-readable name. The metrics assist clients in deciding which
+// Sleep Proxy Server(s) to use when multiple are available on the network. Each metric
+// is a two-digit decimal number in the range 10-99. Lower metrics are generally better.
+//
+//   AA-BB-CC-DD Name
+//
+// Metrics:
+//
+// AA = Intent
+// BB = Portability
+// CC = Marginal Power
+// DD = Total Power
+//
+//
+// ** Intent Metric **
+//
+// 20 = Dedicated Sleep Proxy Server -- a device, permanently powered on,
+//      installed for the express purpose of providing Sleep Proxy Service.
+//
+// 30 = Primary Network Infrastructure Hardware -- a router, DHCP server, NAT gateway,
+//      or similar permanently installed device which is permanently powered on.
+//      This is hardware designed for the express purpose of being network
+//      infrastructure, and for most home users is typically a single point
+//      of failure for the local network -- e.g. most home users only have
+//      a single NAT gateway / DHCP server. Even though in principle the
+//      hardware might technically be capable of running different software,
+//      a typical user is unlikely to do that. e.g. AirPort base station.
+//
+// 40 = Primary Network Infrastructure Software -- a general-purpose computer
+//      (e.g. Mac, Windows, Linux, etc.) which is currently running DHCP server
+//      or NAT gateway software, but the user could choose to turn that off
+//      fairly easily. e.g. iMac running Internet Sharing
+//
+// 50 = Secondary Network Infrastructure Hardware -- like primary infrastructure
+//      hardware, except not a single point of failure for the entire local network.
+//      For example, an AirPort base station in bridge mode. This may have clients
+//      associated with it, and if it goes away those clients will be inconvenienced,
+//      but unlike the NAT gateway / DHCP server, the entire local network is not
+//      dependent on it.
+//
+// 60 = Secondary Network Infrastructure Software -- like 50, but in a general-
+//      purpose CPU.
+//
+// 70 = Incidentally Available Hardware -- a device which has no power switch
+//      and is generally left powered on all the time. Even though it is not a
+//      part of what we conventionally consider network infrastructure (router,
+//      DHCP, NAT, DNS, etc.), and the rest of the network can operate fine
+//      without it, since it's available and unlikely to be turned off, it is a
+//      reasonable candidate for providing Sleep Proxy Service e.g. Apple TV,
+//      or an AirPort base station in client mode, associated with an existing
+//      wireless network (e.g. AirPort Express connected to a music system, or
+//      being used to share a USB printer).
+//
+// 80 = Incidentally Available Software -- a general-purpose computer which
+//      happens at this time to be set to "never sleep", and as such could be
+//      useful as a Sleep Proxy Server, but has not been intentionally provided
+//      for this purpose. Of all the Intent Metric categories this is the
+//      one most likely to be shut down or put to sleep without warning.
+//      However, if nothing else is availalable, it may be better than nothing.
+//      e.g. Office computer in the workplace which has been set to "never sleep"
+//
+//
+// ** Portability Metric **
+//
+// Inversely related to mass of device, on the basis that, all other things
+// being equal, heavier devices are less likely to be moved than lighter devices.
+// E.g. A MacBook running Internet Sharing is probably more likely to be
+// put to sleep and taken away than a Mac Pro running Internet Sharing.
+// The Portability Metric is a logarithmic decibel scale, computed by taking the
+// (approximate) mass of the device in milligrammes, taking the base 10 logarithm
+// of that, multiplying by 10, and subtracting the result from 100:
+//
+//   Portability Metric = 100 - (log10(mg) * 10)
+//
+// The Portability Metric is not necessarily computed literally from the actual
+// mass of the device; the intent is just that lower numbers indicate more
+// permanent devices, and higher numbers indicate devices more likely to be
+// removed from the network, e.g., in order of increasing portability:
+//
+// Mac Pro < iMac < Laptop < iPhone
+//
+// Example values:
+//
+// 10 = 1 metric tonne
+// 40 = 1kg
+// 70 = 1g
+// 90 = 10mg
+//
+//
+// ** Marginal Power and Total Power Metrics **
+//
+// The Marginal Power Metric is the power difference between sleeping and staying awake
+// to be a Sleep Proxy Server.
+//
+// The Total Power Metric is the total power consumption when being Sleep Proxy Server.
+//
+// The Power Metrics use a logarithmic decibel scale, computed as ten times the
+// base 10 logarithm of the (approximate) power in microwatts:
+//
+//   Power Metric = log10(uW) * 10
+//
+// Higher values indicate higher power consumption. Example values:
+//
+// 10 =  10 uW
+// 20 = 100 uW
+// 30 =   1 mW
+// 60 =   1 W
+// 90 =   1 kW
+
+typedef enum
+	{
+	mDNSSleepProxyMetric_Dedicated          = 20,
+	mDNSSleepProxyMetric_PrimaryHardware    = 30,
+	mDNSSleepProxyMetric_PrimarySoftware    = 40,
+	mDNSSleepProxyMetric_SecondaryHardware  = 50,
+	mDNSSleepProxyMetric_SecondarySoftware  = 60,
+	mDNSSleepProxyMetric_IncidentalHardware = 70,
+	mDNSSleepProxyMetric_IncidentalSoftware = 80
+	} mDNSSleepProxyMetric;
+
+extern void mDNSCoreBeSleepProxyServer_internal(mDNS *const m, mDNSu8 sps, mDNSu8 port, mDNSu8 marginalpower, mDNSu8 totpower);
+#define mDNSCoreBeSleepProxyServer(M,S,P,MP,TP) \
+	do { mDNS_Lock(m); mDNSCoreBeSleepProxyServer_internal((M),(S),(P),(MP),(TP)); mDNS_Unlock(m); } while(0)
+
+extern void FindSPSInCache(mDNS *const m, const DNSQuestion *const q, const CacheRecord *sps[3]);
+#define PrototypeSPSName(X) ((X)[0] >= 11 && (X)[3] == '-' && (X)[ 4] == '9' && (X)[ 5] == '9' && \
+                                             (X)[6] == '-' && (X)[ 7] == '9' && (X)[ 8] == '9' && \
+                                             (X)[9] == '-' && (X)[10] == '9' && (X)[11] == '9'    )
+#define ValidSPSName(X) ((X)[0] >= 5 && mDNSIsDigit((X)[1]) && mDNSIsDigit((X)[2]) && mDNSIsDigit((X)[4]) && mDNSIsDigit((X)[5]))
+#define SPSMetric(X) (!ValidSPSName(X) || PrototypeSPSName(X) ? 1000000 : \
+	((X)[1]-'0') * 100000 + ((X)[2]-'0') * 10000 + ((X)[4]-'0') * 1000 + ((X)[5]-'0') * 100 + ((X)[7]-'0') * 10 + ((X)[8]-'0'))
+
+// ***************************************************************************
+#if 0
+#pragma mark -
+#pragma mark - Compile-Time assertion checks
+#endif
+
+// Some C compiler cleverness. We can make the compiler check certain things for
+// us, and report compile-time errors if anything is wrong. The usual way to do
+// this would be to use a run-time "if" statement, but then you don't find out
+// what's wrong until you run the software. This way, if the assertion condition
+// is false, the array size is negative, and the complier complains immediately.
+
+struct CompileTimeAssertionChecks_mDNS
+	{
+	// Check that the compiler generated our on-the-wire packet format structure definitions
+	// properly packed, without adding padding bytes to align fields on 32-bit or 64-bit boundaries.
+	char assert0[(sizeof(rdataSRV)         == 262                          ) ? 1 : -1];
+	char assert1[(sizeof(DNSMessageHeader) ==  12                          ) ? 1 : -1];
+	char assert2[(sizeof(DNSMessage)       ==  12+AbsoluteMaxDNSMessageData) ? 1 : -1];
+	char assert3[(sizeof(mDNSs8)           ==   1                          ) ? 1 : -1];
+	char assert4[(sizeof(mDNSu8)           ==   1                          ) ? 1 : -1];
+	char assert5[(sizeof(mDNSs16)          ==   2                          ) ? 1 : -1];
+	char assert6[(sizeof(mDNSu16)          ==   2                          ) ? 1 : -1];
+	char assert7[(sizeof(mDNSs32)          ==   4                          ) ? 1 : -1];
+	char assert8[(sizeof(mDNSu32)          ==   4                          ) ? 1 : -1];
+	char assert9[(sizeof(mDNSOpaque16)     ==   2                          ) ? 1 : -1];
+	char assertA[(sizeof(mDNSOpaque32)     ==   4                          ) ? 1 : -1];
+	char assertB[(sizeof(mDNSOpaque128)    ==  16                          ) ? 1 : -1];
+	char assertC[(sizeof(CacheRecord  )    ==  sizeof(CacheGroup)          ) ? 1 : -1];
+	char assertD[(sizeof(int)              >=  4                           ) ? 1 : -1];
+	char assertE[(StandardAuthRDSize       >=  256                         ) ? 1 : -1];
+	char assertF[(sizeof(EthernetHeader)   ==   14                         ) ? 1 : -1];
+	char assertG[(sizeof(ARP_EthIP     )   ==   28                         ) ? 1 : -1];
+	char assertH[(sizeof(IPv4Header    )   ==   20                         ) ? 1 : -1];
+	char assertI[(sizeof(IPv6Header    )   ==   40                         ) ? 1 : -1];
+	char assertJ[(sizeof(IPv6NDP       )   ==   24                         ) ? 1 : -1];
+	char assertK[(sizeof(UDPHeader     )   ==    8                         ) ? 1 : -1];
+	char assertL[(sizeof(IKEHeader     )   ==   28                         ) ? 1 : -1];
+	char assertM[(sizeof(TCPHeader     )   ==   20                         ) ? 1 : -1];
+
+	// Check our structures are reasonable sizes. Including overly-large buffers, or embedding
+	// other overly-large structures instead of having a pointer to them, can inadvertently
+	// cause structure sizes (and therefore memory usage) to balloon unreasonably.
+	char sizecheck_RDataBody           [(sizeof(RDataBody)            ==   264) ? 1 : -1];
+	char sizecheck_ResourceRecord      [(sizeof(ResourceRecord)       <=    64) ? 1 : -1];
+	char sizecheck_AuthRecord          [(sizeof(AuthRecord)           <=  1208) ? 1 : -1];
+	char sizecheck_CacheRecord         [(sizeof(CacheRecord)          <=   184) ? 1 : -1];
+	char sizecheck_CacheGroup          [(sizeof(CacheGroup)           <=   184) ? 1 : -1];
+	char sizecheck_DNSQuestion         [(sizeof(DNSQuestion)          <=   786) ? 1 : -1];
+	char sizecheck_ZoneData            [(sizeof(ZoneData)             <=  1624) ? 1 : -1];
+	char sizecheck_NATTraversalInfo    [(sizeof(NATTraversalInfo)     <=   192) ? 1 : -1];
+	char sizecheck_HostnameInfo        [(sizeof(HostnameInfo)         <=  3050) ? 1 : -1];
+	char sizecheck_DNSServer           [(sizeof(DNSServer)            <=   320) ? 1 : -1];
+	char sizecheck_NetworkInterfaceInfo[(sizeof(NetworkInterfaceInfo) <=  6850) ? 1 : -1];
+	char sizecheck_ServiceRecordSet    [(sizeof(ServiceRecordSet)     <=  5500) ? 1 : -1];
+	char sizecheck_DomainAuthInfo      [(sizeof(DomainAuthInfo)       <=  7808) ? 1 : -1];
+	char sizecheck_ServiceInfoQuery    [(sizeof(ServiceInfoQuery)     <=  3200) ? 1 : -1];
+#if APPLE_OSX_mDNSResponder
+	char sizecheck_ClientTunnel        [(sizeof(ClientTunnel)         <=  1148) ? 1 : -1];
+#endif
+	};
+
+// ***************************************************************************
+
+#ifdef __cplusplus
+	}
+#endif
+
+#endif
diff --git a/mdnsresponder/mDNSCore/uDNS.c b/mdnsresponder/mDNSCore/uDNS.c
new file mode 100755
index 0000000..47559a4
--- /dev/null
+++ b/mdnsresponder/mDNSCore/uDNS.c
@@ -0,0 +1,4969 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2006 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+
+ * To Do:
+ * Elimate all mDNSPlatformMemAllocate/mDNSPlatformMemFree from this code -- the core code
+ * is supposed to be malloc-free so that it runs in constant memory determined at compile-time.
+ * Any dynamic run-time requirements should be handled by the platform layer below or client layer above
+ */
+
+#if APPLE_OSX_mDNSResponder
+#include <TargetConditionals.h>
+#endif
+#include "uDNS.h"
+
+#if(defined(_MSC_VER))
+	// Disable "assignment within conditional expression".
+	// Other compilers understand the convention that if you place the assignment expression within an extra pair
+	// of parentheses, this signals to the compiler that you really intended an assignment and no warning is necessary.
+	// The Microsoft compiler doesn't understand this convention, so in the absense of any other way to signal
+	// to the compiler that the assignment is intentional, we have to just turn this warning off completely.
+	#pragma warning(disable:4706)
+#endif
+
+// For domain enumeration and automatic browsing
+// This is the user's DNS search list.
+// In each of these domains we search for our special pointer records (lb._dns-sd._udp.<domain>, etc.)
+// to discover recommended domains for domain enumeration (browse, default browse, registration,
+// default registration) and possibly one or more recommended automatic browsing domains.
+mDNSexport SearchListElem *SearchList = mDNSNULL;
+
+// The value can be set to true by the Platform code e.g., MacOSX uses the plist mechanism
+mDNSBool StrictUnicastOrdering = mDNSfalse;
+
+// We keep track of the number of unicast DNS servers and log a message when we exceed 64.
+// Currently the unicast queries maintain a 64 bit map to track the valid DNS servers for that
+// question. Bit position is the index into the DNS server list. This is done so to try all
+// the servers exactly once before giving up. If we could allocate memory in the core, then
+// arbitrary limitation of 64 DNSServers can be removed.
+mDNSu8 NumUnicastDNSServers = 0;
+#define MAX_UNICAST_DNS_SERVERS	64
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark - General Utility Functions
+#endif
+
+// set retry timestamp for record with exponential backoff
+mDNSlocal void SetRecordRetry(mDNS *const m, AuthRecord *rr, mDNSu32 random)
+	{
+	rr->LastAPTime = m->timenow;
+
+	if (rr->expire && rr->refreshCount < MAX_UPDATE_REFRESH_COUNT)
+		{
+		mDNSs32 remaining = rr->expire - m->timenow;
+		rr->refreshCount++;
+		if (remaining > MIN_UPDATE_REFRESH_TIME)
+			{
+			// Refresh at 70% + random (currently it is 0 to 10%)
+			rr->ThisAPInterval =  7 * (remaining/10) + (random ? random : mDNSRandom(remaining/10));
+			// Don't update more often than 5 minutes
+			if (rr->ThisAPInterval < MIN_UPDATE_REFRESH_TIME)
+				rr->ThisAPInterval = MIN_UPDATE_REFRESH_TIME;
+			LogInfo("SetRecordRetry refresh in %d of %d for %s",
+				rr->ThisAPInterval/mDNSPlatformOneSecond, (rr->expire - m->timenow)/mDNSPlatformOneSecond, ARDisplayString(m, rr));
+			}
+		else
+			{
+			rr->ThisAPInterval = MIN_UPDATE_REFRESH_TIME;
+			LogInfo("SetRecordRetry clamping to min refresh in %d of %d for %s",
+				rr->ThisAPInterval/mDNSPlatformOneSecond, (rr->expire - m->timenow)/mDNSPlatformOneSecond, ARDisplayString(m, rr));
+			}
+		return;
+		}
+
+	rr->expire = 0;
+
+	rr->ThisAPInterval = rr->ThisAPInterval * QuestionIntervalStep;	// Same Retry logic as Unicast Queries
+	if (rr->ThisAPInterval < INIT_RECORD_REG_INTERVAL)
+		rr->ThisAPInterval = INIT_RECORD_REG_INTERVAL;
+	if (rr->ThisAPInterval > MAX_RECORD_REG_INTERVAL)
+		rr->ThisAPInterval = MAX_RECORD_REG_INTERVAL;
+
+	LogInfo("SetRecordRetry retry in %d ms for %s", rr->ThisAPInterval, ARDisplayString(m, rr));
+	}
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark - Name Server List Management
+#endif
+
+mDNSexport DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, const mDNSAddr *addr, const mDNSIPPort port, mDNSBool scoped, mDNSu32 timeout)
+	{
+	DNSServer **p = &m->DNSServers;
+	DNSServer *tmp = mDNSNULL;
+	
+	if ((NumUnicastDNSServers + 1) > MAX_UNICAST_DNS_SERVERS)
+		{
+		LogMsg("mDNS_AddDNSServer: DNS server limit of %d reached, not adding this server", MAX_UNICAST_DNS_SERVERS);
+		return mDNSNULL;
+		}
+
+	if (!d) d = (const domainname *)"";
+
+	LogInfo("mDNS_AddDNSServer: Adding %#a for %##s, InterfaceID %p, scoped %d", addr, d->c, interface, scoped);
+	if (m->mDNS_busy != m->mDNS_reentrancy+1)
+		LogMsg("mDNS_AddDNSServer: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
+
+	while (*p)	// Check if we already have this {interface,address,port,domain} tuple registered
+		{
+		if ((*p)->scoped == scoped && (*p)->interface == interface && (*p)->teststate != DNSServer_Disabled &&
+			mDNSSameAddress(&(*p)->addr, addr) && mDNSSameIPPort((*p)->port, port) && SameDomainName(&(*p)->domain, d))
+			{
+			if (!((*p)->flags & DNSServer_FlagDelete)) debugf("Note: DNS Server %#a:%d for domain %##s (%p) registered more than once", addr, mDNSVal16(port), d->c, interface);
+			(*p)->flags &= ~DNSServer_FlagDelete;
+			tmp = *p;
+			*p = tmp->next;
+			tmp->next = mDNSNULL;
+			}
+		else
+			p=&(*p)->next;
+		}
+
+	if (tmp) *p = tmp; // move to end of list, to ensure ordering from platform layer
+	else
+		{
+		// allocate, add to list
+		*p = mDNSPlatformMemAllocate(sizeof(**p));
+		if (!*p) LogMsg("Error: mDNS_AddDNSServer - malloc");
+		else
+			{
+			NumUnicastDNSServers++;
+			(*p)->scoped	= scoped;
+			(*p)->interface = interface;
+			(*p)->addr      = *addr;
+			(*p)->port      = port;
+			(*p)->flags     = DNSServer_FlagNew;
+			(*p)->teststate = /* DNSServer_Untested */ DNSServer_Passed;
+			(*p)->lasttest  = m->timenow - INIT_UCAST_POLL_INTERVAL;
+			(*p)->timeout   = timeout;
+			AssignDomainName(&(*p)->domain, d);
+			(*p)->next = mDNSNULL;
+			}
+		}
+	(*p)->penaltyTime = 0;
+	return(*p);
+	}
+
+// PenalizeDNSServer is called when the number of queries to the unicast
+// DNS server exceeds MAX_UCAST_UNANSWERED_QUERIES or when we receive an
+// error e.g., SERV_FAIL from DNS server.
+mDNSexport void PenalizeDNSServer(mDNS *const m, DNSQuestion *q)
+	{
+	DNSServer *new;
+	DNSServer *orig = q->qDNSServer;
+	
+	if (m->mDNS_busy != m->mDNS_reentrancy+1)
+		LogMsg("PenalizeDNSServer: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
+
+	// This should never happen. Whenever we change DNS server, we change the ID on the question and hence
+	// we should never accept a response after we penalize a DNS server e.g., send two queries, no response,
+	// penalize DNS server and no new servers to pick for the question and hence qDNSServer is NULL. If we
+	// receive a response now, the DNS server can be NULL. But we won't because the ID already has been
+	// changed.
+	if (!q->qDNSServer)
+		{
+		LogMsg("PenalizeDNSServer: ERROR!! Null DNS server for %##s (%s) %d", q->qname.c, DNSTypeName(q->qtype), q->unansweredQueries);
+		goto end;
+		}
+
+	LogInfo("PenalizeDNSServer: Penalizing DNS server %#a:%d question (%##s) for question %p %##s (%s) SuppressUnusable %d",
+		&q->qDNSServer->addr, mDNSVal16(q->qDNSServer->port), q->qDNSServer->domain.c, q, q->qname.c, DNSTypeName(q->qtype),
+		q->SuppressUnusable);
+
+	// If strict ordering of unicast servers needs to be preserved, we just lookup
+	// the next best match server below
+	//
+	// If strict ordering is not required which is the default behavior, we penalize the server
+	// for DNSSERVER_PENALTY_TIME. We may also use additional logic e.g., don't penalize for PTR
+	// in the future.
+
+	if (!StrictUnicastOrdering)
+		{
+		LogInfo("PenalizeDNSServer: Strict Unicast Ordering is FALSE");
+		// We penalize the server so that new queries don't pick this server for DNSSERVER_PENALTY_TIME
+		// XXX Include other logic here to see if this server should really be penalized
+		//
+		if (q->qtype == kDNSType_PTR)
+			{
+			LogInfo("PenalizeDNSServer: Not Penalizing PTR question");
+			}
+		else
+			{
+			LogInfo("PenalizeDNSServer: Penalizing question type %d", q->qtype);
+			q->qDNSServer->penaltyTime = NonZeroTime(m->timenow + DNSSERVER_PENALTY_TIME);
+			}
+		}
+	else
+		{
+		LogInfo("PenalizeDNSServer: Strict Unicast Ordering is TRUE");
+		}
+
+end:
+	new = GetServerForQuestion(m, q);
+
+	
+	if (new == orig)
+		{
+		if (new)
+			LogMsg("PenalizeDNSServer: ERROR!! GetServerForQuestion returned the same server %#a:%d", &new->addr, 
+		    	mDNSVal16(new->port));
+		else
+			LogMsg("PenalizeDNSServer: ERROR!! GetServerForQuestion returned the same server NULL");
+		q->ThisQInterval = 0;	// Inactivate this question so that we dont bombard the network
+		}
+	else
+		{
+		// The new DNSServer is set in DNSServerChangeForQuestion
+		DNSServerChangeForQuestion(m, q, new);
+
+		if (new)
+			{
+			LogInfo("PenalizeDNSServer: Server for %##s (%s) changed to %#a:%d (%##s)",
+				q->qname.c, DNSTypeName(q->qtype), &q->qDNSServer->addr, mDNSVal16(q->qDNSServer->port), q->qDNSServer->domain.c);
+			// We want to try the next server immediately. As the question may already have backed off, reset
+			// the interval. We do this only the first time when we try all the DNS servers. Once we reached the end of
+			// list and retrying all the servers again e.g., at least one server failed to respond in the previous try, we
+			// use the normal backoff which is done in uDNS_CheckCurrentQuestion when we send the packet out.
+			if (!q->triedAllServersOnce)
+				{
+				q->ThisQInterval = InitialQuestionInterval;
+				q->LastQTime  = m->timenow - q->ThisQInterval;
+				SetNextQueryTime(m, q);
+				}
+			}
+		else
+			{
+			// We don't have any more DNS servers for this question. If some server in the list did not return
+			// any response, we need to keep retrying till we get a response. uDNS_CheckCurrentQuestion handles
+			// this case.
+			//
+			// If all servers responded with a negative response, We need to do two things. First, generate a
+			// negative response so that applications get a reply. We also need to reinitialize the DNS servers
+			// so that when the cache expires, we can restart the query. 
+			//
+			// Negative response may be generated in two ways.
+			//
+			// 1. AnswerQuestionForDNSServerChanges (called from DNSServerChangedForQuestion) might find some
+			//    cache entries and answer this question.
+			// 2. uDNS_CheckCurrentQuestion will create a new cache entry and answer this question
+			//
+			// For (1), it might be okay to reinitialize the DNS servers here. But for (2), we can't do it here
+			// because uDNS_CheckCurrentQuestion will try resending the queries. Hence, to be consistent, we
+			// defer reintializing the DNS servers up until generating a negative cache response.
+			//
+			// Be careful not to touch the ThisQInterval here. For a normal question, when we answer the question
+			// in AnswerCurrentQuestionWithResourceRecord will set ThisQInterval to MaxQuestionInterval and hence
+			// the next query will not happen until cache expiry. If it is a long lived question,
+			// AnswerCurrentQuestionWithResourceRecord will not set it to MaxQuestionInterval. In that case,
+			// we want the normal backoff to work.
+			LogInfo("PenalizeDNSServer: Server for %p, %##s (%s) changed to NULL, Interval %d", q, q->qname.c, DNSTypeName(q->qtype), q->ThisQInterval);
+			}
+		q->unansweredQueries = 0;
+
+		}
+	}
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark - authorization management
+#endif
+
+mDNSlocal DomainAuthInfo *GetAuthInfoForName_direct(mDNS *m, const domainname *const name)
+	{
+	const domainname *n = name;
+	while (n->c[0])
+		{
+		DomainAuthInfo *ptr;
+		for (ptr = m->AuthInfoList; ptr; ptr = ptr->next)
+			if (SameDomainName(&ptr->domain, n))
+				{
+				debugf("GetAuthInfoForName %##s Matched %##s Key name %##s", name->c, ptr->domain.c, ptr->keyname.c);
+				return(ptr);
+				}
+		n = (const domainname *)(n->c + 1 + n->c[0]);
+		}
+	//LogInfo("GetAuthInfoForName none found for %##s", name->c);
+	return mDNSNULL;
+	}
+
+// MUST be called with lock held
+mDNSexport DomainAuthInfo *GetAuthInfoForName_internal(mDNS *m, const domainname *const name)
+	{
+	DomainAuthInfo **p = &m->AuthInfoList;
+
+	if (m->mDNS_busy != m->mDNS_reentrancy+1)
+		LogMsg("GetAuthInfoForName_internal: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
+
+	// First purge any dead keys from the list
+	while (*p)
+		{
+		if ((*p)->deltime && m->timenow - (*p)->deltime >= 0 && AutoTunnelUnregistered(*p))
+			{
+			DNSQuestion *q;
+			DomainAuthInfo *info = *p;
+			LogInfo("GetAuthInfoForName_internal deleting expired key %##s %##s", info->domain.c, info->keyname.c);
+			*p = info->next;	// Cut DomainAuthInfo from list *before* scanning our question list updating AuthInfo pointers
+			for (q = m->Questions; q; q=q->next)
+				if (q->AuthInfo == info)
+					{
+					q->AuthInfo = GetAuthInfoForName_direct(m, &q->qname);
+					debugf("GetAuthInfoForName_internal updated q->AuthInfo from %##s to %##s for %##s (%s)",
+						info->domain.c, q->AuthInfo ? q->AuthInfo->domain.c : mDNSNULL, q->qname.c, DNSTypeName(q->qtype));
+					}
+
+			// Probably not essential, but just to be safe, zero out the secret key data
+			// so we don't leave it hanging around in memory
+			// (where it could potentially get exposed via some other bug)
+			mDNSPlatformMemZero(info, sizeof(*info));
+			mDNSPlatformMemFree(info);
+			}
+		else
+			p = &(*p)->next;
+		}
+
+	return(GetAuthInfoForName_direct(m, name));
+	}
+
+mDNSexport DomainAuthInfo *GetAuthInfoForName(mDNS *m, const domainname *const name)
+	{
+	DomainAuthInfo *d;
+	mDNS_Lock(m);
+	d = GetAuthInfoForName_internal(m, name);
+	mDNS_Unlock(m);
+	return(d);
+	}
+
+// MUST be called with the lock held
+mDNSexport mStatus mDNS_SetSecretForDomain(mDNS *m, DomainAuthInfo *info,
+	const domainname *domain, const domainname *keyname, const char *b64keydata, const domainname *hostname, mDNSIPPort *port, const char *autoTunnelPrefix)
+	{
+	DNSQuestion *q;
+	DomainAuthInfo **p = &m->AuthInfoList;
+	if (!info || !b64keydata) { LogMsg("mDNS_SetSecretForDomain: ERROR: info %p b64keydata %p", info, b64keydata); return(mStatus_BadParamErr); }
+
+	LogInfo("mDNS_SetSecretForDomain: domain %##s key %##s%s%s", domain->c, keyname->c, autoTunnelPrefix ? " prefix " : "", autoTunnelPrefix ? autoTunnelPrefix : "");
+
+	info->AutoTunnel = autoTunnelPrefix;
+	AssignDomainName(&info->domain,  domain);
+	AssignDomainName(&info->keyname, keyname);
+	if (hostname)
+		AssignDomainName(&info->hostname, hostname);
+	else
+		info->hostname.c[0] = 0;
+	if (port)
+		info->port = *port;
+	else
+		info->port = zeroIPPort;
+	mDNS_snprintf(info->b64keydata, sizeof(info->b64keydata), "%s", b64keydata);
+
+	if (DNSDigest_ConstructHMACKeyfromBase64(info, b64keydata) < 0)
+		{
+		LogMsg("mDNS_SetSecretForDomain: ERROR: Could not convert shared secret from base64: domain %##s key %##s %s", domain->c, keyname->c, mDNS_LoggingEnabled ? b64keydata : "");
+		return(mStatus_BadParamErr);
+		}
+
+	// Don't clear deltime until after we've ascertained that b64keydata is valid
+	info->deltime = 0;
+
+	while (*p && (*p) != info) p=&(*p)->next;
+	if (*p) {LogInfo("mDNS_SetSecretForDomain: Domain %##s Already in list", (*p)->domain.c); return(mStatus_AlreadyRegistered);}
+
+	// Caution: Only zero AutoTunnelHostRecord.namestorage and AutoTunnelNAT.clientContext AFTER we've determined that this is a NEW DomainAuthInfo
+	// being added to the list. Otherwise we risk smashing our AutoTunnel host records and NATOperation that are already active and in use.
+	info->AutoTunnelHostRecord.resrec.RecordType = kDNSRecordTypeUnregistered;
+	info->AutoTunnelHostRecord.namestorage.c[0] = 0;
+	info->AutoTunnelTarget    .resrec.RecordType = kDNSRecordTypeUnregistered;
+	info->AutoTunnelDeviceInfo.resrec.RecordType = kDNSRecordTypeUnregistered;
+	info->AutoTunnelService   .resrec.RecordType = kDNSRecordTypeUnregistered;
+	info->AutoTunnel6Record   .resrec.RecordType = kDNSRecordTypeUnregistered;
+	info->AutoTunnelNAT.clientContext = mDNSNULL;
+	info->next = mDNSNULL;
+	*p = info;
+
+	// Check to see if adding this new DomainAuthInfo has changed the credentials for any of our questions
+	for (q = m->Questions; q; q=q->next)
+		{
+		DomainAuthInfo *newinfo = GetAuthInfoForQuestion(m, q);
+		if (q->AuthInfo != newinfo)
+			{
+			debugf("mDNS_SetSecretForDomain updating q->AuthInfo from %##s to %##s for %##s (%s)",
+				q->AuthInfo ? q->AuthInfo->domain.c : mDNSNULL,
+				newinfo     ? newinfo    ->domain.c : mDNSNULL, q->qname.c, DNSTypeName(q->qtype));
+			q->AuthInfo = newinfo;
+			}
+		}
+
+	return(mStatus_NoError);
+	}
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - NAT Traversal
+#endif
+
+mDNSlocal mStatus uDNS_SendNATMsg(mDNS *m, NATTraversalInfo *info)
+	{
+	mStatus err = mStatus_NoError;
+
+	// send msg if we have a router and it is a private address
+	if (!mDNSIPv4AddressIsZero(m->Router.ip.v4) && mDNSv4AddrIsRFC1918(&m->Router.ip.v4))
+		{
+		union { NATAddrRequest NATAddrReq; NATPortMapRequest NATPortReq; } u = { { NATMAP_VERS, NATOp_AddrRequest } } ;
+		const mDNSu8 *end = (mDNSu8 *)&u + sizeof(NATAddrRequest);
+	
+		if (info)			// For NATOp_MapUDP and NATOp_MapTCP, fill in additional fields
+			{
+			mDNSu8 *p = (mDNSu8 *)&u.NATPortReq.NATReq_lease;
+			u.NATPortReq.opcode  = info->Protocol;
+			u.NATPortReq.unused  = zeroID;
+			u.NATPortReq.intport = info->IntPort;
+			u.NATPortReq.extport = info->RequestedPort;
+			p[0] = (mDNSu8)((info->NATLease >> 24) &  0xFF);
+			p[1] = (mDNSu8)((info->NATLease >> 16) &  0xFF);
+			p[2] = (mDNSu8)((info->NATLease >>  8) &  0xFF);
+			p[3] = (mDNSu8)( info->NATLease        &  0xFF);
+			end = (mDNSu8 *)&u + sizeof(NATPortMapRequest);
+			}
+
+		err = mDNSPlatformSendUDP(m, (mDNSu8 *)&u, end, 0, mDNSNULL, &m->Router, NATPMPPort);
+
+#ifdef _LEGACY_NAT_TRAVERSAL_
+		if (mDNSIPPortIsZero(m->UPnPRouterPort) || mDNSIPPortIsZero(m->UPnPSOAPPort)) LNT_SendDiscoveryMsg(m);
+		else if (info) err = LNT_MapPort(m, info);
+		else err = LNT_GetExternalAddress(m);
+#endif // _LEGACY_NAT_TRAVERSAL_
+		}
+	return(err);
+	}
+
+mDNSexport void RecreateNATMappings(mDNS *const m)
+	{
+	NATTraversalInfo *n;
+	for (n = m->NATTraversals; n; n=n->next)
+		{
+		n->ExpiryTime    = 0;		// Mark this mapping as expired
+		n->retryInterval = NATMAP_INIT_RETRY;
+		n->retryPortMap  = m->timenow;
+#ifdef _LEGACY_NAT_TRAVERSAL_
+		if (n->tcpInfo.sock) { mDNSPlatformTCPCloseConnection(n->tcpInfo.sock); n->tcpInfo.sock = mDNSNULL; }
+#endif // _LEGACY_NAT_TRAVERSAL_
+		}
+
+	m->NextScheduledNATOp = m->timenow;		// Need to send packets immediately
+	}
+
+mDNSexport void natTraversalHandleAddressReply(mDNS *const m, mDNSu16 err, mDNSv4Addr ExtAddr)
+	{
+	static mDNSu16 last_err = 0;
+	
+	if (err)
+		{
+		if (err != last_err) LogMsg("Error getting external address %d", err);
+		ExtAddr = zerov4Addr;
+		}
+	else
+		{
+		LogInfo("Received external IP address %.4a from NAT", &ExtAddr);
+		if (mDNSv4AddrIsRFC1918(&ExtAddr))
+			LogMsg("Double NAT (external NAT gateway address %.4a is also a private RFC 1918 address)", &ExtAddr);
+		if (mDNSIPv4AddressIsZero(ExtAddr))
+			err = NATErr_NetFail; // fake error to handle routers that pathologically report success with the zero address
+		}
+		
+	if (!mDNSSameIPv4Address(m->ExternalAddress, ExtAddr))
+		{
+		m->ExternalAddress = ExtAddr;
+		RecreateNATMappings(m);		// Also sets NextScheduledNATOp for us
+		}
+
+	if (!err) // Success, back-off to maximum interval
+		m->retryIntervalGetAddr = NATMAP_MAX_RETRY_INTERVAL;
+	else if (!last_err) // Failure after success, retry quickly (then back-off exponentially)
+		m->retryIntervalGetAddr = NATMAP_INIT_RETRY;
+	// else back-off normally in case of pathological failures
+
+	m->retryGetAddr = m->timenow + m->retryIntervalGetAddr;
+	if (m->NextScheduledNATOp - m->retryIntervalGetAddr > 0)
+		m->NextScheduledNATOp = m->retryIntervalGetAddr;
+
+	last_err = err;
+	}
+
+// Both places that call NATSetNextRenewalTime() update m->NextScheduledNATOp correctly afterwards
+mDNSlocal void NATSetNextRenewalTime(mDNS *const m, NATTraversalInfo *n)
+	{
+	n->retryInterval = (n->ExpiryTime - m->timenow)/2;
+	if (n->retryInterval < NATMAP_MIN_RETRY_INTERVAL)	// Min retry interval is 2 seconds
+		n->retryInterval = NATMAP_MIN_RETRY_INTERVAL;
+	n->retryPortMap = m->timenow + n->retryInterval;
+	}
+
+// Note: When called from handleLNTPortMappingResponse() only pkt->err, pkt->extport and pkt->NATRep_lease fields are filled in
+mDNSexport void natTraversalHandlePortMapReply(mDNS *const m, NATTraversalInfo *n, const mDNSInterfaceID InterfaceID, mDNSu16 err, mDNSIPPort extport, mDNSu32 lease)
+	{
+	const char *prot = n->Protocol == NATOp_MapUDP ? "UDP" : n->Protocol == NATOp_MapTCP ? "TCP" : "?";
+	(void)prot;
+	n->NewResult = err;
+	if (err || lease == 0 || mDNSIPPortIsZero(extport))
+		{
+		LogInfo("natTraversalHandlePortMapReply: %p Response %s Port %5d External Port %5d lease %d error %d",
+			n, prot, mDNSVal16(n->IntPort), mDNSVal16(extport), lease, err);
+		n->retryInterval = NATMAP_MAX_RETRY_INTERVAL;
+		n->retryPortMap = m->timenow + NATMAP_MAX_RETRY_INTERVAL;
+		// No need to set m->NextScheduledNATOp here, since we're only ever extending the m->retryPortMap time
+		if      (err == NATErr_Refused)                     n->NewResult = mStatus_NATPortMappingDisabled;
+		else if (err > NATErr_None && err <= NATErr_Opcode) n->NewResult = mStatus_NATPortMappingUnsupported;
+		}
+	else
+		{
+		if (lease > 999999999UL / mDNSPlatformOneSecond)
+			lease = 999999999UL / mDNSPlatformOneSecond;
+		n->ExpiryTime = NonZeroTime(m->timenow + lease * mDNSPlatformOneSecond);
+	
+		if (!mDNSSameIPPort(n->RequestedPort, extport))
+			LogInfo("natTraversalHandlePortMapReply: %p Response %s Port %5d External Port %5d changed to %5d",
+				n, prot, mDNSVal16(n->IntPort), mDNSVal16(n->RequestedPort), mDNSVal16(extport));
+
+		n->InterfaceID   = InterfaceID;
+		n->RequestedPort = extport;
+	
+		LogInfo("natTraversalHandlePortMapReply: %p Response %s Port %5d External Port %5d lease %d",
+			n, prot, mDNSVal16(n->IntPort), mDNSVal16(extport), lease);
+	
+		NATSetNextRenewalTime(m, n);			// Got our port mapping; now set timer to renew it at halfway point
+		m->NextScheduledNATOp = m->timenow;		// May need to invoke client callback immediately
+		}
+	}
+
+// Must be called with the mDNS_Lock held
+mDNSexport mStatus mDNS_StartNATOperation_internal(mDNS *const m, NATTraversalInfo *traversal)
+	{
+	NATTraversalInfo **n;
+	
+	LogInfo("mDNS_StartNATOperation_internal %p Protocol %d IntPort %d RequestedPort %d NATLease %d", traversal,
+		traversal->Protocol, mDNSVal16(traversal->IntPort), mDNSVal16(traversal->RequestedPort), traversal->NATLease);
+
+	// Note: It important that new traversal requests are appended at the *end* of the list, not prepended at the start
+	for (n = &m->NATTraversals; *n; n=&(*n)->next)
+		{
+		if (traversal == *n)
+			{
+			LogMsg("Error! Tried to add a NAT traversal that's already in the active list: request %p Prot %d Int %d TTL %d",
+				traversal, traversal->Protocol, mDNSVal16(traversal->IntPort), traversal->NATLease);
+			#if ForceAlerts
+				*(long*)0 = 0;
+			#endif
+			return(mStatus_AlreadyRegistered);
+			}
+		if (traversal->Protocol && traversal->Protocol == (*n)->Protocol && mDNSSameIPPort(traversal->IntPort, (*n)->IntPort) &&
+			!mDNSSameIPPort(traversal->IntPort, SSHPort))
+			LogMsg("Warning: Created port mapping request %p Prot %d Int %d TTL %d "
+				"duplicates existing port mapping request %p Prot %d Int %d TTL %d",
+				traversal, traversal->Protocol, mDNSVal16(traversal->IntPort), traversal->NATLease,
+				*n,        (*n)     ->Protocol, mDNSVal16((*n)     ->IntPort), (*n)     ->NATLease);
+		}
+
+	// Initialize necessary fields
+	traversal->next            = mDNSNULL;
+	traversal->ExpiryTime      = 0;
+	traversal->retryInterval   = NATMAP_INIT_RETRY;
+	traversal->retryPortMap    = m->timenow;
+	traversal->NewResult       = mStatus_NoError;
+	traversal->ExternalAddress = onesIPv4Addr;
+	traversal->ExternalPort    = zeroIPPort;
+	traversal->Lifetime        = 0;
+	traversal->Result          = mStatus_NoError;
+
+	// set default lease if necessary
+	if (!traversal->NATLease) traversal->NATLease = NATMAP_DEFAULT_LEASE;
+
+#ifdef _LEGACY_NAT_TRAVERSAL_
+	mDNSPlatformMemZero(&traversal->tcpInfo, sizeof(traversal->tcpInfo));
+#endif // _LEGACY_NAT_TRAVERSAL_
+
+	if (!m->NATTraversals)		// If this is our first NAT request, kick off an address request too
+		{
+		m->retryGetAddr         = m->timenow;
+		m->retryIntervalGetAddr = NATMAP_INIT_RETRY;
+		}
+
+	m->NextScheduledNATOp = m->timenow;	// This will always trigger sending the packet ASAP, and generate client callback if necessary
+
+	*n = traversal;		// Append new NATTraversalInfo to the end of our list
+
+	return(mStatus_NoError);
+	}
+
+// Must be called with the mDNS_Lock held
+mDNSexport mStatus mDNS_StopNATOperation_internal(mDNS *m, NATTraversalInfo *traversal)
+	{
+	mDNSBool unmap = mDNStrue;
+	NATTraversalInfo *p;
+	NATTraversalInfo **ptr = &m->NATTraversals;
+
+	while (*ptr && *ptr != traversal) ptr=&(*ptr)->next;
+	if (*ptr) *ptr = (*ptr)->next;		// If we found it, cut this NATTraversalInfo struct from our list
+	else
+		{
+		LogMsg("mDNS_StopNATOperation_internal: NATTraversalInfo %p not found in list", traversal);
+		return(mStatus_BadReferenceErr);
+		}
+
+	LogInfo("mDNS_StopNATOperation_internal %p %d %d %d %d", traversal,
+		traversal->Protocol, mDNSVal16(traversal->IntPort), mDNSVal16(traversal->RequestedPort), traversal->NATLease);
+
+	if (m->CurrentNATTraversal == traversal)
+		m->CurrentNATTraversal = m->CurrentNATTraversal->next;
+
+	if (traversal->Protocol)
+		for (p = m->NATTraversals; p; p=p->next)
+			if (traversal->Protocol == p->Protocol && mDNSSameIPPort(traversal->IntPort, p->IntPort))
+				{
+				if (!mDNSSameIPPort(traversal->IntPort, SSHPort))
+					LogMsg("Warning: Removed port mapping request %p Prot %d Int %d TTL %d "
+						"duplicates existing port mapping request %p Prot %d Int %d TTL %d",
+						traversal, traversal->Protocol, mDNSVal16(traversal->IntPort), traversal->NATLease,
+						p,         p        ->Protocol, mDNSVal16(p        ->IntPort), p        ->NATLease);
+				unmap = mDNSfalse;
+				}
+
+	if (traversal->ExpiryTime && unmap)
+		{
+		traversal->NATLease = 0;
+		traversal->retryInterval = 0;
+		uDNS_SendNATMsg(m, traversal);
+		}
+
+	// Even if we DIDN'T make a successful UPnP mapping yet, we might still have a partially-open TCP connection we need to clean up
+	#ifdef _LEGACY_NAT_TRAVERSAL_
+		{
+		mStatus err = LNT_UnmapPort(m, traversal);
+		if (err) LogMsg("Legacy NAT Traversal - unmap request failed with error %d", err);
+		}
+	#endif // _LEGACY_NAT_TRAVERSAL_
+
+	return(mStatus_NoError);
+	}
+
+mDNSexport mStatus mDNS_StartNATOperation(mDNS *const m, NATTraversalInfo *traversal)
+	{
+	mStatus status;
+	mDNS_Lock(m);
+	status = mDNS_StartNATOperation_internal(m, traversal);
+	mDNS_Unlock(m);
+	return(status);
+	}
+
+mDNSexport mStatus mDNS_StopNATOperation(mDNS *const m, NATTraversalInfo *traversal)
+	{
+	mStatus status;
+	mDNS_Lock(m);
+	status = mDNS_StopNATOperation_internal(m, traversal);
+	mDNS_Unlock(m);
+	return(status);
+	}
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - Long-Lived Queries
+#endif
+
+// Lock must be held -- otherwise m->timenow is undefined
+mDNSlocal void StartLLQPolling(mDNS *const m, DNSQuestion *q)
+	{
+	debugf("StartLLQPolling: %##s", q->qname.c);
+	q->state = LLQ_Poll;
+	q->ThisQInterval = INIT_UCAST_POLL_INTERVAL;
+	// We want to send our poll query ASAP, but the "+ 1" is because if we set the time to now,
+	// we risk causing spurious "SendQueries didn't send all its queries" log messages
+	q->LastQTime     = m->timenow - q->ThisQInterval + 1;
+	SetNextQueryTime(m, q);
+#if APPLE_OSX_mDNSResponder
+	UpdateAutoTunnelDomainStatuses(m);
+#endif
+	}
+
+mDNSlocal mDNSu8 *putLLQ(DNSMessage *const msg, mDNSu8 *ptr, const DNSQuestion *const question, const LLQOptData *const data)
+	{
+	AuthRecord rr;
+	ResourceRecord *opt = &rr.resrec;
+	rdataOPT *optRD;
+
+	//!!!KRS when we implement multiple llqs per message, we'll need to memmove anything past the question section
+	ptr = putQuestion(msg, ptr, msg->data + AbsoluteMaxDNSMessageData, &question->qname, question->qtype, question->qclass);
+	if (!ptr) { LogMsg("ERROR: putLLQ - putQuestion"); return mDNSNULL; }
+
+	// locate OptRR if it exists, set pointer to end
+	// !!!KRS implement me
+
+	// format opt rr (fields not specified are zero-valued)
+	mDNS_SetupResourceRecord(&rr, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
+	opt->rrclass    = NormalMaxDNSMessageData;
+	opt->rdlength   = sizeof(rdataOPT);	// One option in this OPT record
+	opt->rdestimate = sizeof(rdataOPT);
+
+	optRD = &rr.resrec.rdata->u.opt[0];
+	optRD->opt = kDNSOpt_LLQ;
+	optRD->u.llq = *data;
+	ptr = PutResourceRecordTTLJumbo(msg, ptr, &msg->h.numAdditionals, opt, 0);
+	if (!ptr) { LogMsg("ERROR: putLLQ - PutResourceRecordTTLJumbo"); return mDNSNULL; }
+
+	return ptr;
+	}
+
+// Normally we'd just request event packets be sent directly to m->LLQNAT.ExternalPort, except...
+// with LLQs over TLS/TCP we're doing a weird thing where instead of requesting packets be sent to ExternalAddress:ExternalPort
+// we're requesting that packets be sent to ExternalPort, but at the source address of our outgoing TCP connection.
+// Normally, after going through the NAT gateway, the source address of our outgoing TCP connection is the same as ExternalAddress,
+// so this is fine, except when the TCP connection ends up going over a VPN tunnel instead.
+// To work around this, if we find that the source address for our TCP connection is not a private address, we tell the Dot Mac
+// LLQ server to send events to us directly at port 5353 on that address, instead of at our mapped external NAT port.
+
+mDNSlocal mDNSu16 GetLLQEventPort(const mDNS *const m, const mDNSAddr *const dst)
+	{
+	mDNSAddr src;
+	mDNSPlatformSourceAddrForDest(&src, dst);
+	//LogMsg("GetLLQEventPort: src %#a for dst %#a (%d)", &src, dst, mDNSv4AddrIsRFC1918(&src.ip.v4) ? mDNSVal16(m->LLQNAT.ExternalPort) : 0);
+	return(mDNSv4AddrIsRFC1918(&src.ip.v4) ? mDNSVal16(m->LLQNAT.ExternalPort) : mDNSVal16(MulticastDNSPort));
+	}
+
+// Normally called with llq set.
+// May be called with llq NULL, when retransmitting a lost Challenge Response
+mDNSlocal void sendChallengeResponse(mDNS *const m, DNSQuestion *const q, const LLQOptData *llq)
+	{
+	mDNSu8 *responsePtr = m->omsg.data;
+	LLQOptData llqBuf;
+
+	if (q->tcp) { LogMsg("sendChallengeResponse: ERROR!!: question %##s (%s) tcp non-NULL", q->qname.c, DNSTypeName(q->qtype)); return; }
+
+	if (PrivateQuery(q)) { LogMsg("sendChallengeResponse: ERROR!!: Private Query %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); return; }
+
+	if (q->ntries++ == kLLQ_MAX_TRIES)
+		{
+		LogMsg("sendChallengeResponse: %d failed attempts for LLQ %##s", kLLQ_MAX_TRIES, q->qname.c);
+		StartLLQPolling(m,q);
+		return;
+		}
+
+	if (!llq)		// Retransmission: need to make a new LLQOptData
+		{
+		llqBuf.vers     = kLLQ_Vers;
+		llqBuf.llqOp    = kLLQOp_Setup;
+		llqBuf.err      = LLQErr_NoError;	// Don't need to tell server UDP notification port when sending over UDP
+		llqBuf.id       = q->id;
+		llqBuf.llqlease = q->ReqLease;
+		llq = &llqBuf;
+		}
+
+	q->LastQTime     = m->timenow;
+	q->ThisQInterval = q->tcp ? 0 : (kLLQ_INIT_RESEND * q->ntries * mDNSPlatformOneSecond);		// If using TCP, don't need to retransmit
+	SetNextQueryTime(m, q);
+
+	// To simulate loss of challenge response packet, uncomment line below
+	//if (q->ntries == 1) return;
+
+	InitializeDNSMessage(&m->omsg.h, q->TargetQID, uQueryFlags);
+	responsePtr = putLLQ(&m->omsg, responsePtr, q, llq);
+	if (responsePtr)
+		{
+		mStatus err = mDNSSendDNSMessage(m, &m->omsg, responsePtr, mDNSInterface_Any, q->LocalSocket, &q->servAddr, q->servPort, mDNSNULL, mDNSNULL);
+		if (err) { LogMsg("sendChallengeResponse: mDNSSendDNSMessage%s failed: %d", q->tcp ? " (TCP)" : "", err); }
+		}
+	else StartLLQPolling(m,q);
+	}
+
+mDNSlocal void SetLLQTimer(mDNS *const m, DNSQuestion *const q, const LLQOptData *const llq)
+	{
+	mDNSs32 lease = (mDNSs32)llq->llqlease * mDNSPlatformOneSecond;
+	q->ReqLease      = llq->llqlease;
+	q->LastQTime     = m->timenow;
+	q->expire        = m->timenow + lease;
+	q->ThisQInterval = lease/2 + mDNSRandom(lease/10);
+	debugf("SetLLQTimer setting %##s (%s) to %d %d", q->qname.c, DNSTypeName(q->qtype), lease/mDNSPlatformOneSecond, q->ThisQInterval/mDNSPlatformOneSecond);
+	SetNextQueryTime(m, q);
+	}
+
+mDNSlocal void recvSetupResponse(mDNS *const m, mDNSu8 rcode, DNSQuestion *const q, const LLQOptData *const llq)
+	{
+	if (rcode && rcode != kDNSFlag1_RC_NXDomain)
+		{ LogMsg("ERROR: recvSetupResponse %##s (%s) - rcode && rcode != kDNSFlag1_RC_NXDomain", q->qname.c, DNSTypeName(q->qtype)); return; }
+
+	if (llq->llqOp != kLLQOp_Setup)
+		{ LogMsg("ERROR: recvSetupResponse %##s (%s) - bad op %d", q->qname.c, DNSTypeName(q->qtype), llq->llqOp); return; }
+
+	if (llq->vers != kLLQ_Vers)
+		{ LogMsg("ERROR: recvSetupResponse %##s (%s) - bad vers %d", q->qname.c, DNSTypeName(q->qtype), llq->vers); return; }
+
+	if (q->state == LLQ_InitialRequest)
+		{
+		//LogInfo("Got LLQ_InitialRequest");
+
+		if (llq->err) { LogMsg("recvSetupResponse - received llq->err %d from server", llq->err); StartLLQPolling(m,q); return; }
+	
+		if (q->ReqLease != llq->llqlease)
+			debugf("recvSetupResponse: requested lease %lu, granted lease %lu", q->ReqLease, llq->llqlease);
+	
+		// cache expiration in case we go to sleep before finishing setup
+		q->ReqLease = llq->llqlease;
+		q->expire = m->timenow + ((mDNSs32)llq->llqlease * mDNSPlatformOneSecond);
+	
+		// update state
+		q->state  = LLQ_SecondaryRequest;
+		q->id     = llq->id;
+		q->ntries = 0; // first attempt to send response
+		sendChallengeResponse(m, q, llq);
+		}
+	else if (q->state == LLQ_SecondaryRequest)
+		{
+		//LogInfo("Got LLQ_SecondaryRequest");
+
+		// Fix this immediately if not sooner.  Copy the id from the LLQOptData into our DNSQuestion struct.  This is only
+		// an issue for private LLQs, because we skip parts 2 and 3 of the handshake.  This is related to a bigger
+		// problem of the current implementation of TCP LLQ setup: we're not handling state transitions correctly
+		// if the server sends back SERVFULL or STATIC.
+		if (PrivateQuery(q))
+			{
+			LogInfo("Private LLQ_SecondaryRequest; copying id %08X%08X", llq->id.l[0], llq->id.l[1]);
+			q->id = llq->id;
+			}
+
+		if (llq->err) { LogMsg("ERROR: recvSetupResponse %##s (%s) code %d from server", q->qname.c, DNSTypeName(q->qtype), llq->err); StartLLQPolling(m,q); return; }
+		if (!mDNSSameOpaque64(&q->id, &llq->id))
+			{ LogMsg("recvSetupResponse - ID changed.  discarding"); return; } // this can happen rarely (on packet loss + reordering)
+		q->state         = LLQ_Established;
+		q->ntries        = 0;
+		SetLLQTimer(m, q, llq);
+#if APPLE_OSX_mDNSResponder
+		UpdateAutoTunnelDomainStatuses(m);
+#endif
+		}
+	}
+
+mDNSexport uDNS_LLQType uDNS_recvLLQResponse(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end,
+	const mDNSAddr *const srcaddr, const mDNSIPPort srcport, DNSQuestion **matchQuestion)
+	{
+	DNSQuestion pktQ, *q;
+	if (msg->h.numQuestions && getQuestion(msg, msg->data, end, 0, &pktQ))
+		{
+		const rdataOPT *opt = GetLLQOptData(m, msg, end);
+
+		for (q = m->Questions; q; q = q->next)
+			{
+			if (!mDNSOpaque16IsZero(q->TargetQID) && q->LongLived && q->qtype == pktQ.qtype && q->qnamehash == pktQ.qnamehash && SameDomainName(&q->qname, &pktQ.qname))
+				{
+				debugf("uDNS_recvLLQResponse found %##s (%s) %d %#a %#a %X %X %X %X %d",
+					q->qname.c, DNSTypeName(q->qtype), q->state, srcaddr, &q->servAddr,
+					opt ? opt->u.llq.id.l[0] : 0, opt ? opt->u.llq.id.l[1] : 0, q->id.l[0], q->id.l[1], opt ? opt->u.llq.llqOp : 0);
+				if (q->state == LLQ_Poll) debugf("uDNS_LLQ_Events: q->state == LLQ_Poll msg->h.id %d q->TargetQID %d", mDNSVal16(msg->h.id), mDNSVal16(q->TargetQID));
+				if (q->state == LLQ_Poll && mDNSSameOpaque16(msg->h.id, q->TargetQID))
+					{
+					m->rec.r.resrec.RecordType = 0;		// Clear RecordType to show we're not still using it
+					
+					// Don't reset the state to IntialRequest as we may write that to the dynamic store
+					// and PrefPane might wrongly think that we are "Starting" instead of "Polling". If
+					// we are in polling state because of NAT-PMP disabled or DoubleNAT, next LLQNATCallback
+					// would kick us back to LLQInitialRequest. So, resetting the state here may not be useful.
+					//
+					// If we have a good NAT (neither NAT-PMP disabled nor Double-NAT), then we should not be
+					// possibly in polling state. To be safe, we want to retry from the start in that case
+					// as there may not be another LLQNATCallback
+					//
+					// NOTE: We can be in polling state if we cannot resolve the SOA record i.e, servAddr is set to
+					// all ones. In that case, we would set it in LLQ_InitialRequest as it overrides the NAT-PMP or
+					// Double-NAT state.
+					if (!mDNSAddressIsOnes(&q->servAddr) && !mDNSIPPortIsZero(m->LLQNAT.ExternalPort) &&
+						!m->LLQNAT.Result)
+						{
+						debugf("uDNS_recvLLQResponse got poll response; moving to LLQ_InitialRequest for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+						q->state         = LLQ_InitialRequest;
+						}
+					q->servPort      = zeroIPPort;		// Clear servPort so that startLLQHandshake will retry the GetZoneData processing
+					q->ThisQInterval = LLQ_POLL_INTERVAL + mDNSRandom(LLQ_POLL_INTERVAL/10);	// Retry LLQ setup in approx 15 minutes
+					q->LastQTime     = m->timenow;
+					SetNextQueryTime(m, q);
+					*matchQuestion = q;
+					return uDNS_LLQ_Entire;		// uDNS_LLQ_Entire means flush stale records; assume a large effective TTL
+					}
+				// Note: In LLQ Event packets, the msg->h.id does not match our q->TargetQID, because in that case the msg->h.id nonce is selected by the server
+				else if (opt && q->state == LLQ_Established && opt->u.llq.llqOp == kLLQOp_Event && mDNSSameOpaque64(&opt->u.llq.id, &q->id))
+					{
+					mDNSu8 *ackEnd;
+					//debugf("Sending LLQ ack for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+					InitializeDNSMessage(&m->omsg.h, msg->h.id, ResponseFlags);
+					ackEnd = putLLQ(&m->omsg, m->omsg.data, q, &opt->u.llq);
+					if (ackEnd) mDNSSendDNSMessage(m, &m->omsg, ackEnd, mDNSInterface_Any, q->LocalSocket, srcaddr, srcport, mDNSNULL, mDNSNULL);
+					m->rec.r.resrec.RecordType = 0;		// Clear RecordType to show we're not still using it
+					debugf("uDNS_LLQ_Events: q->state == LLQ_Established msg->h.id %d q->TargetQID %d", mDNSVal16(msg->h.id), mDNSVal16(q->TargetQID));
+					*matchQuestion = q;
+					return uDNS_LLQ_Events;
+					}
+				if (opt && mDNSSameOpaque16(msg->h.id, q->TargetQID))
+					{
+					if (q->state == LLQ_Established && opt->u.llq.llqOp == kLLQOp_Refresh && mDNSSameOpaque64(&opt->u.llq.id, &q->id) && msg->h.numAdditionals && !msg->h.numAnswers)
+						{
+						if (opt->u.llq.err != LLQErr_NoError) LogMsg("recvRefreshReply: received error %d from server", opt->u.llq.err);
+						else
+							{
+							//LogInfo("Received refresh confirmation ntries %d for %##s (%s)", q->ntries, q->qname.c, DNSTypeName(q->qtype));
+							// If we're waiting to go to sleep, then this LLQ deletion may have been the thing
+							// we were waiting for, so schedule another check to see if we can sleep now.
+							if (opt->u.llq.llqlease == 0 && m->SleepLimit) m->NextScheduledSPRetry = m->timenow;
+							GrantCacheExtensions(m, q, opt->u.llq.llqlease);
+							SetLLQTimer(m, q, &opt->u.llq);
+							q->ntries = 0;
+							}
+						m->rec.r.resrec.RecordType = 0;		// Clear RecordType to show we're not still using it
+						*matchQuestion = q;
+						return uDNS_LLQ_Ignore;
+						}
+					if (q->state < LLQ_Established && mDNSSameAddress(srcaddr, &q->servAddr))
+						{
+						LLQ_State oldstate = q->state;
+						recvSetupResponse(m, msg->h.flags.b[1] & kDNSFlag1_RC_Mask, q, &opt->u.llq);
+						m->rec.r.resrec.RecordType = 0;		// Clear RecordType to show we're not still using it
+						// We have a protocol anomaly here in the LLQ definition.
+						// Both the challenge packet from the server and the ack+answers packet have opt->u.llq.llqOp == kLLQOp_Setup.
+						// However, we need to treat them differently:
+						// The challenge packet has no answers in it, and tells us nothing about whether our cache entries
+						// are still valid, so this packet should not cause us to do anything that messes with our cache.
+						// The ack+answers packet gives us the whole truth, so we should handle it by updating our cache
+						// to match the answers in the packet, and only the answers in the packet.
+						*matchQuestion = q;
+						return (oldstate == LLQ_SecondaryRequest ? uDNS_LLQ_Entire : uDNS_LLQ_Ignore);
+						}
+					}
+				}
+			}
+		m->rec.r.resrec.RecordType = 0;		// Clear RecordType to show we're not still using it
+		}
+	*matchQuestion = mDNSNULL;
+	return uDNS_LLQ_Not;
+	}
+
+// Stub definition of TCPSocket_struct so we can access flags field. (Rest of TCPSocket_struct is platform-dependent.)
+struct TCPSocket_struct { TCPSocketFlags flags; /* ... */ };
+
+// tcpCallback is called to handle events (e.g. connection opening and data reception) on TCP connections for
+// Private DNS operations -- private queries, private LLQs, private record updates and private service updates
+mDNSlocal void tcpCallback(TCPSocket *sock, void *context, mDNSBool ConnectionEstablished, mStatus err)
+	{
+	tcpInfo_t *tcpInfo = (tcpInfo_t *)context;
+	mDNSBool   closed  = mDNSfalse;
+	mDNS      *m       = tcpInfo->m;
+	DNSQuestion *const q = tcpInfo->question;
+	tcpInfo_t **backpointer =
+		q                 ? &q           ->tcp :
+		tcpInfo->rr       ? &tcpInfo->rr ->tcp : mDNSNULL;
+	if (backpointer && *backpointer != tcpInfo)
+		LogMsg("tcpCallback: %d backpointer %p incorrect tcpInfo %p question %p rr %p",
+			mDNSPlatformTCPGetFD(tcpInfo->sock), *backpointer, tcpInfo, q, tcpInfo->rr);
+
+	if (err) goto exit;
+
+	if (ConnectionEstablished)
+		{
+		mDNSu8    *end = ((mDNSu8*) &tcpInfo->request) + tcpInfo->requestLen;
+		DomainAuthInfo *AuthInfo;
+
+		// Defensive coding for <rdar://problem/5546824> Crash in mDNSResponder at GetAuthInfoForName_internal + 366
+		// Don't know yet what's causing this, but at least we can be cautious and try to avoid crashing if we find our pointers in an unexpected state
+		if (tcpInfo->rr && tcpInfo->rr->resrec.name != &tcpInfo->rr->namestorage)
+			LogMsg("tcpCallback: ERROR: tcpInfo->rr->resrec.name %p != &tcpInfo->rr->namestorage %p",
+				tcpInfo->rr->resrec.name, &tcpInfo->rr->namestorage);
+		if (tcpInfo->rr  && tcpInfo->rr->        resrec.name != &tcpInfo->rr->        namestorage) return;
+
+		AuthInfo =  tcpInfo->rr  ? GetAuthInfoForName(m, tcpInfo->rr->resrec.name)         : mDNSNULL;
+
+		// connection is established - send the message
+		if (q && q->LongLived && q->state == LLQ_Established)
+			{
+			// Lease renewal over TCP, resulting from opening a TCP connection in sendLLQRefresh
+			end = ((mDNSu8*) &tcpInfo->request) + tcpInfo->requestLen;
+			}
+		else if (q && q->LongLived && q->state != LLQ_Poll && !mDNSIPPortIsZero(m->LLQNAT.ExternalPort) && !mDNSIPPortIsZero(q->servPort))
+			{
+			// Notes:
+			// If we have a NAT port mapping, ExternalPort is the external port
+			// If we have a routable address so we don't need a port mapping, ExternalPort is the same as our own internal port
+			// If we need a NAT port mapping but can't get one, then ExternalPort is zero
+			LLQOptData llqData;			// set llq rdata
+			llqData.vers  = kLLQ_Vers;
+			llqData.llqOp = kLLQOp_Setup;
+			llqData.err   = GetLLQEventPort(m, &tcpInfo->Addr);	// We're using TCP; tell server what UDP port to send notifications to
+			LogInfo("tcpCallback: eventPort %d", llqData.err);
+			llqData.id    = zeroOpaque64;
+			llqData.llqlease = kLLQ_DefLease;
+			InitializeDNSMessage(&tcpInfo->request.h, q->TargetQID, uQueryFlags);
+			end = putLLQ(&tcpInfo->request, tcpInfo->request.data, q, &llqData);
+			if (!end) { LogMsg("ERROR: tcpCallback - putLLQ"); err = mStatus_UnknownErr; goto exit; }
+			AuthInfo = q->AuthInfo;		// Need to add TSIG to this message
+			q->ntries = 0; // Reset ntries so that tcp/tls connection failures don't affect sendChallengeResponse failures
+			}
+		else if (q)
+			{
+			// LLQ Polling mode or non-LLQ uDNS over TCP
+			InitializeDNSMessage(&tcpInfo->request.h, q->TargetQID, uQueryFlags);
+			end = putQuestion(&tcpInfo->request, tcpInfo->request.data, tcpInfo->request.data + AbsoluteMaxDNSMessageData, &q->qname, q->qtype, q->qclass);
+			AuthInfo = q->AuthInfo;		// Need to add TSIG to this message
+			}
+
+		err = mDNSSendDNSMessage(m, &tcpInfo->request, end, mDNSInterface_Any, mDNSNULL, &tcpInfo->Addr, tcpInfo->Port, sock, AuthInfo);
+		if (err) { debugf("ERROR: tcpCallback: mDNSSendDNSMessage - %d", err); err = mStatus_UnknownErr; goto exit; }
+
+		// Record time we sent this question
+		if (q)
+			{
+			mDNS_Lock(m);
+			q->LastQTime = m->timenow;
+			if (q->ThisQInterval < (256 * mDNSPlatformOneSecond))	// Now we have a TCP connection open, make sure we wait at least 256 seconds before retrying
+				q->ThisQInterval = (256 * mDNSPlatformOneSecond);
+			SetNextQueryTime(m, q);
+			mDNS_Unlock(m);
+			}
+		}
+	else
+		{
+		long n;
+		if (tcpInfo->nread < 2)			// First read the two-byte length preceeding the DNS message
+			{
+			mDNSu8 *lenptr = (mDNSu8 *)&tcpInfo->replylen;
+			n = mDNSPlatformReadTCP(sock, lenptr + tcpInfo->nread, 2 - tcpInfo->nread, &closed);
+			if (n < 0)
+				{
+				LogMsg("ERROR: tcpCallback - attempt to read message length failed (%d)", n);
+				err = mStatus_ConnFailed;
+				goto exit;
+				}
+			else if (closed)
+				{
+				// It's perfectly fine for this socket to close after the first reply. The server might
+				// be sending gratuitous replies using UDP and doesn't have a need to leave the TCP socket open.
+				// We'll only log this event if we've never received a reply before.
+				// BIND 9 appears to close an idle connection after 30 seconds.
+				if (tcpInfo->numReplies == 0)
+					{
+					LogMsg("ERROR: socket closed prematurely tcpInfo->nread = %d", tcpInfo->nread);
+					err = mStatus_ConnFailed;
+					goto exit;
+					}
+				else
+					{
+					// Note that we may not be doing the best thing if an error occurs after we've sent a second request
+					// over this tcp connection.  That is, we only track whether we've received at least one response
+					// which may have been to a previous request sent over this tcp connection.
+					if (backpointer) *backpointer = mDNSNULL; // Clear client backpointer FIRST so we don't risk double-disposing our tcpInfo_t 
+					DisposeTCPConn(tcpInfo);
+					return;
+					}
+				}
+
+			tcpInfo->nread += n;
+			if (tcpInfo->nread < 2) goto exit;
+
+			tcpInfo->replylen = (mDNSu16)((mDNSu16)lenptr[0] << 8 | lenptr[1]);
+			if (tcpInfo->replylen < sizeof(DNSMessageHeader))
+				{ LogMsg("ERROR: tcpCallback - length too short (%d bytes)", tcpInfo->replylen); err = mStatus_UnknownErr; goto exit; }
+
+			tcpInfo->reply = mDNSPlatformMemAllocate(tcpInfo->replylen);
+			if (!tcpInfo->reply) { LogMsg("ERROR: tcpCallback - malloc failed"); err = mStatus_NoMemoryErr; goto exit; }
+			}
+
+		n = mDNSPlatformReadTCP(sock, ((char *)tcpInfo->reply) + (tcpInfo->nread - 2), tcpInfo->replylen - (tcpInfo->nread - 2), &closed);
+
+		if (n < 0)
+			{
+			LogMsg("ERROR: tcpCallback - read returned %d", n);
+			err = mStatus_ConnFailed;
+			goto exit;
+			}
+		else if (closed)
+			{
+			if (tcpInfo->numReplies == 0)
+				{
+				LogMsg("ERROR: socket closed prematurely tcpInfo->nread = %d", tcpInfo->nread);
+				err = mStatus_ConnFailed;
+				goto exit;
+				}
+			else
+				{
+				// Note that we may not be doing the best thing if an error occurs after we've sent a second request
+				// over this tcp connection.  That is, we only track whether we've received at least one response
+				// which may have been to a previous request sent over this tcp connection.
+				if (backpointer) *backpointer = mDNSNULL; // Clear client backpointer FIRST so we don't risk double-disposing our tcpInfo_t 
+				DisposeTCPConn(tcpInfo);
+				return;
+				}
+			}
+
+		tcpInfo->nread += n;
+
+		if ((tcpInfo->nread - 2) == tcpInfo->replylen)
+			{
+			mDNSBool tls;
+			DNSMessage *reply = tcpInfo->reply;
+			mDNSu8     *end   = (mDNSu8 *)tcpInfo->reply + tcpInfo->replylen;
+			mDNSAddr    Addr  = tcpInfo->Addr;
+			mDNSIPPort  Port  = tcpInfo->Port;
+			mDNSIPPort srcPort = zeroIPPort;
+			tcpInfo->numReplies++;
+			tcpInfo->reply    = mDNSNULL;	// Detach reply buffer from tcpInfo_t, to make sure client callback can't cause it to be disposed
+			tcpInfo->nread    = 0;
+			tcpInfo->replylen = 0;
+			
+			// If we're going to dispose this connection, do it FIRST, before calling client callback
+			// Note: Sleep code depends on us clearing *backpointer here -- it uses the clearing of rr->tcp
+			// as the signal that the DNS deregistration operation with the server has completed, and the machine may now sleep
+			// If we clear the tcp pointer in the question, mDNSCoreReceiveResponse cannot find a matching question. Hence
+			// we store the minimal information i.e., the source port of the connection in the question itself.
+			// Dereference sock before it is disposed in DisposeTCPConn below.
+			
+			if (sock->flags & kTCPSocketFlags_UseTLS) tls = mDNStrue;
+			else tls = mDNSfalse;
+
+			if (q && q->tcp) {srcPort = q->tcp->SrcPort; q->tcpSrcPort = srcPort;}
+			
+			if (backpointer)
+				if (!q || !q->LongLived || m->SleepState)
+					{ *backpointer = mDNSNULL; DisposeTCPConn(tcpInfo); }
+			
+			mDNSCoreReceive(m, reply, end, &Addr, Port, tls ? (mDNSAddr *)1 : mDNSNULL, srcPort, 0);
+			// USE CAUTION HERE: Invoking mDNSCoreReceive may have caused the environment to change, including canceling this operation itself
+			
+			mDNSPlatformMemFree(reply);
+			return;
+			}
+		}
+
+exit:
+
+	if (err)
+		{
+		// Clear client backpointer FIRST -- that way if one of the callbacks cancels its operation
+		// we won't end up double-disposing our tcpInfo_t
+		if (backpointer) *backpointer = mDNSNULL;
+
+		mDNS_Lock(m);		// Need to grab the lock to get m->timenow
+
+		if (q)
+			{
+			if (q->ThisQInterval == 0)
+				{
+				// We get here when we fail to establish a new TCP/TLS connection that would have been used for a new LLQ request or an LLQ renewal.
+				// Note that ThisQInterval is also zero when sendChallengeResponse resends the LLQ request on an extant TCP/TLS connection.
+				q->LastQTime = m->timenow;
+				if (q->LongLived)
+					{
+					// We didn't get the chance to send our request packet before the TCP/TLS connection failed.
+					// We want to retry quickly, but want to back off exponentially in case the server is having issues.
+					// Since ThisQInterval was 0, we can't just multiply by QuestionIntervalStep, we must track the number
+					// of TCP/TLS connection failures using ntries.
+					mDNSu32 count = q->ntries + 1; // want to wait at least 1 second before retrying
+
+					q->ThisQInterval = InitialQuestionInterval;
+
+					for (;count;count--)
+						q->ThisQInterval *= QuestionIntervalStep;
+
+					if (q->ThisQInterval > LLQ_POLL_INTERVAL)
+						q->ThisQInterval = LLQ_POLL_INTERVAL;
+					else
+						q->ntries++;
+						
+					LogMsg("tcpCallback: stream connection for LLQ %##s (%s) failed %d times, retrying in %d ms", q->qname.c, DNSTypeName(q->qtype), q->ntries, q->ThisQInterval);
+					}
+				else
+					{
+					q->ThisQInterval = MAX_UCAST_POLL_INTERVAL;
+					LogMsg("tcpCallback: stream connection for %##s (%s) failed, retrying in %d ms", q->qname.c, DNSTypeName(q->qtype), q->ThisQInterval);
+					}
+				SetNextQueryTime(m, q);
+				}
+			else if (NextQSendTime(q) - m->timenow > (q->LongLived ? LLQ_POLL_INTERVAL : MAX_UCAST_POLL_INTERVAL))
+				{
+				// If we get an error and our next scheduled query for this question is more than the max interval from now,
+				// reset the next query to ensure we wait no longer the maximum interval from now before trying again.
+				q->LastQTime     = m->timenow;
+				q->ThisQInterval = q->LongLived ? LLQ_POLL_INTERVAL : MAX_UCAST_POLL_INTERVAL;
+				SetNextQueryTime(m, q);
+				LogMsg("tcpCallback: stream connection for %##s (%s) failed, retrying in %d ms", q->qname.c, DNSTypeName(q->qtype), q->ThisQInterval);
+				}
+			
+			// We're about to dispose of the TCP connection, so we must reset the state to retry over TCP/TLS
+			// because sendChallengeResponse will send the query via UDP if we don't have a tcp pointer.
+			// Resetting to LLQ_InitialRequest will cause uDNS_CheckCurrentQuestion to call startLLQHandshake, which
+			// will attempt to establish a new tcp connection.
+			if (q->LongLived && q->state == LLQ_SecondaryRequest)
+				q->state = LLQ_InitialRequest;
+			
+			// ConnFailed may happen if the server sends a TCP reset or TLS fails, in which case we want to retry establishing the LLQ
+			// quickly rather than switching to polling mode.  This case is handled by the above code to set q->ThisQInterval just above.
+			// If the error isn't ConnFailed, then the LLQ is in bad shape, so we switch to polling mode.
+			if (err != mStatus_ConnFailed)
+				{
+				if (q->LongLived && q->state != LLQ_Poll) StartLLQPolling(m, q);
+				}
+			}
+
+		mDNS_Unlock(m);
+
+		DisposeTCPConn(tcpInfo);
+		}
+	}
+
+mDNSlocal tcpInfo_t *MakeTCPConn(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end,
+	TCPSocketFlags flags, const mDNSAddr *const Addr, const mDNSIPPort Port, domainname *hostname,
+	DNSQuestion *const question, AuthRecord *const rr)
+	{
+	mStatus err;
+	mDNSIPPort srcport = zeroIPPort;
+	tcpInfo_t *info;
+
+	if ((flags & kTCPSocketFlags_UseTLS) && (!hostname || !hostname->c[0]))
+		{ LogMsg("MakeTCPConn: TLS connection being setup with NULL hostname"); return mDNSNULL; }
+
+	info = (tcpInfo_t *)mDNSPlatformMemAllocate(sizeof(tcpInfo_t));
+	if (!info) { LogMsg("ERROR: MakeTCP - memallocate failed"); return(mDNSNULL); }
+	mDNSPlatformMemZero(info, sizeof(tcpInfo_t));
+
+	info->m          = m;
+	info->sock       = mDNSPlatformTCPSocket(m, flags, &srcport);
+	info->requestLen = 0;
+	info->question   = question;
+	info->rr         = rr;
+	info->Addr       = *Addr;
+	info->Port       = Port;
+	info->reply      = mDNSNULL;
+	info->replylen   = 0;
+	info->nread      = 0;
+	info->numReplies = 0;
+	info->SrcPort = srcport;
+
+	if (msg)
+		{
+		info->requestLen = (int) (end - ((mDNSu8*)msg));
+		mDNSPlatformMemCopy(&info->request, msg, info->requestLen);
+		}
+
+	if (!info->sock) { LogMsg("MakeTCPConn: unable to create TCP socket"); mDNSPlatformMemFree(info); return(mDNSNULL); }
+	err = mDNSPlatformTCPConnect(info->sock, Addr, Port, hostname, (question ? question->InterfaceID : mDNSNULL), tcpCallback, info);
+
+	// Probably suboptimal here.
+	// Instead of returning mDNSNULL here on failure, we should probably invoke the callback with an error code.
+	// That way clients can put all the error handling and retry/recovery code in one place,
+	// instead of having to handle immediate errors in one place and async errors in another.
+	// Also: "err == mStatus_ConnEstablished" probably never happens.
+
+	// Don't need to log "connection failed" in customer builds -- it happens quite often during sleep, wake, configuration changes, etc.
+	if      (err == mStatus_ConnEstablished) { tcpCallback(info->sock, info, mDNStrue, mStatus_NoError); }
+	else if (err != mStatus_ConnPending    ) { LogInfo("MakeTCPConn: connection failed"); DisposeTCPConn(info); return(mDNSNULL); }
+	return(info);
+	}
+
+mDNSexport void DisposeTCPConn(struct tcpInfo_t *tcp)
+	{
+	mDNSPlatformTCPCloseConnection(tcp->sock);
+	if (tcp->reply) mDNSPlatformMemFree(tcp->reply);
+	mDNSPlatformMemFree(tcp);
+	}
+
+// Lock must be held
+mDNSexport void startLLQHandshake(mDNS *m, DNSQuestion *q)
+	{
+	if (mDNSIPv4AddressIsOnes(m->LLQNAT.ExternalAddress))
+		{
+		LogInfo("startLLQHandshake: waiting for NAT status for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+		q->ThisQInterval = LLQ_POLL_INTERVAL + mDNSRandom(LLQ_POLL_INTERVAL/10);	// Retry in approx 15 minutes
+		q->LastQTime = m->timenow;
+		SetNextQueryTime(m, q);
+		return;
+		}
+
+	// Either we don't have NAT-PMP support (ExternalPort is zero) or behind a Double NAT that may or
+	// may not have NAT-PMP support (NATResult is non-zero)
+	if (mDNSIPPortIsZero(m->LLQNAT.ExternalPort) || m->LLQNAT.Result)
+		{
+		LogInfo("startLLQHandshake: Cannot receive inbound packets; will poll for %##s (%s) External Port %d, NAT Result %d",
+			q->qname.c, DNSTypeName(q->qtype), mDNSVal16(m->LLQNAT.ExternalPort), m->LLQNAT.Result);
+		StartLLQPolling(m, q);
+		return;
+		}
+
+	if (mDNSIPPortIsZero(q->servPort))
+		{
+		debugf("startLLQHandshake: StartGetZoneData for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+		q->ThisQInterval = LLQ_POLL_INTERVAL + mDNSRandom(LLQ_POLL_INTERVAL/10);	// Retry in approx 15 minutes
+		q->LastQTime     = m->timenow;
+		SetNextQueryTime(m, q);
+		q->servAddr = zeroAddr;
+		// We know q->servPort is zero because of check above
+		if (q->nta) CancelGetZoneData(m, q->nta);
+		q->nta = StartGetZoneData(m, &q->qname, ZoneServiceLLQ, LLQGotZoneData, q);
+		return;
+		}
+
+	if (PrivateQuery(q))
+		{
+		if (q->tcp) LogInfo("startLLQHandshake: Disposing existing TCP connection for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+		if (q->tcp) { DisposeTCPConn(q->tcp); q->tcp = mDNSNULL; }
+		if (!q->nta)
+			{
+			// Normally we lookup the zone data and then call this function. And we never free the zone data
+			// for "PrivateQuery". But sometimes this can happen due to some race conditions. When we
+			// switch networks, we might end up "Polling" the network e.g., we are behind a Double NAT.
+			// When we poll, we free the zone information as we send the query to the server (See
+			// PrivateQueryGotZoneData). The NAT callback (LLQNATCallback) may happen soon after that. If we
+			// are still behind Double NAT, we would have returned early in this function. But we could
+			// have switched to a network with no NATs and we should get the zone data again.
+			LogInfo("startLLQHandshake: nta is NULL for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+			q->nta = StartGetZoneData(m, &q->qname, ZoneServiceLLQ, LLQGotZoneData, q);
+			return;
+			}
+		else if (!q->nta->Host.c[0])
+			{
+			// This should not happen. If it happens, we print a log and MakeTCPConn will fail if it can't find a hostname
+			LogMsg("startLLQHandshake: ERROR!!: nta non NULL for %##s (%s) but HostName %d NULL, LongLived %d", q->qname.c, DNSTypeName(q->qtype), q->nta->Host.c[0], q->LongLived);
+			}
+		q->tcp = MakeTCPConn(m, mDNSNULL, mDNSNULL, kTCPSocketFlags_UseTLS, &q->servAddr, q->servPort, &q->nta->Host, q, mDNSNULL);
+		if (!q->tcp)
+			q->ThisQInterval = mDNSPlatformOneSecond * 5;	// If TCP failed (transient networking glitch) try again in five seconds
+		else
+			{
+			q->state         = LLQ_SecondaryRequest;		// Right now, for private DNS, we skip the four-way LLQ handshake
+			q->ReqLease      = kLLQ_DefLease;
+			q->ThisQInterval = 0;
+			}
+		q->LastQTime     = m->timenow;
+		SetNextQueryTime(m, q);
+		}
+	else
+		{
+		debugf("startLLQHandshake: m->AdvertisedV4 %#a%s Server %#a:%d%s %##s (%s)",
+			&m->AdvertisedV4,                     mDNSv4AddrIsRFC1918(&m->AdvertisedV4.ip.v4) ? " (RFC 1918)" : "",
+			&q->servAddr, mDNSVal16(q->servPort), mDNSAddrIsRFC1918(&q->servAddr)             ? " (RFC 1918)" : "",
+			q->qname.c, DNSTypeName(q->qtype));
+
+		if (q->ntries++ >= kLLQ_MAX_TRIES)
+			{
+			LogMsg("startLLQHandshake: %d failed attempts for LLQ %##s Polling.", kLLQ_MAX_TRIES, q->qname.c);
+			StartLLQPolling(m, q);
+			}
+		else
+			{
+			mDNSu8 *end;
+			LLQOptData llqData;
+
+			// set llq rdata
+			llqData.vers  = kLLQ_Vers;
+			llqData.llqOp = kLLQOp_Setup;
+			llqData.err   = LLQErr_NoError;	// Don't need to tell server UDP notification port when sending over UDP
+			llqData.id    = zeroOpaque64;
+			llqData.llqlease = kLLQ_DefLease;
+	
+			InitializeDNSMessage(&m->omsg.h, q->TargetQID, uQueryFlags);
+			end = putLLQ(&m->omsg, m->omsg.data, q, &llqData);
+			if (!end) { LogMsg("ERROR: startLLQHandshake - putLLQ"); StartLLQPolling(m,q); return; }
+	
+			mDNSSendDNSMessage(m, &m->omsg, end, mDNSInterface_Any, q->LocalSocket, &q->servAddr, q->servPort, mDNSNULL, mDNSNULL);
+	
+			// update question state
+			q->state         = LLQ_InitialRequest;
+			q->ReqLease      = kLLQ_DefLease;
+			q->ThisQInterval = (kLLQ_INIT_RESEND * mDNSPlatformOneSecond);
+			q->LastQTime     = m->timenow;
+			SetNextQueryTime(m, q);
+			}
+		}
+	}
+
+// forward declaration so GetServiceTarget can do reverse lookup if needed
+mDNSlocal void GetStaticHostname(mDNS *m);
+
+mDNSexport const domainname *GetServiceTarget(mDNS *m, AuthRecord *const rr)
+	{
+	debugf("GetServiceTarget %##s", rr->resrec.name->c);
+
+	if (!rr->AutoTarget)		// If not automatically tracking this host's current name, just return the existing target
+		return(&rr->resrec.rdata->u.srv.target);
+	else
+		{
+#if APPLE_OSX_mDNSResponder
+		DomainAuthInfo *AuthInfo = GetAuthInfoForName_internal(m, rr->resrec.name);
+		if (AuthInfo && AuthInfo->AutoTunnel)
+			{
+			// If this AutoTunnel is not yet active, start it now (which entails activating its NAT Traversal request,
+			// which will subsequently advertise the appropriate records when the NAT Traversal returns a result)
+			if (!AuthInfo->AutoTunnelNAT.clientContext && m->AutoTunnelHostAddr.b[0])
+				{
+				LogInfo("GetServiceTarget: Calling SetupLocalAutoTunnelInterface_internal");
+				SetupLocalAutoTunnelInterface_internal(m, mDNStrue);
+				}
+			if (AuthInfo->AutoTunnelHostRecord.namestorage.c[0] == 0) return(mDNSNULL);
+			debugf("GetServiceTarget: Returning %##s", AuthInfo->AutoTunnelHostRecord.namestorage.c);
+			return(&AuthInfo->AutoTunnelHostRecord.namestorage);
+			}
+		else
+#endif // APPLE_OSX_mDNSResponder
+			{
+			const int srvcount = CountLabels(rr->resrec.name);
+			HostnameInfo *besthi = mDNSNULL, *hi;
+			int best = 0;
+			for (hi = m->Hostnames; hi; hi = hi->next)
+				if (hi->arv4.state == regState_Registered || hi->arv4.state == regState_Refresh ||
+					hi->arv6.state == regState_Registered || hi->arv6.state == regState_Refresh)
+					{
+					int x, hostcount = CountLabels(&hi->fqdn);
+					for (x = hostcount < srvcount ? hostcount : srvcount; x > 0 && x > best; x--)
+						if (SameDomainName(SkipLeadingLabels(rr->resrec.name, srvcount - x), SkipLeadingLabels(&hi->fqdn, hostcount - x)))
+							{ best = x; besthi = hi; }
+					}
+	
+			if (besthi) return(&besthi->fqdn);
+			}
+		if (m->StaticHostname.c[0]) return(&m->StaticHostname);
+		else GetStaticHostname(m); // asynchronously do reverse lookup for primary IPv4 address
+		LogInfo("GetServiceTarget: Returning NULL for %s", ARDisplayString(m, rr));
+		return(mDNSNULL);
+		}
+	}
+
+mDNSlocal const domainname *PUBLIC_UPDATE_SERVICE_TYPE  = (const domainname*)"\x0B_dns-update"     "\x04_udp";
+mDNSlocal const domainname *PUBLIC_LLQ_SERVICE_TYPE     = (const domainname*)"\x08_dns-llq"        "\x04_udp";
+
+mDNSlocal const domainname *PRIVATE_UPDATE_SERVICE_TYPE = (const domainname*)"\x0F_dns-update-tls" "\x04_tcp";
+mDNSlocal const domainname *PRIVATE_QUERY_SERVICE_TYPE  = (const domainname*)"\x0E_dns-query-tls"  "\x04_tcp";
+mDNSlocal const domainname *PRIVATE_LLQ_SERVICE_TYPE    = (const domainname*)"\x0C_dns-llq-tls"    "\x04_tcp";
+
+#define ZoneDataSRV(X) (\
+	(X)->ZoneService == ZoneServiceUpdate ? ((X)->ZonePrivate ? PRIVATE_UPDATE_SERVICE_TYPE : PUBLIC_UPDATE_SERVICE_TYPE) : \
+	(X)->ZoneService == ZoneServiceQuery  ? ((X)->ZonePrivate ? PRIVATE_QUERY_SERVICE_TYPE  : (const domainname*)""     ) : \
+	(X)->ZoneService == ZoneServiceLLQ    ? ((X)->ZonePrivate ? PRIVATE_LLQ_SERVICE_TYPE    : PUBLIC_LLQ_SERVICE_TYPE   ) : (const domainname*)"")
+
+// Forward reference: GetZoneData_StartQuery references GetZoneData_QuestionCallback, and
+// GetZoneData_QuestionCallback calls GetZoneData_StartQuery
+mDNSlocal mStatus GetZoneData_StartQuery(mDNS *const m, ZoneData *zd, mDNSu16 qtype);
+
+// GetZoneData_QuestionCallback is called from normal client callback context (core API calls allowed)
+mDNSlocal void GetZoneData_QuestionCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
+	{
+	ZoneData *zd = (ZoneData*)question->QuestionContext;
+
+	debugf("GetZoneData_QuestionCallback: %s %s", AddRecord ? "Add" : "Rmv", RRDisplayString(m, answer));
+
+	if (!AddRecord) return;												// Don't care about REMOVE events
+	if (AddRecord == QC_addnocache && answer->rdlength == 0) return;	// Don't care about transient failure indications
+	if (answer->rrtype != question->qtype) return;						// Don't care about CNAMEs
+
+	if (answer->rrtype == kDNSType_SOA)
+		{
+		debugf("GetZoneData GOT SOA %s", RRDisplayString(m, answer));
+		mDNS_StopQuery(m, question);
+		if (question->ThisQInterval != -1)
+			LogMsg("GetZoneData_QuestionCallback: Question %##s (%s) ThisQInterval %d not -1", question->qname.c, DNSTypeName(question->qtype), question->ThisQInterval);
+		if (answer->rdlength)
+			{
+			AssignDomainName(&zd->ZoneName, answer->name);
+			zd->ZoneClass = answer->rrclass;
+			AssignDomainName(&zd->question.qname, &zd->ZoneName);
+			GetZoneData_StartQuery(m, zd, kDNSType_SRV);
+			}
+		else if (zd->CurrentSOA->c[0])
+			{
+			DomainAuthInfo *AuthInfo = GetAuthInfoForName(m, zd->CurrentSOA);
+			if (AuthInfo && AuthInfo->AutoTunnel)
+				{
+				// To keep the load on the server down, we don't chop down on
+				// SOA lookups for AutoTunnels
+				LogInfo("GetZoneData_QuestionCallback: not chopping labels for %##s", zd->CurrentSOA->c);
+				zd->ZoneDataCallback(m, mStatus_NoSuchNameErr, zd);
+				}
+			else 
+				{
+				zd->CurrentSOA = (domainname *)(zd->CurrentSOA->c + zd->CurrentSOA->c[0]+1);
+				AssignDomainName(&zd->question.qname, zd->CurrentSOA);
+				GetZoneData_StartQuery(m, zd, kDNSType_SOA);
+				}
+			}
+		else
+			{
+			LogInfo("GetZoneData recursed to root label of %##s without finding SOA", zd->ChildName.c);
+			zd->ZoneDataCallback(m, mStatus_NoSuchNameErr, zd);
+			}
+		}
+	else if (answer->rrtype == kDNSType_SRV)
+		{
+		debugf("GetZoneData GOT SRV %s", RRDisplayString(m, answer));
+		mDNS_StopQuery(m, question);
+		if (question->ThisQInterval != -1)
+			LogMsg("GetZoneData_QuestionCallback: Question %##s (%s) ThisQInterval %d not -1", question->qname.c, DNSTypeName(question->qtype), question->ThisQInterval);
+// Right now we don't want to fail back to non-encrypted operations
+// If the AuthInfo has the AutoTunnel field set, then we want private or nothing
+// <rdar://problem/5687667> BTMM: Don't fallback to unencrypted operations when SRV lookup fails
+#if 0
+		if (!answer->rdlength && zd->ZonePrivate && zd->ZoneService != ZoneServiceQuery)
+			{
+			zd->ZonePrivate = mDNSfalse;	// Causes ZoneDataSRV() to yield a different SRV name when building the query
+			GetZoneData_StartQuery(m, zd, kDNSType_SRV);		// Try again, non-private this time
+			}
+		else
+#endif
+			{
+			if (answer->rdlength)
+				{
+				AssignDomainName(&zd->Host, &answer->rdata->u.srv.target);
+				zd->Port = answer->rdata->u.srv.port;
+				AssignDomainName(&zd->question.qname, &zd->Host);
+				GetZoneData_StartQuery(m, zd, kDNSType_A);
+				}
+			else
+				{
+				zd->ZonePrivate = mDNSfalse;
+				zd->Host.c[0] = 0;
+				zd->Port = zeroIPPort;
+				zd->Addr = zeroAddr;
+				zd->ZoneDataCallback(m, mStatus_NoError, zd);
+				}
+			}
+		}
+	else if (answer->rrtype == kDNSType_A)
+		{
+		debugf("GetZoneData GOT A %s", RRDisplayString(m, answer));
+		mDNS_StopQuery(m, question);
+		if (question->ThisQInterval != -1)
+			LogMsg("GetZoneData_QuestionCallback: Question %##s (%s) ThisQInterval %d not -1", question->qname.c, DNSTypeName(question->qtype), question->ThisQInterval);
+		zd->Addr.type  = mDNSAddrType_IPv4;
+		zd->Addr.ip.v4 = (answer->rdlength == 4) ? answer->rdata->u.ipv4 : zerov4Addr;
+		// In order to simulate firewalls blocking our outgoing TCP connections, returning immediate ICMP errors or TCP resets,
+		// the code below will make us try to connect to loopback, resulting in an immediate "port unreachable" failure.
+		// This helps us test to make sure we handle this case gracefully
+		// <rdar://problem/5607082> BTMM: mDNSResponder taking 100 percent CPU after upgrading to 10.5.1
+#if 0
+		zd->Addr.ip.v4.b[0] = 127;
+		zd->Addr.ip.v4.b[1] = 0;
+		zd->Addr.ip.v4.b[2] = 0;
+		zd->Addr.ip.v4.b[3] = 1;
+#endif
+		// The caller needs to free the memory when done with zone data
+		zd->ZoneDataCallback(m, mStatus_NoError, zd);
+		}
+	}
+
+// GetZoneData_StartQuery is called from normal client context (lock not held, or client callback)
+mDNSlocal mStatus GetZoneData_StartQuery(mDNS *const m, ZoneData *zd, mDNSu16 qtype)
+	{
+	if (qtype == kDNSType_SRV)
+		{
+		AssignDomainName(&zd->question.qname, ZoneDataSRV(zd));
+		AppendDomainName(&zd->question.qname, &zd->ZoneName);
+		debugf("lookupDNSPort %##s", zd->question.qname.c);
+		}
+
+	// CancelGetZoneData can get called at any time. We should stop the question if it has not been
+	// stopped already. A value of -1 for ThisQInterval indicates that the question is not active
+	// yet.
+	zd->question.ThisQInterval       = -1;
+	zd->question.InterfaceID         = mDNSInterface_Any;
+	zd->question.Target              = zeroAddr;
+	//zd->question.qname.c[0]        = 0;			// Already set
+	zd->question.qtype               = qtype;
+	zd->question.qclass              = kDNSClass_IN;
+	zd->question.LongLived           = mDNSfalse;
+	zd->question.ExpectUnique        = mDNStrue;
+	zd->question.ForceMCast          = mDNSfalse;
+	zd->question.ReturnIntermed      = mDNStrue;
+	zd->question.SuppressUnusable    = mDNSfalse;
+	zd->question.SearchListIndex     = 0;
+	zd->question.AppendSearchDomains = 0;
+	zd->question.RetryWithSearchDomains = mDNSfalse;
+	zd->question.TimeoutQuestion     = 0;
+	zd->question.WakeOnResolve       = 0;
+	zd->question.qnameOrig           = mDNSNULL;
+	zd->question.QuestionCallback    = GetZoneData_QuestionCallback;
+	zd->question.QuestionContext     = zd;
+
+	//LogMsg("GetZoneData_StartQuery %##s (%s) %p", zd->question.qname.c, DNSTypeName(zd->question.qtype), zd->question.Private);
+	return(mDNS_StartQuery(m, &zd->question));
+	}
+
+// StartGetZoneData is an internal routine (i.e. must be called with the lock already held)
+mDNSexport ZoneData *StartGetZoneData(mDNS *const m, const domainname *const name, const ZoneService target, ZoneDataCallback callback, void *ZoneDataContext)
+	{
+	DomainAuthInfo *AuthInfo = GetAuthInfoForName_internal(m, name);
+	int initialskip = (AuthInfo && AuthInfo->AutoTunnel) ? DomainNameLength(name) - DomainNameLength(&AuthInfo->domain) : 0;
+	ZoneData *zd = (ZoneData*)mDNSPlatformMemAllocate(sizeof(ZoneData));
+	if (!zd) { LogMsg("ERROR: StartGetZoneData - mDNSPlatformMemAllocate failed"); return mDNSNULL; }
+	mDNSPlatformMemZero(zd, sizeof(ZoneData));
+	AssignDomainName(&zd->ChildName, name);
+	zd->ZoneService      = target;
+	zd->CurrentSOA       = (domainname *)(&zd->ChildName.c[initialskip]);
+	zd->ZoneName.c[0]    = 0;
+	zd->ZoneClass        = 0;
+	zd->Host.c[0]        = 0;
+	zd->Port             = zeroIPPort;
+	zd->Addr             = zeroAddr;
+	zd->ZonePrivate      = AuthInfo && AuthInfo->AutoTunnel ? mDNStrue : mDNSfalse;
+	zd->ZoneDataCallback = callback;
+	zd->ZoneDataContext  = ZoneDataContext;
+
+	zd->question.QuestionContext = zd;
+
+	mDNS_DropLockBeforeCallback();		// GetZoneData_StartQuery expects to be called from a normal callback, so we emulate that here
+	if (AuthInfo && AuthInfo->AutoTunnel && !mDNSIPPortIsZero(AuthInfo->port))
+		{
+		LogInfo("StartGetZoneData: Bypassing SOA, SRV query for %##s", AuthInfo->domain.c);
+		// We bypass SOA and SRV queries if we know the hostname and port already from the configuration.
+		// Today this is only true for AutoTunnel. As we bypass, we need to infer a few things:
+		//
+		// 1. Zone name is the same as the AuthInfo domain 
+		// 2. ZoneClass is kDNSClass_IN which should be a safe assumption
+		//
+		// If we want to make this bypass mechanism work for non-AutoTunnels also, (1) has to hold
+		// good. Otherwise, it has to be configured also.
+
+		AssignDomainName(&zd->ZoneName, &AuthInfo->domain);
+		zd->ZoneClass = kDNSClass_IN;
+		AssignDomainName(&zd->Host, &AuthInfo->hostname);
+		zd->Port = AuthInfo->port;
+		AssignDomainName(&zd->question.qname, &zd->Host);
+		GetZoneData_StartQuery(m, zd, kDNSType_A);
+		}
+	else
+		{
+		if (AuthInfo && AuthInfo->AutoTunnel) LogInfo("StartGetZoneData: Not Bypassing SOA, SRV query for %##s", AuthInfo->domain.c);
+		AssignDomainName(&zd->question.qname, zd->CurrentSOA);
+		GetZoneData_StartQuery(m, zd, kDNSType_SOA);
+		}
+	mDNS_ReclaimLockAfterCallback();
+
+	return zd;
+	}
+
+// Returns if the question is a GetZoneData question. These questions are special in
+// that they are created internally while resolving a private query or LLQs.
+mDNSexport mDNSBool IsGetZoneDataQuestion(DNSQuestion *q)
+	{
+	if (q->QuestionCallback == GetZoneData_QuestionCallback) return(mDNStrue);
+	else return(mDNSfalse);
+	}
+
+// GetZoneData queries are a special case -- even if we have a key for them, we don't do them privately,
+// because that would result in an infinite loop (i.e. to do a private query we first need to get
+// the _dns-query-tls SRV record for the zone, and we can't do *that* privately because to do so
+// we'd need to already know the _dns-query-tls SRV record.
+// Also, as a general rule, we never do SOA queries privately
+mDNSexport DomainAuthInfo *GetAuthInfoForQuestion(mDNS *m, const DNSQuestion *const q)	// Must be called with lock held
+	{
+	if (q->QuestionCallback == GetZoneData_QuestionCallback) return(mDNSNULL);
+	if (q->qtype            == kDNSType_SOA                ) return(mDNSNULL);
+	return(GetAuthInfoForName_internal(m, &q->qname));
+	}
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark - host name and interface management
+#endif
+
+mDNSlocal void SendRecordRegistration(mDNS *const m, AuthRecord *rr);
+mDNSlocal void SendRecordDeregistration(mDNS *m, AuthRecord *rr);
+mDNSlocal mDNSBool IsRecordMergeable(mDNS *const m, AuthRecord *rr, mDNSs32 time);
+
+// When this function is called, service record is already deregistered. We just
+// have to deregister the PTR and TXT records.
+mDNSlocal void UpdateAllServiceRecords(mDNS *const m, AuthRecord *rr, mDNSBool reg)
+	{
+	AuthRecord *r, *srvRR;
+
+	if (rr->resrec.rrtype != kDNSType_SRV) { LogMsg("UpdateAllServiceRecords:ERROR!! ResourceRecord not a service record %s", ARDisplayString(m, rr)); return; }
+
+	if (reg && rr->state == regState_NoTarget) { LogMsg("UpdateAllServiceRecords:ERROR!! SRV record %s in noTarget state during registration", ARDisplayString(m, rr)); return; }
+
+	LogInfo("UpdateAllServiceRecords: ResourceRecord %s", ARDisplayString(m, rr));
+
+	for (r = m->ResourceRecords; r; r=r->next)
+		{
+		if (!AuthRecord_uDNS(r)) continue;
+		srvRR = mDNSNULL;
+		if (r->resrec.rrtype == kDNSType_PTR)
+			srvRR = r->Additional1;
+		else if (r->resrec.rrtype == kDNSType_TXT)
+			srvRR = r->DependentOn;
+		if (srvRR && srvRR->resrec.rrtype != kDNSType_SRV)
+			LogMsg("UpdateAllServiceRecords: ERROR!! Resource record %s wrong, expecting SRV type", ARDisplayString(m, srvRR));
+		if (srvRR == rr)
+			{
+			if (!reg)
+				{
+				LogInfo("UpdateAllServiceRecords: deregistering %s", ARDisplayString(m, r));
+				r->SRVChanged = mDNStrue;
+				r->ThisAPInterval = INIT_RECORD_REG_INTERVAL;
+				r->LastAPTime = m->timenow - INIT_RECORD_REG_INTERVAL;
+				r->state = regState_DeregPending;
+				}
+			else 
+				{
+				// Clearing SRVchanged is a safety measure. If our pevious dereg never
+				// came back and we had a target change, we are starting fresh
+				r->SRVChanged = mDNSfalse;
+				// if it is already registered or in the process of registering, then don't
+				// bother re-registering. This happens today for non-BTMM domains where the
+				// TXT and PTR get registered before SRV records because of the delay in
+				// getting the port mapping. There is no point in re-registering the TXT
+				// and PTR records. 
+				if ((r->state == regState_Registered) ||
+				    (r->state == regState_Pending && r->nta && !mDNSIPv4AddressIsZero(r->nta->Addr.ip.v4)))
+					LogInfo("UpdateAllServiceRecords: not registering %s, state %d", ARDisplayString(m, r), r->state);
+				else
+					{
+					LogInfo("UpdateAllServiceRecords: registering %s, state %d", ARDisplayString(m, r), r->state);
+					ActivateUnicastRegistration(m, r);
+					}
+				}
+			}
+		}
+	}
+
+// Called in normal client context (lock not held)
+// Currently only supports SRV records for nat mapping
+mDNSlocal void CompleteRecordNatMap(mDNS *m, NATTraversalInfo *n)
+	{
+	const domainname *target;
+	domainname *srvt;
+	AuthRecord *rr = (AuthRecord *)n->clientContext;
+	debugf("SRVNatMap complete %.4a IntPort %u ExternalPort %u NATLease %u", &n->ExternalAddress, mDNSVal16(n->IntPort), mDNSVal16(n->ExternalPort), n->NATLease);
+
+	if (!rr) { LogMsg("CompleteRecordNatMap called with unknown AuthRecord object"); return; }
+	if (!n->NATLease) { LogMsg("CompleteRecordNatMap No NATLease for %s", ARDisplayString(m, rr)); return; }
+
+	if (rr->resrec.rrtype != kDNSType_SRV) {LogMsg("CompleteRecordNatMap: Not a service record %s", ARDisplayString(m, rr)); return; }
+
+	if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) { LogInfo("CompleteRecordNatMap called for %s, Service deregistering", ARDisplayString(m, rr)); return; }
+
+	if (rr->state == regState_DeregPending) { LogInfo("CompleteRecordNatMap called for %s, record in DeregPending", ARDisplayString(m, rr)); return; }
+
+	// As we free the zone info after registering/deregistering with the server (See hndlRecordUpdateReply),
+	// we need to restart the get zone data and nat mapping request to get the latest mapping result as we can't handle it
+	// at this moment. Restart from the beginning.
+	if (!rr->nta || mDNSIPv4AddressIsZero(rr->nta->Addr.ip.v4))
+		{
+		LogInfo("CompleteRecordNatMap called for %s but no zone information!", ARDisplayString(m, rr));
+		// We need to clear out the NATinfo state so that it will result in re-acquiring the mapping
+		// and hence this callback called again.
+		if (rr->NATinfo.clientContext)
+			{
+			mDNS_StopNATOperation_internal(m, &rr->NATinfo);
+			rr->NATinfo.clientContext = mDNSNULL;
+			}
+		rr->state = regState_Pending;
+		rr->ThisAPInterval = INIT_RECORD_REG_INTERVAL;
+		rr->LastAPTime = m->timenow - INIT_RECORD_REG_INTERVAL;
+		return;
+		}
+
+	mDNS_Lock(m);
+	// Reevaluate the target always as Target could have changed while
+	// we were getting the port mapping (See UpdateOneSRVRecord)
+	target = GetServiceTarget(m, rr);
+	srvt = GetRRDomainNameTarget(&rr->resrec);
+	if (!target || target->c[0] == 0 || mDNSIPPortIsZero(n->ExternalPort))
+		{
+		if (target && target->c[0])
+			LogInfo("CompleteRecordNatMap - Target %##s for ResourceRecord %##s, ExternalPort %d", target->c, rr->resrec.name->c, mDNSVal16(n->ExternalPort));
+		else
+			LogInfo("CompleteRecordNatMap - no target for %##s, ExternalPort %d", rr->resrec.name->c, mDNSVal16(n->ExternalPort));
+		if (srvt) srvt->c[0] = 0;
+		rr->state = regState_NoTarget;
+		rr->resrec.rdlength = rr->resrec.rdestimate = 0;
+		mDNS_Unlock(m);
+		UpdateAllServiceRecords(m, rr, mDNSfalse);
+		return;
+		}
+	LogInfo("CompleteRecordNatMap - Target %##s for ResourceRecord %##s, ExternalPort %d", target->c, rr->resrec.name->c, mDNSVal16(n->ExternalPort));
+	// This function might get called multiple times during a network transition event. Previosuly, we could
+	// have put the SRV record in NoTarget state above and deregistered all the other records. When this
+	// function gets called again with a non-zero ExternalPort, we need to set the target and register the
+	// other records again.
+	if (srvt && !SameDomainName(srvt, target))
+		{
+		AssignDomainName(srvt, target);
+		SetNewRData(&rr->resrec, mDNSNULL, 0);		// Update rdlength, rdestimate, rdatahash
+		}
+
+	// SRVChanged is set when when the target of the SRV record changes (See UpdateOneSRVRecord).
+	// As a result of the target change, we might register just that SRV Record if it was
+	// previously registered and we have a new target OR deregister SRV (and the associated
+	// PTR/TXT records) if we don't have a target anymore. When we get a response from the server,
+	// SRVChanged state tells that we registered/deregistered because of a target change
+	// and hence handle accordingly e.g., if we deregistered, put the records in NoTarget state OR
+	// if we registered then put it in Registered state.
+	//
+	// Here, we are registering all the records again from the beginning. Treat this as first time
+	// registration rather than a temporary target change.
+	rr->SRVChanged = mDNSfalse;
+
+	// We want IsRecordMergeable to check whether it is a record whose update can be
+	// sent with others. We set the time before we call IsRecordMergeable, so that
+	// it does not fail this record based on time. We are interested in other checks
+	// at this time
+	rr->state = regState_Pending;
+	rr->ThisAPInterval = INIT_RECORD_REG_INTERVAL;
+	rr->LastAPTime = m->timenow - INIT_RECORD_REG_INTERVAL;
+	if (IsRecordMergeable(m, rr, m->timenow + MERGE_DELAY_TIME))
+		// Delay the record registration by MERGE_DELAY_TIME so that we can merge them
+		// into one update
+		rr->LastAPTime += MERGE_DELAY_TIME;
+	mDNS_Unlock(m);
+	// We call this always even though it may not be necessary always e.g., normal registration
+	// process where TXT and PTR gets registered followed by the SRV record after it gets
+	// the port mapping. In that case, UpdateAllServiceRecords handles the optimization. The
+	// update of TXT and PTR record is required if we entered noTargetState before as explained
+	// above.
+	UpdateAllServiceRecords(m, rr, mDNStrue);
+	}
+
+mDNSlocal void StartRecordNatMap(mDNS *m, AuthRecord *rr)
+	{
+	const mDNSu8 *p;
+	mDNSu8 protocol;
+
+	if (rr->resrec.rrtype != kDNSType_SRV)
+		{
+		LogInfo("StartRecordNatMap: Resource Record %##s type %d, not supported", rr->resrec.name->c, rr->resrec.rrtype);
+		return;
+		}
+	p = rr->resrec.name->c;
+	//Assume <Service Instance>.<App Protocol>.<Transport protocol>.<Name>
+	// Skip the first two labels to get to the transport protocol
+	if (p[0]) p += 1 + p[0];
+	if (p[0]) p += 1 + p[0];
+	if      (SameDomainLabel(p, (mDNSu8 *)"\x4" "_tcp")) protocol = NATOp_MapTCP;
+	else if (SameDomainLabel(p, (mDNSu8 *)"\x4" "_udp")) protocol = NATOp_MapUDP;
+	else { LogMsg("StartRecordNatMap: could not determine transport protocol of service %##s", rr->resrec.name->c); return; }
+	
+	//LogMsg("StartRecordNatMap: clientContext %p IntPort %d srv.port %d %s",
+	//	rr->NATinfo.clientContext, mDNSVal16(rr->NATinfo.IntPort), mDNSVal16(rr->resrec.rdata->u.srv.port), ARDisplayString(m, rr));
+	if (rr->NATinfo.clientContext) mDNS_StopNATOperation_internal(m, &rr->NATinfo);
+	rr->NATinfo.Protocol       = protocol;
+
+	// Shouldn't be trying to set IntPort here --
+	// BuildUpdateMessage overwrites srs->RR_SRV.resrec.rdata->u.srv.port with external (mapped) port number
+	rr->NATinfo.IntPort        = rr->resrec.rdata->u.srv.port;
+	rr->NATinfo.RequestedPort  = rr->resrec.rdata->u.srv.port;
+	rr->NATinfo.NATLease       = 0;		// Request default lease
+	rr->NATinfo.clientCallback = CompleteRecordNatMap;
+	rr->NATinfo.clientContext  = rr;
+	mDNS_StartNATOperation_internal(m, &rr->NATinfo);
+	}
+
+// Unlink an Auth Record from the m->ResourceRecords list.
+// When a resource record enters regState_NoTarget initially, mDNS_Register_internal
+// does not initialize completely e.g., it cannot check for duplicates etc. The resource
+// record is temporarily left in the ResourceRecords list so that we can initialize later
+// when the target is resolvable. Similarly, when host name changes, we enter regState_NoTarget
+// and we do the same.
+
+// This UnlinkResourceRecord routine is very worrying. It bypasses all the normal cleanup performed
+// by mDNS_Deregister_internal and just unceremoniously cuts the record from the active list.
+// This is why re-regsitering this record was producing syslog messages like this:
+// "Error! Tried to add a NAT traversal that's already in the active list"
+// Right now UnlinkResourceRecord is fortunately only called by RegisterAllServiceRecords,
+// which then immediately calls mDNS_Register_internal to re-register the record, which probably
+// masked more serious problems. Any other use of UnlinkResourceRecord is likely to lead to crashes.
+// For now we'll workaround that specific problem by explicitly calling mDNS_StopNATOperation_internal,
+// but long-term we should either stop cancelling the record registration and then re-registering it,
+// or if we really do need to do this for some reason it should be done via the usual
+// mDNS_Deregister_internal path instead of just cutting the record from the list.
+
+mDNSlocal mStatus UnlinkResourceRecord(mDNS *const m, AuthRecord *const rr)
+	{
+	AuthRecord **list = &m->ResourceRecords;
+	while (*list && *list != rr) list = &(*list)->next;
+	if (*list)
+		{
+		*list = rr->next;
+		rr->next = mDNSNULL;
+
+		// Temporary workaround to cancel any active NAT mapping operation
+		if (rr->NATinfo.clientContext)
+			{
+			mDNS_StopNATOperation_internal(m, &rr->NATinfo);
+			rr->NATinfo.clientContext = mDNSNULL;
+			if (rr->resrec.rrtype == kDNSType_SRV) rr->resrec.rdata->u.srv.port = rr->NATinfo.IntPort;
+			}
+
+		return(mStatus_NoError);
+		}
+	LogMsg("UnlinkResourceRecord:ERROR!! - no such active record %##s", rr->resrec.name->c);
+	return(mStatus_NoSuchRecord);
+	}
+
+// We need to go through mDNS_Register again as we did not complete the 
+// full initialization last time e.g., duplicate checks.
+// After we register, we will be in regState_GetZoneData.
+mDNSlocal void RegisterAllServiceRecords(mDNS *const m, AuthRecord *rr)
+	{
+	LogInfo("RegisterAllServiceRecords: Service Record %##s", rr->resrec.name->c);
+	// First Register the service record, we do this differently from other records because
+	// when it entered NoTarget state, it did not go through complete initialization
+	rr->SRVChanged = mDNSfalse;
+	UnlinkResourceRecord(m, rr);
+	mDNS_Register_internal(m, rr);
+	// Register the other records
+	UpdateAllServiceRecords(m, rr, mDNStrue);
+	}
+
+// Called with lock held
+mDNSlocal void UpdateOneSRVRecord(mDNS *m, AuthRecord *rr)
+	{
+	// Target change if:
+	// We have a target and were previously waiting for one, or
+	// We had a target and no longer do, or
+	// The target has changed
+
+	domainname *curtarget = &rr->resrec.rdata->u.srv.target;
+	const domainname *const nt = GetServiceTarget(m, rr);
+	const domainname *const newtarget = nt ? nt : (domainname*)"";
+	mDNSBool TargetChanged = (newtarget->c[0] && rr->state == regState_NoTarget) || !SameDomainName(curtarget, newtarget);
+	mDNSBool HaveZoneData  = rr->nta && !mDNSIPv4AddressIsZero(rr->nta->Addr.ip.v4);
+
+	// Nat state change if:
+	// We were behind a NAT, and now we are behind a new NAT, or
+	// We're not behind a NAT but our port was previously mapped to a different external port
+	// We were not behind a NAT and now we are
+
+	mDNSIPPort port        = rr->resrec.rdata->u.srv.port;
+	mDNSBool NowNeedNATMAP = (rr->AutoTarget == Target_AutoHostAndNATMAP && !mDNSIPPortIsZero(port) && mDNSv4AddrIsRFC1918(&m->AdvertisedV4.ip.v4) && rr->nta && !mDNSAddrIsRFC1918(&rr->nta->Addr));
+	mDNSBool WereBehindNAT = (rr->NATinfo.clientContext != mDNSNULL);
+	mDNSBool PortWasMapped = (rr->NATinfo.clientContext && !mDNSSameIPPort(rr->NATinfo.RequestedPort, port));		// I think this is always false -- SC Sept 07
+	mDNSBool NATChanged    = (!WereBehindNAT && NowNeedNATMAP) || (!NowNeedNATMAP && PortWasMapped);
+
+	(void)HaveZoneData; //unused
+
+	LogInfo("UpdateOneSRVRecord: Resource Record %s TargetChanged %d, NewTarget %##s", ARDisplayString(m, rr), TargetChanged, nt->c);
+
+	debugf("UpdateOneSRVRecord: %##s newtarget %##s TargetChanged %d HaveZoneData %d port %d NowNeedNATMAP %d WereBehindNAT %d PortWasMapped %d NATChanged %d",
+		rr->resrec.name->c, newtarget,
+		TargetChanged, HaveZoneData, mDNSVal16(port), NowNeedNATMAP, WereBehindNAT, PortWasMapped, NATChanged);
+
+	if (m->mDNS_busy != m->mDNS_reentrancy+1)
+		LogMsg("UpdateOneSRVRecord: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
+
+	if (!TargetChanged && !NATChanged) return;
+
+	// If we are deregistering the record, then ignore any NAT/Target change.
+	if (rr->resrec.RecordType == kDNSRecordTypeDeregistering)
+		{
+		LogInfo("UpdateOneSRVRecord: Deregistering record, Ignoring TargetChanged %d, NATChanged %d for %##s, state %d", TargetChanged, NATChanged,
+			rr->resrec.name->c, rr->state);
+		return;
+		}
+
+	if (newtarget)
+		LogInfo("UpdateOneSRVRecord: TargetChanged %d, NATChanged %d for %##s, state %d, newtarget %##s", TargetChanged, NATChanged, rr->resrec.name->c, rr->state, newtarget->c);
+	else
+		LogInfo("UpdateOneSRVRecord: TargetChanged %d, NATChanged %d for %##s, state %d, null newtarget", TargetChanged, NATChanged, rr->resrec.name->c, rr->state);
+	switch(rr->state)
+		{
+		case regState_NATMap:
+			// In these states, the SRV has either not yet been registered (it will get up-to-date information when it is)
+			// or is in the process of, or has already been, deregistered. This assumes that whenever we transition out
+			// of this state, we need to look at the target again.
+			return;
+
+		case regState_UpdatePending:
+			// We are getting a Target change/NAT change while the SRV record is being updated ?
+			// let us not do anything for now.
+			return;
+
+		case regState_NATError:
+			if (!NATChanged) return;
+			// if nat changed, register if we have a target (below)
+
+		case regState_NoTarget:
+			if (!newtarget->c[0])
+				{
+				LogInfo("UpdateOneSRVRecord: No target yet for Resource Record %s", ARDisplayString(m, rr));
+				return;
+				}
+			RegisterAllServiceRecords(m , rr);
+			return;
+		case regState_DeregPending:
+			// We are in DeregPending either because the service was deregistered from above or we handled
+			// a NAT/Target change before and sent the deregistration below. There are a few race conditions
+			// possible
+			//
+			// 1. We are handling a second NAT/Target change while the first dereg is in progress. It is possible
+			//    that first dereg never made it through because there was no network connectivity e.g., disconnecting
+			//    from network triggers this function due to a target change and later connecting to the network
+			//    retriggers this function but the deregistration never made it through yet. Just fall through.
+			//    If there is a target register otherwise deregister.
+			//
+			// 2. While we sent the dereg during a previous NAT/Target change, uDNS_DeregisterRecord gets
+			//    called as part of service deregistration. When the response comes back, we call
+			//    CompleteDeregistration rather than handle NAT/Target change because the record is in
+			//    kDNSRecordTypeDeregistering state.
+			//
+			// 3. If the upper layer deregisters the service, we check for kDNSRecordTypeDeregistering both
+			//    here in this function to avoid handling NAT/Target change and in hndlRecordUpdateReply to call
+			//    CompleteDeregistration instead of handling NAT/Target change. Hence, we are not concerned
+			//    about that case here.
+			//
+			// We just handle case (1) by falling through
+		case regState_Pending:
+		case regState_Refresh:
+		case regState_Registered:
+			// target or nat changed.  deregister service.  upon completion, we'll look for a new target
+			rr->SRVChanged = mDNStrue;
+			rr->ThisAPInterval = INIT_RECORD_REG_INTERVAL;
+			rr->LastAPTime = m->timenow - INIT_RECORD_REG_INTERVAL;
+			if (newtarget->c[0])
+				{
+				LogInfo("UpdateOneSRVRecord: SRV record changed for service %##s, registering with new target %##s",
+					rr->resrec.name->c, newtarget->c);
+				rr->state = regState_Pending;
+				}
+			else
+				{
+				LogInfo("UpdateOneSRVRecord: SRV record changed for service %##s de-registering", rr->resrec.name->c);
+				rr->state = regState_DeregPending;
+				UpdateAllServiceRecords(m, rr, mDNSfalse);
+				}
+			return;
+		case regState_Unregistered:
+		default: LogMsg("UpdateOneSRVRecord: Unknown state %d for %##s", rr->state, rr->resrec.name->c);
+		}
+	}
+
+mDNSexport void UpdateAllSRVRecords(mDNS *m)
+	{
+	m->NextSRVUpdate = 0;
+	LogInfo("UpdateAllSRVRecords %d", m->SleepState);
+
+	if (m->CurrentRecord)
+		LogMsg("UpdateAllSRVRecords ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord));
+	m->CurrentRecord = m->ResourceRecords;
+	while (m->CurrentRecord)
+		{
+		AuthRecord *rptr = m->CurrentRecord;
+		m->CurrentRecord = m->CurrentRecord->next;
+		if (AuthRecord_uDNS(rptr) && rptr->resrec.rrtype == kDNSType_SRV)
+			UpdateOneSRVRecord(m, rptr);
+		}
+	}
+
+// Forward reference: AdvertiseHostname references HostnameCallback, and HostnameCallback calls AdvertiseHostname
+mDNSlocal void HostnameCallback(mDNS *const m, AuthRecord *const rr, mStatus result);
+
+// Called in normal client context (lock not held)
+mDNSlocal void hostnameGetPublicAddressCallback(mDNS *m, NATTraversalInfo *n)
+	{
+	HostnameInfo *h = (HostnameInfo *)n->clientContext;
+
+	if (!h) { LogMsg("RegisterHostnameRecord: registration cancelled"); return; }
+
+	if (!n->Result)
+		{
+		if (mDNSIPv4AddressIsZero(n->ExternalAddress) || mDNSv4AddrIsRFC1918(&n->ExternalAddress)) return;
+		
+		if (h->arv4.resrec.RecordType)
+			{
+			if (mDNSSameIPv4Address(h->arv4.resrec.rdata->u.ipv4, n->ExternalAddress)) return;	// If address unchanged, do nothing
+			LogInfo("Updating hostname %p %##s IPv4 from %.4a to %.4a (NAT gateway's external address)",n,
+				h->arv4.resrec.name->c, &h->arv4.resrec.rdata->u.ipv4, &n->ExternalAddress);
+			mDNS_Deregister(m, &h->arv4);	// mStatus_MemFree callback will re-register with new address
+			}
+		else
+			{
+			LogInfo("Advertising hostname %##s IPv4 %.4a (NAT gateway's external address)", h->arv4.resrec.name->c, &n->ExternalAddress);
+			h->arv4.resrec.RecordType = kDNSRecordTypeKnownUnique;
+			h->arv4.resrec.rdata->u.ipv4 = n->ExternalAddress;
+			mDNS_Register(m, &h->arv4);
+			}
+		}
+	}
+
+// register record or begin NAT traversal
+mDNSlocal void AdvertiseHostname(mDNS *m, HostnameInfo *h)
+	{
+	if (!mDNSIPv4AddressIsZero(m->AdvertisedV4.ip.v4) && h->arv4.resrec.RecordType == kDNSRecordTypeUnregistered)
+		{
+		mDNS_SetupResourceRecord(&h->arv4, mDNSNULL, mDNSInterface_Any, kDNSType_A, kHostNameTTL, kDNSRecordTypeUnregistered, AuthRecordAny, HostnameCallback, h);
+		AssignDomainName(&h->arv4.namestorage, &h->fqdn);
+		h->arv4.resrec.rdata->u.ipv4 = m->AdvertisedV4.ip.v4;
+		h->arv4.state = regState_Unregistered;
+		if (mDNSv4AddrIsRFC1918(&m->AdvertisedV4.ip.v4))
+			{
+			// If we already have a NAT query active, stop it and restart it to make sure we get another callback
+			if (h->natinfo.clientContext) mDNS_StopNATOperation_internal(m, &h->natinfo);
+			h->natinfo.Protocol         = 0;
+			h->natinfo.IntPort          = zeroIPPort;
+			h->natinfo.RequestedPort    = zeroIPPort;
+			h->natinfo.NATLease         = 0;
+			h->natinfo.clientCallback   = hostnameGetPublicAddressCallback;
+			h->natinfo.clientContext    = h;
+			mDNS_StartNATOperation_internal(m, &h->natinfo);
+			}
+		else
+			{
+			LogInfo("Advertising hostname %##s IPv4 %.4a", h->arv4.resrec.name->c, &m->AdvertisedV4.ip.v4);
+			h->arv4.resrec.RecordType = kDNSRecordTypeKnownUnique;
+			mDNS_Register_internal(m, &h->arv4);
+			}
+		}
+
+	if (!mDNSIPv6AddressIsZero(m->AdvertisedV6.ip.v6) && h->arv6.resrec.RecordType == kDNSRecordTypeUnregistered)
+		{
+		mDNS_SetupResourceRecord(&h->arv6, mDNSNULL, mDNSInterface_Any, kDNSType_AAAA, kHostNameTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, HostnameCallback, h);
+		AssignDomainName(&h->arv6.namestorage, &h->fqdn);
+		h->arv6.resrec.rdata->u.ipv6 = m->AdvertisedV6.ip.v6;
+		h->arv6.state = regState_Unregistered;
+		LogInfo("Advertising hostname %##s IPv6 %.16a", h->arv6.resrec.name->c, &m->AdvertisedV6.ip.v6);
+		mDNS_Register_internal(m, &h->arv6);
+		}
+	}
+
+mDNSlocal void HostnameCallback(mDNS *const m, AuthRecord *const rr, mStatus result)
+	{
+	HostnameInfo *hi = (HostnameInfo *)rr->RecordContext;
+
+	if (result == mStatus_MemFree)
+		{
+		if (hi)
+			{
+			// If we're still in the Hostnames list, update to new address
+			HostnameInfo *i;
+			LogInfo("HostnameCallback: Got mStatus_MemFree for %p %p %s", hi, rr, ARDisplayString(m, rr));
+			for (i = m->Hostnames; i; i = i->next)
+				if (rr == &i->arv4 || rr == &i->arv6)
+					{ mDNS_Lock(m); AdvertiseHostname(m, i); mDNS_Unlock(m); return; }
+
+			// Else, we're not still in the Hostnames list, so free the memory
+			if (hi->arv4.resrec.RecordType == kDNSRecordTypeUnregistered &&
+				hi->arv6.resrec.RecordType == kDNSRecordTypeUnregistered)
+				{
+				if (hi->natinfo.clientContext) mDNS_StopNATOperation_internal(m, &hi->natinfo);
+				hi->natinfo.clientContext = mDNSNULL;
+				mDNSPlatformMemFree(hi);	// free hi when both v4 and v6 AuthRecs deallocated
+				}
+			}
+		return;
+		}
+
+	if (result)
+		{
+		// don't unlink or free - we can retry when we get a new address/router
+		if (rr->resrec.rrtype == kDNSType_A)
+			LogMsg("HostnameCallback: Error %d for registration of %##s IP %.4a", result, rr->resrec.name->c, &rr->resrec.rdata->u.ipv4);
+		else
+			LogMsg("HostnameCallback: Error %d for registration of %##s IP %.16a", result, rr->resrec.name->c, &rr->resrec.rdata->u.ipv6);
+		if (!hi) { mDNSPlatformMemFree(rr); return; }
+		if (rr->state != regState_Unregistered) LogMsg("Error: HostnameCallback invoked with error code for record not in regState_Unregistered!");
+
+		if (hi->arv4.state == regState_Unregistered &&
+			hi->arv6.state == regState_Unregistered)
+			{
+			// only deliver status if both v4 and v6 fail
+			rr->RecordContext = (void *)hi->StatusContext;
+			if (hi->StatusCallback)
+				hi->StatusCallback(m, rr, result); // client may NOT make API calls here
+			rr->RecordContext = (void *)hi;
+			}
+		return;
+		}
+
+	// register any pending services that require a target
+	mDNS_Lock(m);
+	m->NextSRVUpdate = NonZeroTime(m->timenow);
+	mDNS_Unlock(m);
+
+	// Deliver success to client
+	if (!hi) { LogMsg("HostnameCallback invoked with orphaned address record"); return; }
+	if (rr->resrec.rrtype == kDNSType_A)
+		LogInfo("Registered hostname %##s IP %.4a", rr->resrec.name->c, &rr->resrec.rdata->u.ipv4);
+	else
+		LogInfo("Registered hostname %##s IP %.16a", rr->resrec.name->c, &rr->resrec.rdata->u.ipv6);
+
+	rr->RecordContext = (void *)hi->StatusContext;
+	if (hi->StatusCallback)
+		hi->StatusCallback(m, rr, result); // client may NOT make API calls here
+	rr->RecordContext = (void *)hi;
+	}
+
+mDNSlocal void FoundStaticHostname(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
+	{
+	const domainname *pktname = &answer->rdata->u.name;
+	domainname *storedname = &m->StaticHostname;
+	HostnameInfo *h = m->Hostnames;
+
+	(void)question;
+
+	if (answer->rdlength != 0)
+		LogInfo("FoundStaticHostname: question %##s -> answer %##s (%s)", question->qname.c, answer->rdata->u.name.c, AddRecord ? "ADD" : "RMV");
+	else
+		LogInfo("FoundStaticHostname: question %##s -> answer NULL (%s)", question->qname.c, AddRecord ? "ADD" : "RMV");
+
+	if (AddRecord && answer->rdlength != 0 && !SameDomainName(pktname, storedname))
+		{
+		AssignDomainName(storedname, pktname);
+		while (h)
+			{
+			if (h->arv4.state == regState_Pending || h->arv4.state == regState_NATMap || h->arv6.state == regState_Pending)
+				{
+				// if we're in the process of registering a dynamic hostname, delay SRV update so we don't have to reregister services if the dynamic name succeeds
+				m->NextSRVUpdate = NonZeroTime(m->timenow + 5 * mDNSPlatformOneSecond);
+				debugf("FoundStaticHostname: NextSRVUpdate in %d %d", m->NextSRVUpdate - m->timenow, m->timenow);
+				return;
+				}
+			h = h->next;
+			}
+		mDNS_Lock(m);
+		m->NextSRVUpdate = NonZeroTime(m->timenow);
+		mDNS_Unlock(m);
+		}
+	else if (!AddRecord && SameDomainName(pktname, storedname))
+		{
+		mDNS_Lock(m);
+		storedname->c[0] = 0;
+		m->NextSRVUpdate = NonZeroTime(m->timenow);
+		mDNS_Unlock(m);
+		}
+	}
+
+// Called with lock held
+mDNSlocal void GetStaticHostname(mDNS *m)
+	{
+	char buf[MAX_REVERSE_MAPPING_NAME_V4];
+	DNSQuestion *q = &m->ReverseMap;
+	mDNSu8 *ip = m->AdvertisedV4.ip.v4.b;
+	mStatus err;
+
+	if (m->ReverseMap.ThisQInterval != -1) return; // already running
+	if (mDNSIPv4AddressIsZero(m->AdvertisedV4.ip.v4)) return;
+
+	mDNSPlatformMemZero(q, sizeof(*q));
+	// Note: This is reverse order compared to a normal dotted-decimal IP address, so we can't use our customary "%.4a" format code
+	mDNS_snprintf(buf, sizeof(buf), "%d.%d.%d.%d.in-addr.arpa.", ip[3], ip[2], ip[1], ip[0]);
+	if (!MakeDomainNameFromDNSNameString(&q->qname, buf)) { LogMsg("Error: GetStaticHostname - bad name %s", buf); return; }
+
+	q->InterfaceID      = mDNSInterface_Any;
+	q->Target           = zeroAddr;
+	q->qtype            = kDNSType_PTR;
+	q->qclass           = kDNSClass_IN;
+	q->LongLived        = mDNSfalse;
+	q->ExpectUnique     = mDNSfalse;
+	q->ForceMCast       = mDNSfalse;
+	q->ReturnIntermed   = mDNStrue;
+	q->SuppressUnusable = mDNSfalse;
+	q->SearchListIndex  = 0;
+	q->AppendSearchDomains = 0;
+	q->RetryWithSearchDomains = mDNSfalse;
+	q->TimeoutQuestion  = 0;
+	q->WakeOnResolve    = 0;
+	q->qnameOrig        = mDNSNULL;
+	q->QuestionCallback = FoundStaticHostname;
+	q->QuestionContext  = mDNSNULL;
+
+	LogInfo("GetStaticHostname: %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+	err = mDNS_StartQuery_internal(m, q);
+	if (err) LogMsg("Error: GetStaticHostname - StartQuery returned error %d", err);
+	}
+
+mDNSexport void mDNS_AddDynDNSHostName(mDNS *m, const domainname *fqdn, mDNSRecordCallback *StatusCallback, const void *StatusContext)
+   {
+	HostnameInfo **ptr = &m->Hostnames;
+
+	LogInfo("mDNS_AddDynDNSHostName %##s", fqdn);
+
+	while (*ptr && !SameDomainName(fqdn, &(*ptr)->fqdn)) ptr = &(*ptr)->next;
+	if (*ptr) { LogMsg("DynDNSHostName %##s already in list", fqdn->c); return; }
+
+	// allocate and format new address record
+	*ptr = mDNSPlatformMemAllocate(sizeof(**ptr));
+	if (!*ptr) { LogMsg("ERROR: mDNS_AddDynDNSHostName - malloc"); return; }
+
+	mDNSPlatformMemZero(*ptr, sizeof(**ptr));
+	AssignDomainName(&(*ptr)->fqdn, fqdn);
+	(*ptr)->arv4.state     = regState_Unregistered;
+	(*ptr)->arv6.state     = regState_Unregistered;
+	(*ptr)->StatusCallback = StatusCallback;
+	(*ptr)->StatusContext  = StatusContext;
+
+	AdvertiseHostname(m, *ptr);
+	}
+
+mDNSexport void mDNS_RemoveDynDNSHostName(mDNS *m, const domainname *fqdn)
+	{
+	HostnameInfo **ptr = &m->Hostnames;
+
+	LogInfo("mDNS_RemoveDynDNSHostName %##s", fqdn);
+
+	while (*ptr && !SameDomainName(fqdn, &(*ptr)->fqdn)) ptr = &(*ptr)->next;
+	if (!*ptr) LogMsg("mDNS_RemoveDynDNSHostName: no such domainname %##s", fqdn->c);
+	else
+		{
+		HostnameInfo *hi = *ptr;
+		// We do it this way because, if we have no active v6 record, the "mDNS_Deregister_internal(m, &hi->arv4);"
+		// below could free the memory, and we have to make sure we don't touch hi fields after that.
+		mDNSBool f4 = hi->arv4.resrec.RecordType != kDNSRecordTypeUnregistered && hi->arv4.state != regState_Unregistered;
+		mDNSBool f6 = hi->arv6.resrec.RecordType != kDNSRecordTypeUnregistered && hi->arv6.state != regState_Unregistered;
+		if (f4) LogInfo("mDNS_RemoveDynDNSHostName removing v4 %##s", fqdn);
+		if (f6) LogInfo("mDNS_RemoveDynDNSHostName removing v6 %##s", fqdn);
+		*ptr = (*ptr)->next; // unlink
+		if (f4) mDNS_Deregister_internal(m, &hi->arv4, mDNS_Dereg_normal);
+		if (f6) mDNS_Deregister_internal(m, &hi->arv6, mDNS_Dereg_normal);
+		// When both deregistrations complete we'll free the memory in the mStatus_MemFree callback
+		}
+	if (!m->mDNS_busy) LogMsg("mDNS_RemoveDynDNSHostName: ERROR: Lock not held");
+	m->NextSRVUpdate = NonZeroTime(m->timenow);
+	}
+
+// Currently called without holding the lock
+// Maybe we should change that?
+mDNSexport void mDNS_SetPrimaryInterfaceInfo(mDNS *m, const mDNSAddr *v4addr, const mDNSAddr *v6addr, const mDNSAddr *router)
+	{
+	mDNSBool v4Changed, v6Changed, RouterChanged;
+
+	if (m->mDNS_busy != m->mDNS_reentrancy)
+		LogMsg("mDNS_SetPrimaryInterfaceInfo: mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
+
+	if (v4addr && v4addr->type != mDNSAddrType_IPv4) { LogMsg("mDNS_SetPrimaryInterfaceInfo v4 address - incorrect type.  Discarding. %#a", v4addr); return; }
+	if (v6addr && v6addr->type != mDNSAddrType_IPv6) { LogMsg("mDNS_SetPrimaryInterfaceInfo v6 address - incorrect type.  Discarding. %#a", v6addr); return; }
+	if (router && router->type != mDNSAddrType_IPv4) { LogMsg("mDNS_SetPrimaryInterfaceInfo passed non-v4 router.  Discarding. %#a",        router); return; }
+
+	mDNS_Lock(m);
+
+	v4Changed     = !mDNSSameIPv4Address(m->AdvertisedV4.ip.v4, v4addr ? v4addr->ip.v4 : zerov4Addr);
+	v6Changed     = !mDNSSameIPv6Address(m->AdvertisedV6.ip.v6, v6addr ? v6addr->ip.v6 : zerov6Addr);
+	RouterChanged = !mDNSSameIPv4Address(m->Router.ip.v4,       router ? router->ip.v4 : zerov4Addr);
+
+	if (v4addr && (v4Changed || RouterChanged))
+		debugf("mDNS_SetPrimaryInterfaceInfo: address changed from %#a to %#a", &m->AdvertisedV4, v4addr);
+
+	if (v4addr) m->AdvertisedV4 = *v4addr; else m->AdvertisedV4.ip.v4 = zerov4Addr;
+	if (v6addr) m->AdvertisedV6 = *v6addr; else m->AdvertisedV6.ip.v6 = zerov6Addr;
+	if (router) m->Router       = *router; else m->Router      .ip.v4 = zerov4Addr;
+	// setting router to zero indicates that nat mappings must be reestablished when router is reset
+
+	if (v4Changed || RouterChanged || v6Changed)
+		{
+		HostnameInfo *i;
+		LogInfo("mDNS_SetPrimaryInterfaceInfo: %s%s%s%#a %#a %#a",
+			v4Changed     ? "v4Changed "     : "",
+			RouterChanged ? "RouterChanged " : "",
+			v6Changed     ? "v6Changed "     : "", v4addr, v6addr, router);
+
+		for (i = m->Hostnames; i; i = i->next)
+			{
+			LogInfo("mDNS_SetPrimaryInterfaceInfo updating host name registrations for %##s", i->fqdn.c);
+	
+			if (i->arv4.resrec.RecordType > kDNSRecordTypeDeregistering &&
+				!mDNSSameIPv4Address(i->arv4.resrec.rdata->u.ipv4, m->AdvertisedV4.ip.v4))
+				{
+				LogInfo("mDNS_SetPrimaryInterfaceInfo deregistering %s", ARDisplayString(m, &i->arv4));
+				mDNS_Deregister_internal(m, &i->arv4, mDNS_Dereg_normal);
+				}
+	
+			if (i->arv6.resrec.RecordType > kDNSRecordTypeDeregistering &&
+				!mDNSSameIPv6Address(i->arv6.resrec.rdata->u.ipv6, m->AdvertisedV6.ip.v6))
+				{
+				LogInfo("mDNS_SetPrimaryInterfaceInfo deregistering %s", ARDisplayString(m, &i->arv6));
+				mDNS_Deregister_internal(m, &i->arv6, mDNS_Dereg_normal);
+				}
+
+			// AdvertiseHostname will only register new address records.
+			// For records still in the process of deregistering it will ignore them, and let the mStatus_MemFree callback handle them.
+			AdvertiseHostname(m, i);
+			}
+
+		if (v4Changed || RouterChanged)
+			{
+			// If we have a non-zero IPv4 address, we should try immediately to see if we have a NAT gateway
+			// If we have no IPv4 address, we don't want to be in quite such a hurry to report failures to our clients
+			// <rdar://problem/6935929> Sleeping server sometimes briefly disappears over Back to My Mac after it wakes up
+			m->ExternalAddress      = zerov4Addr;
+			m->retryIntervalGetAddr = NATMAP_INIT_RETRY;
+			m->retryGetAddr         = m->timenow + (v4addr ? 0 : mDNSPlatformOneSecond * 5);
+			m->NextScheduledNATOp   = m->timenow;
+			m->LastNATMapResultCode = NATErr_None;
+#ifdef _LEGACY_NAT_TRAVERSAL_
+			LNT_ClearState(m);
+#endif // _LEGACY_NAT_TRAVERSAL_
+			LogInfo("mDNS_SetPrimaryInterfaceInfo:%s%s: retryGetAddr in %d %d",
+				v4Changed     ? " v4Changed"     : "",
+				RouterChanged ? " RouterChanged" : "",
+				m->retryGetAddr - m->timenow, m->timenow);
+			}
+
+		if (m->ReverseMap.ThisQInterval != -1) mDNS_StopQuery_internal(m, &m->ReverseMap);
+		m->StaticHostname.c[0] = 0;
+		
+		m->NextSRVUpdate = NonZeroTime(m->timenow);
+		
+#if APPLE_OSX_mDNSResponder
+		if (RouterChanged)	uuid_generate(m->asl_uuid);
+		UpdateAutoTunnelDomainStatuses(m);
+#endif
+		}
+
+	mDNS_Unlock(m);
+	}
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark - Incoming Message Processing
+#endif
+
+mDNSlocal mStatus ParseTSIGError(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, const domainname *const displayname)
+	{
+	const mDNSu8 *ptr;
+	mStatus err = mStatus_NoError;
+	int i;
+
+	ptr = LocateAdditionals(msg, end);
+	if (!ptr) goto finish;
+
+	for (i = 0; i < msg->h.numAdditionals; i++)
+		{
+		ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &m->rec);
+		if (!ptr) goto finish;
+		if (m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative && m->rec.r.resrec.rrtype == kDNSType_TSIG)
+			{
+			mDNSu32 macsize;
+			mDNSu8 *rd = m->rec.r.resrec.rdata->u.data;
+			mDNSu8 *rdend = rd + m->rec.r.resrec.rdlength;
+			int alglen = DomainNameLengthLimit(&m->rec.r.resrec.rdata->u.name, rdend);
+			if (alglen > MAX_DOMAIN_NAME) goto finish;
+			rd += alglen;                                       // algorithm name
+			if (rd + 6 > rdend) goto finish;
+			rd += 6;                                            // 48-bit timestamp
+			if (rd + sizeof(mDNSOpaque16) > rdend) goto finish;
+			rd += sizeof(mDNSOpaque16);                         // fudge
+			if (rd + sizeof(mDNSOpaque16) > rdend) goto finish;
+			macsize = mDNSVal16(*(mDNSOpaque16 *)rd);
+			rd += sizeof(mDNSOpaque16);                         // MAC size
+			if (rd + macsize > rdend) goto finish;
+			rd += macsize;
+			if (rd + sizeof(mDNSOpaque16) > rdend) goto finish;
+			rd += sizeof(mDNSOpaque16);                         // orig id
+			if (rd + sizeof(mDNSOpaque16) > rdend) goto finish;
+			err = mDNSVal16(*(mDNSOpaque16 *)rd);               // error code
+
+			if      (err == TSIG_ErrBadSig)  { LogMsg("%##s: bad signature", displayname->c);              err = mStatus_BadSig;     }
+			else if (err == TSIG_ErrBadKey)  { LogMsg("%##s: bad key", displayname->c);                    err = mStatus_BadKey;     }
+			else if (err == TSIG_ErrBadTime) { LogMsg("%##s: bad time", displayname->c);                   err = mStatus_BadTime;    }
+			else if (err)                    { LogMsg("%##s: unknown tsig error %d", displayname->c, err); err = mStatus_UnknownErr; }
+			goto finish;
+			}
+		m->rec.r.resrec.RecordType = 0;		// Clear RecordType to show we're not still using it
+		}
+
+	finish:
+	m->rec.r.resrec.RecordType = 0;		// Clear RecordType to show we're not still using it
+	return err;
+	}
+
+mDNSlocal mStatus checkUpdateResult(mDNS *const m, const domainname *const displayname, const mDNSu8 rcode, const DNSMessage *const msg, const mDNSu8 *const end)
+	{
+	(void)msg;	// currently unused, needed for TSIG errors
+	if (!rcode) return mStatus_NoError;
+	else if (rcode == kDNSFlag1_RC_YXDomain)
+		{
+		debugf("name in use: %##s", displayname->c);
+		return mStatus_NameConflict;
+		}
+	else if (rcode == kDNSFlag1_RC_Refused)
+		{
+		LogMsg("Update %##s refused", displayname->c);
+		return mStatus_Refused;
+		}
+	else if (rcode == kDNSFlag1_RC_NXRRSet)
+		{
+		LogMsg("Reregister refused (NXRRSET): %##s", displayname->c);
+		return mStatus_NoSuchRecord;
+		}
+	else if (rcode == kDNSFlag1_RC_NotAuth)
+		{
+		// TSIG errors should come with FormErr as per RFC 2845, but BIND 9 sends them with NotAuth so we look here too
+		mStatus tsigerr = ParseTSIGError(m, msg, end, displayname);
+		if (!tsigerr)
+			{
+			LogMsg("Permission denied (NOAUTH): %##s", displayname->c);
+			return mStatus_UnknownErr;
+			}
+		else return tsigerr;
+		}
+	else if (rcode == kDNSFlag1_RC_FormErr)
+		{
+		mStatus tsigerr = ParseTSIGError(m, msg, end, displayname);
+		if (!tsigerr)
+			{
+			LogMsg("Format Error: %##s", displayname->c);
+			return mStatus_UnknownErr;
+			}
+		else return tsigerr;
+		}
+	else
+		{
+		LogMsg("Update %##s failed with rcode %d", displayname->c, rcode);
+		return mStatus_UnknownErr;
+		}
+	}
+
+// We add three Additional Records for unicast resource record registrations
+// which is a function of AuthInfo and AutoTunnel properties
+mDNSlocal mDNSu32 RRAdditionalSize(mDNS *const m, DomainAuthInfo *AuthInfo)
+	{
+	mDNSu32 leaseSize, hinfoSize, tsigSize;
+	mDNSu32 rr_base_size = 10; // type (2) class (2) TTL (4) rdlength (2)
+
+	// OPT RR : Emptyname(.) + base size + rdataOPT
+	leaseSize = 1 + rr_base_size + sizeof(rdataOPT);
+
+	// HINFO: Resource Record Name + base size + RDATA
+	// HINFO is added only for autotunnels
+	hinfoSize = 0;
+	if (AuthInfo && AuthInfo->AutoTunnel)
+		hinfoSize = (m->hostlabel.c[0] + 1) + DomainNameLength(&AuthInfo->domain) +
+			rr_base_size + (2 + m->HIHardware.c[0] + m->HISoftware.c[0]);
+
+	//TSIG: Resource Record Name + base size + RDATA
+	// RDATA:
+    // 	Algorithm name: hmac-md5.sig-alg.reg.int (8+7+3+3 + 5 bytes for length = 26 bytes)
+    // 	Time: 6 bytes
+	// 	Fudge: 2 bytes
+    // 	Mac Size: 2 bytes
+	// 	Mac: 16 bytes
+	// 	ID: 2 bytes
+    // 	Error: 2 bytes
+    // 	Len: 2 bytes
+	// 	Total: 58 bytes 
+	tsigSize = 0;
+	if (AuthInfo) tsigSize = DomainNameLength(&AuthInfo->keyname) + rr_base_size + 58;
+
+	return (leaseSize + hinfoSize + tsigSize);
+	}
+
+//Note: Make sure that RREstimatedSize is updated accordingly if anything that is done here
+//would modify rdlength/rdestimate
+mDNSlocal mDNSu8* BuildUpdateMessage(mDNS *const m, mDNSu8 *ptr, AuthRecord *rr, mDNSu8 *limit)
+	{
+	//If this record is deregistering, then just send the deletion record
+	if (rr->state == regState_DeregPending)
+		{
+		rr->expire = 0;		// Indicate that we have no active registration any more
+		ptr = putDeletionRecordWithLimit(&m->omsg, ptr, &rr->resrec, limit);
+		if (!ptr) goto exit;
+		return ptr;
+		}
+
+	// This is a common function to both sending an update in a group or individual
+	// records separately. Hence, we change the state here.
+	if (rr->state == regState_Registered) rr->state = regState_Refresh;
+	if (rr->state != regState_Refresh && rr->state != regState_UpdatePending)
+		rr->state = regState_Pending;
+
+	// For Advisory records like e.g., _services._dns-sd, which is shared, don't send goodbyes as multiple
+	// host might be registering records and deregistering from one does not make sense
+	if (rr->resrec.RecordType != kDNSRecordTypeAdvisory) rr->RequireGoodbye = mDNStrue;
+
+	if ((rr->resrec.rrtype == kDNSType_SRV) && (rr->AutoTarget == Target_AutoHostAndNATMAP) &&
+		!mDNSIPPortIsZero(rr->NATinfo.ExternalPort))
+		{
+		rr->resrec.rdata->u.srv.port = rr->NATinfo.ExternalPort;
+		}
+
+	if (rr->state == regState_UpdatePending)
+		{
+		// delete old RData
+		SetNewRData(&rr->resrec, rr->OrigRData, rr->OrigRDLen);
+		if (!(ptr = putDeletionRecordWithLimit(&m->omsg, ptr, &rr->resrec, limit))) goto exit; // delete old rdata
+
+		// add new RData
+		SetNewRData(&rr->resrec, rr->InFlightRData, rr->InFlightRDLen);
+		if (!(ptr = PutResourceRecordTTLWithLimit(&m->omsg, ptr, &m->omsg.h.mDNS_numUpdates, &rr->resrec, rr->resrec.rroriginalttl, limit)))  goto exit;
+		}
+	else
+		{
+		if (rr->resrec.RecordType == kDNSRecordTypeKnownUnique || rr->resrec.RecordType == kDNSRecordTypeVerified)
+			{
+			// KnownUnique : Delete any previous value 
+			// For Unicast registrations, we don't verify that it is unique, but set to verified and hence we want to
+			// delete any previous value
+			ptr = putDeleteRRSetWithLimit(&m->omsg, ptr, rr->resrec.name, rr->resrec.rrtype, limit);
+			if (!ptr) goto exit;
+			}
+		else if (rr->resrec.RecordType != kDNSRecordTypeShared)
+			{
+			// For now don't do this, until we have the logic for intelligent grouping of individual records into logical service record sets
+			//ptr = putPrereqNameNotInUse(rr->resrec.name, &m->omsg, ptr, end);
+			if (!ptr) goto exit;
+			}
+
+		ptr = PutResourceRecordTTLWithLimit(&m->omsg, ptr, &m->omsg.h.mDNS_numUpdates, &rr->resrec, rr->resrec.rroriginalttl, limit);
+		if (!ptr) goto exit;
+		}
+
+	return ptr;
+exit:
+	LogMsg("BuildUpdateMessage: Error formatting message for %s", ARDisplayString(m, rr));
+	return mDNSNULL;
+	}
+
+// Called with lock held
+mDNSlocal void SendRecordRegistration(mDNS *const m, AuthRecord *rr)
+	{
+	mDNSu8 *ptr = m->omsg.data;
+	mStatus err = mStatus_UnknownErr;
+	mDNSu8 *limit;
+	DomainAuthInfo *AuthInfo;
+
+	// For the ability to register large TXT records, we limit the single record registrations
+	// to AbsoluteMaxDNSMessageData
+	limit = ptr + AbsoluteMaxDNSMessageData;
+
+	AuthInfo = GetAuthInfoForName_internal(m, rr->resrec.name);
+	limit -= RRAdditionalSize(m, AuthInfo);
+
+	if (m->mDNS_busy != m->mDNS_reentrancy+1)
+		LogMsg("SendRecordRegistration: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
+
+	if (!rr->nta || mDNSIPv4AddressIsZero(rr->nta->Addr.ip.v4))
+		{
+		// We never call this function when there is no zone information . Log a message if it ever happens.
+		LogMsg("SendRecordRegistration: No Zone information, should not happen %s", ARDisplayString(m, rr));
+		return;
+		}
+
+	rr->updateid = mDNS_NewMessageID(m);
+	InitializeDNSMessage(&m->omsg.h, rr->updateid, UpdateReqFlags);
+
+	// set zone
+	ptr = putZone(&m->omsg, ptr, limit, rr->zone, mDNSOpaque16fromIntVal(rr->resrec.rrclass));
+	if (!ptr) goto exit;
+
+	if (!(ptr = BuildUpdateMessage(m, ptr, rr, limit))) goto exit;
+
+	if (rr->uselease)
+		{
+		ptr = putUpdateLeaseWithLimit(&m->omsg, ptr, DEFAULT_UPDATE_LEASE, limit);
+		if (!ptr) goto exit;
+		}
+	if (rr->Private)
+		{
+		LogInfo("SendRecordRegistration TCP %p %s", rr->tcp, ARDisplayString(m, rr));
+		if (rr->tcp) LogInfo("SendRecordRegistration: Disposing existing TCP connection for %s", ARDisplayString(m, rr));
+		if (rr->tcp) { DisposeTCPConn(rr->tcp); rr->tcp = mDNSNULL; }
+		if (!rr->nta) { LogMsg("SendRecordRegistration:Private:ERROR!! nta is NULL for %s", ARDisplayString(m, rr)); return; }
+		rr->tcp = MakeTCPConn(m, &m->omsg, ptr, kTCPSocketFlags_UseTLS, &rr->nta->Addr, rr->nta->Port, &rr->nta->Host, mDNSNULL, rr);
+		}
+	else
+		{
+		LogInfo("SendRecordRegistration UDP %s", ARDisplayString(m, rr));
+		if (!rr->nta) { LogMsg("SendRecordRegistration:ERROR!! nta is NULL for %s", ARDisplayString(m, rr)); return; }
+		err = mDNSSendDNSMessage(m, &m->omsg, ptr, mDNSInterface_Any, mDNSNULL, &rr->nta->Addr, rr->nta->Port, mDNSNULL, GetAuthInfoForName_internal(m, rr->resrec.name));
+		if (err) debugf("ERROR: SendRecordRegistration - mDNSSendDNSMessage - %d", err);
+		}
+
+	SetRecordRetry(m, rr, 0);
+	return;
+exit:
+	LogMsg("SendRecordRegistration: Error formatting message for %s, disabling further updates", ARDisplayString(m, rr));
+	// Disable this record from future updates
+	rr->state = regState_NoTarget;
+	}
+
+// Is the given record "rr" eligible for merging ?
+mDNSlocal mDNSBool IsRecordMergeable(mDNS *const m, AuthRecord *rr, mDNSs32 time)
+	{
+	DomainAuthInfo *info;
+	(void) m; //unused
+	// A record is eligible for merge, if the following properties are met.
+	//
+	// 1. uDNS Resource Record
+	// 2. It is time to send them now
+	// 3. It is in proper state
+	// 4. Update zone has been resolved
+	// 5. if DomainAuthInfo exists for the zone, it should not be soon deleted
+	// 6. Zone information is present
+	// 7. Update server is not zero
+	// 8. It has a non-null zone
+	// 9. It uses a lease option 
+	// 10. DontMerge is not set
+	//
+	// Following code is implemented as separate "if" statements instead of one "if" statement
+	// is for better debugging purposes e.g., we know exactly what failed if debugging turned on.
+
+	if (!AuthRecord_uDNS(rr)) return mDNSfalse;
+
+	if (rr->LastAPTime + rr->ThisAPInterval - time > 0)
+		{ debugf("IsRecordMergeable: Time %d not reached for %s", rr->LastAPTime + rr->ThisAPInterval - m->timenow, ARDisplayString(m, rr)); return mDNSfalse; }
+
+	if (!rr->zone) return mDNSfalse;
+
+	info = GetAuthInfoForName_internal(m, rr->zone);
+
+	if (info && info->deltime && m->timenow - info->deltime >= 0) {debugf("IsRecordMergeable: Domain %##s will be deleted soon", info->domain.c); return mDNSfalse;}
+
+	if (rr->state != regState_DeregPending && rr->state != regState_Pending && rr->state != regState_Registered && rr->state != regState_Refresh && rr->state != regState_UpdatePending)
+		{ debugf("IsRecordMergeable: state %d not right  %s", rr->state, ARDisplayString(m, rr)); return mDNSfalse; }
+
+	if (!rr->nta || mDNSIPv4AddressIsZero(rr->nta->Addr.ip.v4)) return mDNSfalse;
+
+	if (!rr->uselease) return mDNSfalse;
+	
+	if (rr->mState == mergeState_DontMerge) {debugf("IsRecordMergeable Dontmerge true %s", ARDisplayString(m, rr));return mDNSfalse;}
+	debugf("IsRecordMergeable: Returning true for %s", ARDisplayString(m, rr));
+	return mDNStrue;
+	}
+
+// Is the resource record "rr" eligible to merge to with "currentRR" ?
+mDNSlocal mDNSBool AreRecordsMergeable(mDNS *const m, AuthRecord *currentRR, AuthRecord *rr, mDNSs32 time)
+	{
+	// A record is eligible to merge with another record as long it is eligible for merge in itself
+	// and it has the same zone information as the other record
+	if (!IsRecordMergeable(m, rr, time)) return mDNSfalse;
+
+	if (!SameDomainName(currentRR->zone, rr->zone))
+		{ debugf("AreRecordMergeable zone mismatch current rr Zone %##s, rr zone  %##s", currentRR->zone->c, rr->zone->c); return mDNSfalse; }
+
+	if (!mDNSSameIPv4Address(currentRR->nta->Addr.ip.v4, rr->nta->Addr.ip.v4)) return mDNSfalse;
+
+	if (!mDNSSameIPPort(currentRR->nta->Port, rr->nta->Port)) return mDNSfalse;
+
+	debugf("AreRecordsMergeable: Returning true for %s", ARDisplayString(m, rr));
+	return mDNStrue;
+	}
+
+// If we can't build the message successfully because of problems in pre-computing
+// the space, we disable merging for all the current records
+mDNSlocal void RRMergeFailure(mDNS *const m)
+	{
+	AuthRecord *rr;
+	for (rr = m->ResourceRecords; rr; rr = rr->next)
+		{
+		rr->mState = mergeState_DontMerge;
+		rr->SendRNow = mDNSNULL;
+		// Restarting the registration is much simpler than saving and restoring
+		// the exact time
+		ActivateUnicastRegistration(m, rr);
+		}
+	}
+
+mDNSlocal void SendGroupRRMessage(mDNS *const m, AuthRecord *anchorRR, mDNSu8 *ptr, DomainAuthInfo *info)
+	{
+	mDNSu8 *limit;
+	if (!anchorRR) {debugf("SendGroupRRMessage: Could not merge records"); return;}
+
+	if (info && info->AutoTunnel) limit = m->omsg.data + AbsoluteMaxDNSMessageData;
+	else limit = m->omsg.data + NormalMaxDNSMessageData;
+
+	// This has to go in the additional section and hence need to be done last
+	ptr = putUpdateLeaseWithLimit(&m->omsg, ptr, DEFAULT_UPDATE_LEASE, limit);
+	if (!ptr)
+		{
+		LogMsg("SendGroupRRMessage: ERROR: Could not put lease option, failing the group registration");
+		// if we can't put the lease, we need to undo the merge
+		RRMergeFailure(m);
+		return;
+		}
+	if (anchorRR->Private)
+		{
+		if (anchorRR->tcp) debugf("SendGroupRRMessage: Disposing existing TCP connection for %s", ARDisplayString(m, anchorRR));
+		if (anchorRR->tcp) { DisposeTCPConn(anchorRR->tcp); anchorRR->tcp = mDNSNULL; }
+		if (!anchorRR->nta) { LogMsg("SendGroupRRMessage:ERROR!! nta is NULL for %s", ARDisplayString(m, anchorRR)); return; }
+		anchorRR->tcp = MakeTCPConn(m, &m->omsg, ptr, kTCPSocketFlags_UseTLS, &anchorRR->nta->Addr, anchorRR->nta->Port, &anchorRR->nta->Host, mDNSNULL, anchorRR);
+		if (!anchorRR->tcp) LogInfo("SendGroupRRMessage: Cannot establish TCP connection for %s", ARDisplayString(m, anchorRR));
+		else LogInfo("SendGroupRRMessage: Sent a group update ID: %d start %p, end %p, limit %p", mDNSVal16(m->omsg.h.id), m->omsg.data, ptr, limit);
+		}
+	else 
+		{
+		mStatus err = mDNSSendDNSMessage(m, &m->omsg, ptr, mDNSInterface_Any, mDNSNULL, &anchorRR->nta->Addr, anchorRR->nta->Port, mDNSNULL, info);
+		if (err) LogInfo("SendGroupRRMessage: Cannot send UDP message for %s", ARDisplayString(m, anchorRR));
+		else LogInfo("SendGroupRRMessage: Sent a group UDP update ID: %d start %p, end %p, limit %p", mDNSVal16(m->omsg.h.id), m->omsg.data, ptr, limit);
+		}
+	return;
+	}
+
+// As we always include the zone information and the resource records contain zone name
+// at the end, it will get compressed. Hence, we subtract zoneSize and add two bytes for
+// the compression pointer
+mDNSlocal mDNSu32 RREstimatedSize(AuthRecord *rr, int zoneSize)
+	{
+	int rdlength;
+
+	// Note: Estimation of the record size has to mirror the logic in BuildUpdateMessage, otherwise estimation
+	// would be wrong. Currently BuildUpdateMessage calls SetNewRData in UpdatePending case. Hence, we need
+	// to account for that here. Otherwise, we might under estimate the size.
+	if (rr->state == regState_UpdatePending)
+		// old RData that will be deleted
+		// new RData that will be added
+		rdlength = rr->OrigRDLen + rr->InFlightRDLen;
+	else
+		rdlength = rr->resrec.rdestimate;
+
+	if (rr->state == regState_DeregPending)
+		{
+		debugf("RREstimatedSize: ResourceRecord %##s (%s), DomainNameLength %d, zoneSize %d, rdestimate %d",
+				rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), DomainNameLength(rr->resrec.name), zoneSize, rdlength);
+		return DomainNameLength(rr->resrec.name) - zoneSize + 2 + 10 + rdlength;
+		}
+
+	// For SRV, TXT, AAAA etc. that are Unique/Verified, we also send a Deletion Record
+	if (rr->resrec.RecordType == kDNSRecordTypeKnownUnique || rr->resrec.RecordType == kDNSRecordTypeVerified)
+		{
+		// Deletion Record: Resource Record Name + Base size (10) + 0
+		// Record: Resource Record Name (Compressed = 2) + Base size (10) + rdestimate
+
+		debugf("RREstimatedSize: ResourceRecord %##s (%s), DomainNameLength %d, zoneSize %d, rdestimate %d",
+				rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), DomainNameLength(rr->resrec.name), zoneSize, rdlength);
+		return DomainNameLength(rr->resrec.name) - zoneSize + 2 + 10 + 2 + 10 + rdlength;
+		}
+	else
+		{
+		return DomainNameLength(rr->resrec.name) - zoneSize + 2 + 10 + rdlength;
+		}
+	}
+
+mDNSlocal AuthRecord *MarkRRForSending(mDNS *const m)
+	{
+	AuthRecord *rr;
+	AuthRecord *firstRR = mDNSNULL;
+
+	// Look for records that needs to be sent in the next two seconds (MERGE_DELAY_TIME is set to 1 second).
+	// The logic is as follows.
+	//
+	// 1. Record 1 finishes getting zone data and its registration gets delayed by 1 second
+	// 2. Record 2 comes 0.1 second later, finishes getting its zone data and its registration is also delayed by
+	//    1 second which is now scheduled at 1.1 second
+	//
+	// By looking for 1 second into the future (m->timenow + MERGE_DELAY_TIME below does that) we have merged both
+	// of the above records. Note that we can't look for records too much into the future as this will affect the
+	// retry logic. The first retry is scheduled at 3 seconds. Hence, we should always look smaller than that.
+	// Anything more than one second will affect the first retry to happen sooner. 
+	//
+	// Note: As a side effect of looking one second into the future to facilitate merging, the retries happen
+	// one second sooner.
+	for (rr = m->ResourceRecords; rr; rr = rr->next)
+		{
+		if (!firstRR)
+			{
+			if (!IsRecordMergeable(m, rr, m->timenow + MERGE_DELAY_TIME)) continue;
+			firstRR = rr;
+			}
+		else if (!AreRecordsMergeable(m, firstRR, rr, m->timenow + MERGE_DELAY_TIME)) continue;
+
+		if (rr->SendRNow) LogMsg("MarkRRForSending: Resourcerecord %s already marked for sending", ARDisplayString(m, rr));
+		rr->SendRNow = mDNSInterfaceMark;
+		}
+
+	// We parsed through all records and found something to send. The services/records might
+	// get registered at different times but we want the refreshes to be all merged and sent
+	// as one update. Hence, we accelerate some of the records so that they will sync up in
+	// the future. Look at the records excluding the ones that we have already sent in the
+	// previous pass. If it half way through its scheduled refresh/retransmit, merge them
+	// into this packet.
+	//
+	// Note that we only look at Registered/Refresh state to keep it simple. As we don't know
+	// whether the current update will fit into one or more packets, merging a resource record
+	// (which is in a different state) that has been scheduled for retransmit would trigger
+	// sending more packets.
+	if (firstRR)
+		{
+		int acc = 0;
+		for (rr = m->ResourceRecords; rr; rr = rr->next)
+			{
+			if ((rr->state != regState_Registered && rr->state != regState_Refresh) ||
+				(rr->SendRNow == mDNSInterfaceMark) || 
+				(!AreRecordsMergeable(m, firstRR, rr, m->timenow + rr->ThisAPInterval/2)))
+				continue;
+			rr->SendRNow = mDNSInterfaceMark;
+			acc++;
+			}
+		if (acc) LogInfo("MarkRRForSending: Accelereated %d records", acc);
+		}
+	return firstRR;
+	}
+
+mDNSlocal mDNSBool SendGroupUpdates(mDNS *const m)
+	{
+	mDNSOpaque16 msgid;
+	mDNSs32 spaceleft = 0;
+	mDNSs32 zoneSize, rrSize;
+	mDNSu8 *oldnext; // for debugging
+	mDNSu8 *next = m->omsg.data;
+	AuthRecord *rr;
+	AuthRecord *anchorRR = mDNSNULL;
+	int nrecords = 0;
+	AuthRecord *startRR = m->ResourceRecords;
+	mDNSu8 *limit = mDNSNULL;
+	DomainAuthInfo *AuthInfo = mDNSNULL;
+	mDNSBool sentallRecords = mDNStrue;
+
+
+	// We try to fit as many ResourceRecords as possible in AbsoluteNormal/MaxDNSMessageData. Before we start
+	// putting in resource records, we need to reserve space for a few things. Every group/packet should
+	// have the following.
+	//
+	// 1) Needs space for the Zone information (which needs to be at the beginning)
+	// 2) Additional section MUST have space for lease option, HINFO and TSIG option (which needs to
+	//    to be at the end)
+	// 
+	// In future we need to reserve space for the pre-requisites which also goes at the beginning.
+	// To accomodate pre-requisites in the future, first we walk the whole list marking records
+	// that can be sent in this packet and computing the space needed for these records. 
+	// For TXT and SRV records, we delete the previous record if any by sending the same
+	// resource record with ANY RDATA and zero rdlen. Hence, we need to have space for both of them.
+
+	while (startRR)
+		{
+		AuthInfo = mDNSNULL;
+		anchorRR = mDNSNULL;
+		nrecords = 0;
+		zoneSize = 0;
+		for (rr = startRR; rr; rr = rr->next)
+			{
+			if (rr->SendRNow != mDNSInterfaceMark) continue;
+	
+			rr->SendRNow = mDNSNULL;
+
+			if (!anchorRR)
+				{
+				AuthInfo = GetAuthInfoForName_internal(m, rr->zone);
+
+				// Though we allow single record registrations for UDP to be AbsoluteMaxDNSMessageData (See
+				// SendRecordRegistration) to handle large TXT records, to avoid fragmentation we limit UDP
+				// message to NormalMaxDNSMessageData
+				if (AuthInfo && AuthInfo->AutoTunnel) spaceleft = AbsoluteMaxDNSMessageData;
+				else spaceleft = NormalMaxDNSMessageData;
+		
+				next = m->omsg.data;
+				spaceleft -= RRAdditionalSize(m, AuthInfo);
+				if (spaceleft <= 0)
+					{
+					LogMsg("SendGroupUpdates: ERROR!!: spaceleft is zero at the beginning");
+					RRMergeFailure(m);
+					return mDNSfalse;
+					}
+				limit = next + spaceleft;
+
+				// Build the initial part of message before putting in the other records
+ 				msgid = mDNS_NewMessageID(m);
+				InitializeDNSMessage(&m->omsg.h, msgid, UpdateReqFlags);
+	
+				// We need zone information at the beginning of the packet. Length: ZNAME, ZTYPE(2), ZCLASS(2)
+				// zone has to be non-NULL for a record to be mergeable, hence it is safe to set/ examine zone
+				//without checking for NULL.
+				zoneSize = DomainNameLength(rr->zone) + 4;
+				spaceleft -= zoneSize;
+				if (spaceleft <= 0)
+					{
+					LogMsg("SendGroupUpdates: ERROR no space for zone information, disabling merge");
+					RRMergeFailure(m);
+					return mDNSfalse;
+					}
+				next = putZone(&m->omsg, next, limit, rr->zone, mDNSOpaque16fromIntVal(rr->resrec.rrclass));
+				if (!next)
+					{
+					LogMsg("SendGroupUpdates: ERROR! Cannot put zone, disabling merge");
+					RRMergeFailure(m);
+					return mDNSfalse;
+					}
+				anchorRR = rr;
+				}
+
+			rrSize = RREstimatedSize(rr, zoneSize - 4);
+
+			if ((spaceleft - rrSize) < 0) 
+				{
+				// If we can't fit even a single message, skip it, it will be sent separately
+				// in CheckRecordUpdates
+				if (!nrecords)
+					{
+					LogInfo("SendGroupUpdates: Skipping message %s, spaceleft %d, rrSize %d", ARDisplayString(m, rr), spaceleft, rrSize);
+					// Mark this as not sent so that the caller knows about it
+					rr->SendRNow = mDNSInterfaceMark;
+					// We need to remove the merge delay so that we can send it immediately
+					rr->ThisAPInterval = INIT_RECORD_REG_INTERVAL;
+					rr->LastAPTime = m->timenow - INIT_RECORD_REG_INTERVAL;
+					rr = rr->next;
+					anchorRR = mDNSNULL;
+					sentallRecords = mDNSfalse;
+					}
+				else
+					{
+					LogInfo("SendGroupUpdates:1: Parsed %d records and sending using %s, spaceleft %d, rrSize %d", nrecords, ARDisplayString(m, anchorRR), spaceleft, rrSize);
+					SendGroupRRMessage(m, anchorRR, next, AuthInfo);
+					}
+				break;		// breaks out of for loop
+				}
+			spaceleft -= rrSize;
+			oldnext = next;
+			LogInfo("SendGroupUpdates: Building a message with resource record %s, next %p, state %d, ttl %d", ARDisplayString(m, rr), next, rr->state, rr->resrec.rroriginalttl);
+			if (!(next = BuildUpdateMessage(m, next, rr, limit)))
+				{
+				// We calculated the space and if we can't fit in, we had some bug in the calculation,
+				// disable merge completely.
+				LogMsg("SendGroupUpdates: ptr NULL while building message with %s", ARDisplayString(m, rr));
+				RRMergeFailure(m);
+				return mDNSfalse;
+				}
+			// If our estimate was higher, adjust to the actual size
+			if ((next - oldnext) > rrSize)
+				LogMsg("SendGroupUpdates: ERROR!! Record size estimation is wrong for %s, Estimate %d, Actual %d, state %d", ARDisplayString(m, rr), rrSize, next - oldnext, rr->state); 
+			else { spaceleft += rrSize; spaceleft -= (next - oldnext); }
+
+			nrecords++;
+			// We could have sent an update earlier with this "rr" as anchorRR for which we never got a response.
+			// To preserve ordering, we blow away the previous connection before sending this.
+			if (rr->tcp) { DisposeTCPConn(rr->tcp); rr->tcp = mDNSNULL;}
+			rr->updateid = msgid;
+	
+			// By setting the retry time interval here, we will not be looking at these records
+			// again when we return to CheckGroupRecordUpdates.
+			SetRecordRetry(m, rr, 0);
+			}
+			// Either we have parsed all the records or stopped at "rr" above due to lack of space
+			startRR = rr;
+		}
+
+	if (anchorRR)
+		{
+		LogInfo("SendGroupUpdates: Parsed %d records and sending using %s", nrecords, ARDisplayString(m, anchorRR));
+		SendGroupRRMessage(m, anchorRR, next, AuthInfo);
+		}
+	return sentallRecords;
+	}
+
+// Merge the record registrations and send them as a group only if they
+// have same DomainAuthInfo and hence the same key to put the TSIG
+mDNSlocal void CheckGroupRecordUpdates(mDNS *const m)
+	{
+	AuthRecord *rr, *nextRR;
+	// Keep sending as long as there is at least one record to be sent
+	while (MarkRRForSending(m))
+		{
+		if (!SendGroupUpdates(m))
+			{
+			// if everything that was marked was not sent, send them out individually
+			for (rr = m->ResourceRecords; rr; rr = nextRR)
+				{
+				// SendRecordRegistrtion might delete the rr from list, hence
+				// dereference nextRR before calling the function
+				nextRR = rr->next;
+				if (rr->SendRNow == mDNSInterfaceMark)
+					{
+					// Any records marked for sending should be eligible to be sent out
+					// immediately. Just being cautious
+					if (rr->LastAPTime + rr->ThisAPInterval - m->timenow > 0)
+						{ LogMsg("CheckGroupRecordUpdates: ERROR!! Resourcerecord %s not ready", ARDisplayString(m, rr)); continue; }
+					rr->SendRNow = mDNSNULL;
+					SendRecordRegistration(m, rr);
+					}
+				}
+			}
+		}
+		
+	debugf("CheckGroupRecordUpdates: No work, returning");
+	return;
+	}
+
+mDNSlocal void hndlSRVChanged(mDNS *const m, AuthRecord *rr)
+	{
+	// Reevaluate the target always as NAT/Target could have changed while
+	// we were registering/deeregistering
+	domainname *dt;
+	const domainname *target = GetServiceTarget(m, rr);
+	if (!target || target->c[0] == 0)
+		{
+		// we don't have a target, if we just derregistered, then we don't have to do anything
+		if (rr->state == regState_DeregPending)
+			{
+			LogInfo("hndlSRVChanged: SRVChanged, No Target, SRV Deregistered for %##s, state %d", rr->resrec.name->c,
+				rr->state);
+			rr->SRVChanged = mDNSfalse;
+			dt = GetRRDomainNameTarget(&rr->resrec);
+			if (dt) dt->c[0] = 0;
+			rr->state = regState_NoTarget;	// Wait for the next target change
+			rr->resrec.rdlength = rr->resrec.rdestimate = 0;
+			return;
+			}
+	
+		// we don't have a target, if we just registered, we need to deregister
+		if (rr->state == regState_Pending)
+			{
+			LogInfo("hndlSRVChanged: SRVChanged, No Target, Deregistering again %##s, state %d", rr->resrec.name->c, rr->state);
+			rr->ThisAPInterval = INIT_RECORD_REG_INTERVAL;
+			rr->LastAPTime = m->timenow - INIT_RECORD_REG_INTERVAL;
+			rr->state = regState_DeregPending;
+			return;
+			}
+		LogInfo("hndlSRVChanged: Not in DeregPending or RegPending state %##s, state %d", rr->resrec.name->c, rr->state);
+		}
+	else
+		{
+		// If we were in registered state and SRV changed to NULL, we deregister and come back here
+		// if we have a target, we need to register again.
+		//
+		// if we just registered check to see if it is same. If it is different just re-register the
+		// SRV and its assoicated records
+		//
+		// UpdateOneSRVRecord takes care of re-registering all service records
+		if ((rr->state == regState_DeregPending) ||
+		   (rr->state == regState_Pending && !SameDomainName(target, &rr->resrec.rdata->u.srv.target)))
+			{
+			dt = GetRRDomainNameTarget(&rr->resrec);
+			if (dt) dt->c[0] = 0;
+			rr->state = regState_NoTarget;	// NoTarget will allow us to pick up new target OR nat traversal state
+			rr->resrec.rdlength = rr->resrec.rdestimate = 0;
+			LogInfo("hndlSRVChanged: SRVChanged, Valid Target %##s, Registering all records for %##s, state %d",
+				target->c, rr->resrec.name->c, rr->state);
+			rr->SRVChanged = mDNSfalse;
+			UpdateOneSRVRecord(m, rr);
+			return;
+			}
+		// Target did not change while this record was registering. Hence, we go to
+		// Registered state - the state we started from.
+		if (rr->state == regState_Pending) rr->state = regState_Registered;
+		}
+	
+	rr->SRVChanged = mDNSfalse;
+	}
+
+// Called with lock held
+mDNSlocal void hndlRecordUpdateReply(mDNS *m, AuthRecord *rr, mStatus err, mDNSu32 random)
+	{
+	mDNSBool InvokeCallback = mDNStrue;
+	mDNSIPPort UpdatePort = zeroIPPort;
+
+	if (m->mDNS_busy != m->mDNS_reentrancy+1)
+		LogMsg("hndlRecordUpdateReply: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
+
+	LogInfo("hndlRecordUpdateReply: err %d ID %d state %d %s(%p)", err, mDNSVal16(rr->updateid), rr->state, ARDisplayString(m, rr), rr);
+
+	rr->updateError = err;
+#if APPLE_OSX_mDNSResponder
+	if (err == mStatus_BadSig || err == mStatus_BadKey) UpdateAutoTunnelDomainStatuses(m);
+#endif
+
+	SetRecordRetry(m, rr, random);
+
+	rr->updateid = zeroID;	// Make sure that this is not considered as part of a group anymore
+	// Later when need to send an update, we will get the zone data again. Thus we avoid
+	// using stale information.
+	//
+	// Note: By clearing out the zone info here, it also helps better merging of records
+	// in some cases. For example, when we get out regState_NoTarget state e.g., move out
+	// of Double NAT, we want all the records to be in one update. Some BTMM records like
+	// _autotunnel6 and host records are registered/deregistered when NAT state changes.
+	// As they are re-registered the zone information is cleared out. To merge with other
+	// records that might be possibly going out, clearing out the information here helps
+	// as all of them try to get the zone data.
+	if (rr->nta)
+		{
+		// We always expect the question to be stopped when we get a valid response from the server.
+		// If the zone info tries to change during this time, updateid would be different and hence
+		// this response should not have been accepted.
+		if (rr->nta->question.ThisQInterval != -1)
+			LogMsg("hndlRecordUpdateReply: ResourceRecord %s, zone info question %##s (%s) interval %d not -1",
+				ARDisplayString(m, rr), rr->nta->question.qname.c, DNSTypeName(rr->nta->question.qtype), rr->nta->question.ThisQInterval);
+		UpdatePort = rr->nta->Port;
+		CancelGetZoneData(m, rr->nta);
+		rr->nta = mDNSNULL;
+		}
+
+	// If we are deregistering the record, then complete the deregistration. Ignore any NAT/SRV change
+	// that could have happened during that time.
+	if (rr->resrec.RecordType == kDNSRecordTypeDeregistering && rr->state == regState_DeregPending)
+		{
+		debugf("hndlRecordUpdateReply: Received reply for deregister record %##s type %d", rr->resrec.name->c, rr->resrec.rrtype);
+		if (err) LogMsg("ERROR: Deregistration of record %##s type %d failed with error %d",
+						rr->resrec.name->c, rr->resrec.rrtype, err);
+		rr->state = regState_Unregistered;
+		CompleteDeregistration(m, rr);
+		return;
+		}
+
+	// We are returning early without updating the state. When we come back from sleep we will re-register after
+	// re-initializing all the state as though it is a first registration. If the record can't be registered e.g.,
+	// no target, it will be deregistered. Hence, the updating to the right state should not matter when going
+	// to sleep.
+	if (m->SleepState)
+		{
+		// Need to set it to NoTarget state so that RecordReadyForSleep knows that
+		// we are done
+		if (rr->resrec.rrtype == kDNSType_SRV && rr->state == regState_DeregPending)
+			rr->state = regState_NoTarget;
+		return;
+		}
+
+	if (rr->state == regState_UpdatePending)
+		{
+		if (err) LogMsg("Update record failed for %##s (err %d)", rr->resrec.name->c, err);
+		rr->state = regState_Registered;
+		// deallocate old RData
+		if (rr->UpdateCallback) rr->UpdateCallback(m, rr, rr->OrigRData, rr->OrigRDLen);
+		SetNewRData(&rr->resrec, rr->InFlightRData, rr->InFlightRDLen);
+		rr->OrigRData = mDNSNULL;
+		rr->InFlightRData = mDNSNULL;
+		}
+
+	if (rr->SRVChanged)
+		{
+		if (rr->resrec.rrtype == kDNSType_SRV)
+			hndlSRVChanged(m, rr);
+		else
+			{
+			LogInfo("hndlRecordUpdateReply: Deregistered %##s (%s), state %d", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), rr->state);
+			rr->SRVChanged = mDNSfalse; 
+			if (rr->state != regState_DeregPending) LogMsg("hndlRecordUpdateReply: ResourceRecord %s not in DeregPending state %d", ARDisplayString(m, rr), rr->state);
+			rr->state = regState_NoTarget;	// Wait for the next target change
+			}
+		return;
+		}
+		
+	if (rr->state == regState_Pending || rr->state == regState_Refresh)
+		{
+		if (!err)
+			{
+			if (rr->state == regState_Refresh) InvokeCallback = mDNSfalse;
+			rr->state = regState_Registered;
+			}
+		else
+			{
+			// Retry without lease only for non-Private domains
+			LogMsg("hndlRecordUpdateReply: Registration of record %##s type %d failed with error %d", rr->resrec.name->c, rr->resrec.rrtype, err);
+			if (!rr->Private && rr->uselease && err == mStatus_UnknownErr && mDNSSameIPPort(UpdatePort, UnicastDNSPort))
+				{
+				LogMsg("hndlRecordUpdateReply: Will retry update of record %##s without lease option", rr->resrec.name->c);
+				rr->uselease = mDNSfalse;
+				rr->ThisAPInterval = INIT_RECORD_REG_INTERVAL;
+				rr->LastAPTime = m->timenow - INIT_RECORD_REG_INTERVAL;
+				return;
+				}
+			// Communicate the error to the application in the callback below
+			}
+		}
+
+	if (rr->QueuedRData && rr->state == regState_Registered)
+		{
+		rr->state = regState_UpdatePending;
+		rr->InFlightRData = rr->QueuedRData;
+		rr->InFlightRDLen = rr->QueuedRDLen;
+		rr->OrigRData = rr->resrec.rdata;
+		rr->OrigRDLen = rr->resrec.rdlength;
+		rr->QueuedRData = mDNSNULL;
+		rr->ThisAPInterval = INIT_RECORD_REG_INTERVAL;
+		rr->LastAPTime = m->timenow - INIT_RECORD_REG_INTERVAL;
+		return;
+		}
+
+	// Don't invoke the callback on error as this may not be useful to the client.
+	// The client may potentially delete the resource record on error which we normally
+	// delete during deregistration
+	if (!err && InvokeCallback && rr->RecordCallback)
+		{
+		LogInfo("hndlRecordUpdateReply: Calling record callback on %##s", rr->resrec.name->c);
+		mDNS_DropLockBeforeCallback();
+		rr->RecordCallback(m, rr, err);
+		mDNS_ReclaimLockAfterCallback();
+		}
+	// CAUTION: MUST NOT do anything more with rr after calling rr->Callback(), because the client's callback function
+	// is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc.
+	}
+
+mDNSexport void uDNS_ReceiveNATPMPPacket(mDNS *m, const mDNSInterfaceID InterfaceID, mDNSu8 *pkt, mDNSu16 len)
+	{
+	NATTraversalInfo *ptr;
+	NATAddrReply     *AddrReply    = (NATAddrReply    *)pkt;
+	NATPortMapReply  *PortMapReply = (NATPortMapReply *)pkt;
+	mDNSu32 nat_elapsed, our_elapsed;
+
+	// Minimum packet is vers (1) opcode (1) err (2) upseconds (4) = 8 bytes
+	if (!AddrReply->err && len < 8) { LogMsg("NAT Traversal message too short (%d bytes)", len); return; }
+	if (AddrReply->vers != NATMAP_VERS) { LogMsg("Received NAT Traversal response with version %d (expected %d)", pkt[0], NATMAP_VERS); return; }
+
+	// Read multi-byte numeric values (fields are identical in a NATPortMapReply)
+	AddrReply->err       = (mDNSu16) (                                                (mDNSu16)pkt[2] << 8 | pkt[3]);
+	AddrReply->upseconds = (mDNSs32) ((mDNSs32)pkt[4] << 24 | (mDNSs32)pkt[5] << 16 | (mDNSs32)pkt[6] << 8 | pkt[7]);
+
+	nat_elapsed = AddrReply->upseconds - m->LastNATupseconds;
+	our_elapsed = (m->timenow - m->LastNATReplyLocalTime) / mDNSPlatformOneSecond;
+	debugf("uDNS_ReceiveNATPMPPacket %X upseconds %u nat_elapsed %d our_elapsed %d", AddrReply->opcode, AddrReply->upseconds, nat_elapsed, our_elapsed);
+
+	// We compute a conservative estimate of how much the NAT gateways's clock should have advanced
+	// 1. We subtract 12.5% from our own measured elapsed time, to allow for NAT gateways that have an inacurate clock that runs slowly
+	// 2. We add a two-second safety margin to allow for rounding errors: e.g.
+	//    -- if NAT gateway sends a packet at t=2.000 seconds, then one at t=7.999, that's approximately 6 real seconds,
+	//       but based on the values in the packet (2,7) the apparent difference according to the packet is only 5 seconds
+	//    -- if we're slow handling packets and/or we have coarse clock granularity,
+	//       we could receive the t=2 packet at our t=1.999 seconds, which we round down to 1
+	//       and the t=7.999 packet at our t=8.000 seconds, which we record as 8,
+	//       giving an apparent local time difference of 7 seconds
+	//    The two-second safety margin coves this possible calculation discrepancy
+	if (AddrReply->upseconds < m->LastNATupseconds || nat_elapsed + 2 < our_elapsed - our_elapsed/8)
+		{ LogMsg("NAT gateway %#a rebooted", &m->Router); RecreateNATMappings(m); }
+
+	m->LastNATupseconds      = AddrReply->upseconds;
+	m->LastNATReplyLocalTime = m->timenow;
+#ifdef _LEGACY_NAT_TRAVERSAL_
+	LNT_ClearState(m);
+#endif // _LEGACY_NAT_TRAVERSAL_
+
+	if (AddrReply->opcode == NATOp_AddrResponse)
+		{
+#if APPLE_OSX_mDNSResponder
+		static char msgbuf[16];
+		mDNS_snprintf(msgbuf, sizeof(msgbuf), "%d", AddrReply->err);
+		mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.natpmp.AddressRequest", AddrReply->err ? "failure" : "success", msgbuf, "");
+#endif
+		if (!AddrReply->err && len < sizeof(NATAddrReply)) { LogMsg("NAT Traversal AddrResponse message too short (%d bytes)", len); return; }
+		natTraversalHandleAddressReply(m, AddrReply->err, AddrReply->ExtAddr);
+		}
+	else if (AddrReply->opcode == NATOp_MapUDPResponse || AddrReply->opcode == NATOp_MapTCPResponse)
+		{
+		mDNSu8 Protocol = AddrReply->opcode & 0x7F;
+#if APPLE_OSX_mDNSResponder
+		static char msgbuf[16];
+		mDNS_snprintf(msgbuf, sizeof(msgbuf), "%s - %d", AddrReply->opcode == NATOp_MapUDPResponse ? "UDP" : "TCP", PortMapReply->err);
+		mDNSASLLog((uuid_t *)&m->asl_uuid, "natt.natpmp.PortMapRequest", PortMapReply->err ? "failure" : "success", msgbuf, "");
+#endif
+		if (!PortMapReply->err)
+			{
+			if (len < sizeof(NATPortMapReply)) { LogMsg("NAT Traversal PortMapReply message too short (%d bytes)", len); return; }
+			PortMapReply->NATRep_lease = (mDNSu32) ((mDNSu32)pkt[12] << 24 | (mDNSu32)pkt[13] << 16 | (mDNSu32)pkt[14] << 8 | pkt[15]);
+			}
+
+		// Since some NAT-PMP server implementations don't return the requested internal port in
+		// the reply, we can't associate this reply with a particular NATTraversalInfo structure.
+		// We globally keep track of the most recent error code for mappings.
+		m->LastNATMapResultCode = PortMapReply->err;
+		
+		for (ptr = m->NATTraversals; ptr; ptr=ptr->next)
+			if (ptr->Protocol == Protocol && mDNSSameIPPort(ptr->IntPort, PortMapReply->intport))
+				natTraversalHandlePortMapReply(m, ptr, InterfaceID, PortMapReply->err, PortMapReply->extport, PortMapReply->NATRep_lease);
+		}
+	else { LogMsg("Received NAT Traversal response with version unknown opcode 0x%X", AddrReply->opcode); return; }
+
+	// Don't need an SSDP socket if we get a NAT-PMP packet
+	if (m->SSDPSocket) { debugf("uDNS_ReceiveNATPMPPacket destroying SSDPSocket %p", &m->SSDPSocket); mDNSPlatformUDPClose(m->SSDPSocket); m->SSDPSocket = mDNSNULL; }
+	}
+
+// <rdar://problem/3925163> Shorten DNS-SD queries to avoid NAT bugs
+// <rdar://problem/4288449> Add check to avoid crashing NAT gateways that have buggy DNS relay code
+//
+// We know of bugs in home NAT gateways that cause them to crash if they receive certain DNS queries.
+// The DNS queries that make them crash are perfectly legal DNS queries, but even if they weren't,
+// the gateway shouldn't crash -- in today's world of viruses and network attacks, software has to
+// be written assuming that a malicious attacker could send them any packet, properly-formed or not.
+// Still, we don't want to be crashing people's home gateways, so we go out of our way to avoid
+// the queries that crash them.
+//
+// Some examples:
+//
+// 1. Any query where the name ends in ".in-addr.arpa." and the text before this is 32 or more bytes.
+//    The query type does not need to be PTR -- the gateway will crash for any query type.
+//    e.g. "ping long-name-crashes-the-buggy-router.in-addr.arpa" will crash one of these.
+//
+// 2. Any query that results in a large response with the TC bit set.
+//
+// 3. Any PTR query that doesn't begin with four decimal numbers.
+//    These gateways appear to assume that the only possible PTR query is a reverse-mapping query
+//    (e.g. "1.0.168.192.in-addr.arpa") and if they ever get a PTR query where the first four
+//    labels are not all decimal numbers in the range 0-255, they handle that by crashing.
+//    These gateways also ignore the remainder of the name following the four decimal numbers
+//    -- whether or not it actually says in-addr.arpa, they just make up an answer anyway.
+//
+// The challenge therefore is to craft a query that will discern whether the DNS server
+// is one of these buggy ones, without crashing it. Furthermore we don't want our test
+// queries making it all the way to the root name servers, putting extra load on those
+// name servers and giving Apple a bad reputation. To this end we send this query:
+//     dig -t ptr 1.0.0.127.dnsbugtest.1.0.0.127.in-addr.arpa.
+//
+// The text preceding the ".in-addr.arpa." is under 32 bytes, so it won't cause crash (1).
+// It will not yield a large response with the TC bit set, so it won't cause crash (2).
+// It starts with four decimal numbers, so it won't cause crash (3).
+// The name falls within the "1.0.0.127.in-addr.arpa." domain, the reverse-mapping name for the local
+// loopback address, and therefore the query will black-hole at the first properly-configured DNS server
+// it reaches, making it highly unlikely that this query will make it all the way to the root.
+//
+// Finally, the correct response to this query is NXDOMAIN or a similar error, but the
+// gateways that ignore the remainder of the name following the four decimal numbers
+// give themselves away by actually returning a result for this nonsense query.
+
+mDNSlocal const domainname *DNSRelayTestQuestion = (const domainname*)
+	"\x1" "1" "\x1" "0" "\x1" "0" "\x3" "127" "\xa" "dnsbugtest"
+	"\x1" "1" "\x1" "0" "\x1" "0" "\x3" "127" "\x7" "in-addr" "\x4" "arpa";
+
+// See comments above for DNSRelayTestQuestion
+// If this is the kind of query that has the risk of crashing buggy DNS servers, we do a test question first
+mDNSlocal mDNSBool NoTestQuery(DNSQuestion *q)
+	{
+	int i;
+	mDNSu8 *p = q->qname.c;
+	if (q->AuthInfo) return(mDNStrue);		// Don't need a test query for private queries sent directly to authoritative server over TLS/TCP
+	if (q->qtype != kDNSType_PTR) return(mDNStrue);		// Don't need a test query for any non-PTR queries
+	for (i=0; i<4; i++)		// If qname does not begin with num.num.num.num, can't skip the test query
+		{
+		if (p[0] < 1 || p[0] > 3) return(mDNSfalse);
+		if (              p[1] < '0' || p[1] > '9' ) return(mDNSfalse);
+		if (p[0] >= 2 && (p[2] < '0' || p[2] > '9')) return(mDNSfalse);
+		if (p[0] >= 3 && (p[3] < '0' || p[3] > '9')) return(mDNSfalse);
+		p += 1 + p[0];
+		}
+	// If remainder of qname is ".in-addr.arpa.", this is a vanilla reverse-mapping query and
+	// we can safely do it without needing a test query first, otherwise we need the test query.
+	return(SameDomainName((domainname*)p, (const domainname*)"\x7" "in-addr" "\x4" "arpa"));
+	}
+
+// Returns mDNStrue if response was handled
+mDNSlocal mDNSBool uDNS_ReceiveTestQuestionResponse(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end,
+	const mDNSAddr *const srcaddr, const mDNSIPPort srcport)
+	{
+	const mDNSu8 *ptr = msg->data;
+	DNSQuestion pktq;
+	DNSServer *s;
+	mDNSu32 result = 0;
+
+	// 1. Find out if this is an answer to one of our test questions
+	if (msg->h.numQuestions != 1) return(mDNSfalse);
+	ptr = getQuestion(msg, ptr, end, mDNSInterface_Any, &pktq);
+	if (!ptr) return(mDNSfalse);
+	if (pktq.qtype != kDNSType_PTR || pktq.qclass != kDNSClass_IN) return(mDNSfalse);
+	if (!SameDomainName(&pktq.qname, DNSRelayTestQuestion)) return(mDNSfalse);
+
+	// 2. If the DNS relay gave us a positive response, then it's got buggy firmware
+	// else, if the DNS relay gave us an error or no-answer response, it passed our test
+	if ((msg->h.flags.b[1] & kDNSFlag1_RC_Mask) == kDNSFlag1_RC_NoErr && msg->h.numAnswers > 0)
+		result = DNSServer_Failed;
+	else
+		result = DNSServer_Passed;
+
+	// 3. Find occurrences of this server in our list, and mark them appropriately
+	for (s = m->DNSServers; s; s = s->next)
+		{
+		mDNSBool matchaddr = (s->teststate != result && mDNSSameAddress(srcaddr, &s->addr) && mDNSSameIPPort(srcport, s->port));
+		mDNSBool matchid   = (s->teststate == DNSServer_Untested && mDNSSameOpaque16(msg->h.id, s->testid));
+		if (matchaddr || matchid)
+			{
+			DNSQuestion *q;
+			s->teststate = result;
+			if (result == DNSServer_Passed)
+				{
+				LogInfo("DNS Server %#a:%d (%#a:%d) %d passed%s",
+					&s->addr, mDNSVal16(s->port), srcaddr, mDNSVal16(srcport), mDNSVal16(s->testid),
+					matchaddr ? "" : " NOTE: Reply did not come from address to which query was sent");
+				}
+			else
+				{
+				LogMsg("NOTE: Wide-Area Service Discovery disabled to avoid crashing defective DNS relay %#a:%d (%#a:%d) %d%s",
+					&s->addr, mDNSVal16(s->port), srcaddr, mDNSVal16(srcport), mDNSVal16(s->testid),
+					matchaddr ? "" : " NOTE: Reply did not come from address to which query was sent");
+				}
+
+			// If this server has just changed state from DNSServer_Untested to DNSServer_Passed, then retrigger any waiting questions.
+			// We use the NoTestQuery() test so that we only retrigger questions that were actually blocked waiting for this test to complete.
+			if (result == DNSServer_Passed)		// Unblock any questions that were waiting for this result
+				for (q = m->Questions; q; q=q->next)
+					if (q->qDNSServer == s && !NoTestQuery(q))
+						{
+						q->ThisQInterval = INIT_UCAST_POLL_INTERVAL / QuestionIntervalStep;
+						q->unansweredQueries = 0;
+						q->LastQTime = m->timenow - q->ThisQInterval;
+						m->NextScheduledQuery = m->timenow;
+						}
+			}
+		}
+
+	return(mDNStrue); // Return mDNStrue to tell uDNS_ReceiveMsg it doesn't need to process this packet further
+	}
+
+// Called from mDNSCoreReceive with the lock held
+mDNSexport void uDNS_ReceiveMsg(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *const srcaddr, const mDNSIPPort srcport)
+	{
+	DNSQuestion *qptr;
+	mStatus err = mStatus_NoError;
+
+	mDNSu8 StdR    = kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery;
+	mDNSu8 UpdateR = kDNSFlag0_QR_Response | kDNSFlag0_OP_Update;
+	mDNSu8 QR_OP   = (mDNSu8)(msg->h.flags.b[0] & kDNSFlag0_QROP_Mask);
+	mDNSu8 rcode   = (mDNSu8)(msg->h.flags.b[1] & kDNSFlag1_RC_Mask);
+
+	(void)srcport; // Unused
+
+	debugf("uDNS_ReceiveMsg from %#-15a with "
+		"%2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s %d bytes",
+		srcaddr,
+		msg->h.numQuestions,   msg->h.numQuestions   == 1 ? ", "   : "s,",
+		msg->h.numAnswers,     msg->h.numAnswers     == 1 ? ", "   : "s,",
+		msg->h.numAuthorities, msg->h.numAuthorities == 1 ? "y,  " : "ies,",
+		msg->h.numAdditionals, msg->h.numAdditionals == 1 ? ""     : "s", end - msg->data);
+
+	if (QR_OP == StdR)
+		{
+		//if (srcaddr && recvLLQResponse(m, msg, end, srcaddr, srcport)) return;
+		if (uDNS_ReceiveTestQuestionResponse(m, msg, end, srcaddr, srcport)) return;
+		for (qptr = m->Questions; qptr; qptr = qptr->next)
+			if (msg->h.flags.b[0] & kDNSFlag0_TC && mDNSSameOpaque16(qptr->TargetQID, msg->h.id) && m->timenow - qptr->LastQTime < RESPONSE_WINDOW)
+				{
+				if (!srcaddr) LogMsg("uDNS_ReceiveMsg: TCP DNS response had TC bit set: ignoring");
+				else
+					{
+					// Don't reuse TCP connections. We might have failed over to a different DNS server
+					// while the first TCP connection is in progress. We need a new TCP connection to the
+					// new DNS server. So, always try to establish a new connection.
+					if (qptr->tcp) { DisposeTCPConn(qptr->tcp); qptr->tcp = mDNSNULL; }
+					qptr->tcp = MakeTCPConn(m, mDNSNULL, mDNSNULL, kTCPSocketFlags_Zero, srcaddr, srcport, mDNSNULL, qptr, mDNSNULL);
+					}
+				}
+		}
+
+	if (QR_OP == UpdateR)
+		{
+		mDNSu32 lease = GetPktLease(m, msg, end);
+		mDNSs32 expire = m->timenow + (mDNSs32)lease * mDNSPlatformOneSecond;
+		mDNSu32 random = mDNSRandom((mDNSs32)lease * mDNSPlatformOneSecond/10);
+
+		//rcode = kDNSFlag1_RC_ServFail;	// Simulate server failure (rcode 2)
+
+		// Walk through all the records that matches the messageID. There could be multiple
+		// records if we had sent them in a group
+		if (m->CurrentRecord)
+			LogMsg("uDNS_ReceiveMsg ERROR m->CurrentRecord already set %s", ARDisplayString(m, m->CurrentRecord));
+		m->CurrentRecord = m->ResourceRecords;
+		while (m->CurrentRecord)
+			{
+			AuthRecord *rptr = m->CurrentRecord;
+			m->CurrentRecord = m->CurrentRecord->next;
+			if (AuthRecord_uDNS(rptr) && mDNSSameOpaque16(rptr->updateid, msg->h.id))
+				{
+				err = checkUpdateResult(m, rptr->resrec.name, rcode, msg, end);
+				if (!err && rptr->uselease && lease)
+					if (rptr->expire - expire >= 0 || rptr->state != regState_UpdatePending)
+						{
+						rptr->expire = expire;
+						rptr->refreshCount = 0;
+						}
+				// We pass the random value to make sure that if we update multiple
+				// records, they all get the same random value
+				hndlRecordUpdateReply(m, rptr, err, random);
+				}
+			}
+		}
+	debugf("Received unexpected response: ID %d matches no active records", mDNSVal16(msg->h.id));
+	}
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark - Query Routines
+#endif
+
+mDNSexport void sendLLQRefresh(mDNS *m, DNSQuestion *q)
+	{
+	mDNSu8 *end;
+	LLQOptData llq;
+	mDNSu8 *limit = m->omsg.data + AbsoluteMaxDNSMessageData;
+
+	if (q->ReqLease)
+		if ((q->state == LLQ_Established && q->ntries >= kLLQ_MAX_TRIES) || q->expire - m->timenow < 0)
+			{
+			LogMsg("Unable to refresh LLQ %##s (%s) - will retry in %d seconds", q->qname.c, DNSTypeName(q->qtype), LLQ_POLL_INTERVAL / mDNSPlatformOneSecond);
+			StartLLQPolling(m,q);
+			return;
+			}
+
+	llq.vers     = kLLQ_Vers;
+	llq.llqOp    = kLLQOp_Refresh;
+	llq.err      = q->tcp ? GetLLQEventPort(m, &q->servAddr) : LLQErr_NoError;	// If using TCP tell server what UDP port to send notifications to
+	llq.id       = q->id;
+	llq.llqlease = q->ReqLease;
+
+	InitializeDNSMessage(&m->omsg.h, q->TargetQID, uQueryFlags);
+	end = putLLQ(&m->omsg, m->omsg.data, q, &llq);
+	if (!end) { LogMsg("sendLLQRefresh: putLLQ failed %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); return; }
+
+	// Note that we (conditionally) add HINFO and TSIG here, since the question might be going away,
+	// so we may not be able to reference it (most importantly it's AuthInfo) when we actually send the message
+	end = putHINFO(m, &m->omsg, end, q->AuthInfo, limit);
+	if (!end) { LogMsg("sendLLQRefresh: putHINFO failed %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); return; }
+
+	if (PrivateQuery(q))
+		{
+		DNSDigest_SignMessageHostByteOrder(&m->omsg, &end, q->AuthInfo);
+		if (!end) { LogMsg("sendLLQRefresh: DNSDigest_SignMessage failed %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); return; }
+		}
+
+	if (PrivateQuery(q) && !q->tcp)
+		{
+		LogInfo("sendLLQRefresh setting up new TLS session %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+		if (!q->nta) { LogMsg("sendLLQRefresh:ERROR!! q->nta is NULL for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); return; }
+		q->tcp = MakeTCPConn(m, &m->omsg, end, kTCPSocketFlags_UseTLS, &q->servAddr, q->servPort, &q->nta->Host, q, mDNSNULL);
+		}
+	else
+		{
+		mStatus err;
+
+		// if AuthInfo and AuthInfo->AutoTunnel is set, we use the TCP socket but don't need to pass the AuthInfo as
+		// we already protected the message above.
+		LogInfo("sendLLQRefresh: using existing %s session %##s (%s)", PrivateQuery(q) ? "TLS" : "UDP",
+			q->qname.c, DNSTypeName(q->qtype));
+
+		err = mDNSSendDNSMessage(m, &m->omsg, end, mDNSInterface_Any, q->LocalSocket, &q->servAddr, q->servPort, q->tcp ? q->tcp->sock : mDNSNULL, mDNSNULL);
+		if (err)
+			{
+			LogMsg("sendLLQRefresh: mDNSSendDNSMessage%s failed: %d", q->tcp ? " (TCP)" : "", err);
+			if (q->tcp) { DisposeTCPConn(q->tcp); q->tcp = mDNSNULL; }
+			}
+		}
+
+	q->ntries++;
+
+	debugf("sendLLQRefresh ntries %d %##s (%s)", q->ntries, q->qname.c, DNSTypeName(q->qtype));
+
+	q->LastQTime = m->timenow;
+	SetNextQueryTime(m, q);
+	}
+
+mDNSexport void LLQGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneInfo)
+	{
+	DNSQuestion *q = (DNSQuestion *)zoneInfo->ZoneDataContext;
+
+	mDNS_Lock(m);
+
+	// If we get here it means that the GetZoneData operation has completed.
+	// We hold on to the zone data if it is AutoTunnel as we use the hostname
+	// in zoneInfo during the TLS connection setup.
+	q->servAddr = zeroAddr;
+	q->servPort = zeroIPPort;
+
+	if (!err && zoneInfo && !mDNSIPPortIsZero(zoneInfo->Port) && !mDNSAddressIsZero(&zoneInfo->Addr) && zoneInfo->Host.c[0])
+		{
+		q->servAddr = zoneInfo->Addr;
+		q->servPort = zoneInfo->Port;
+		if (!PrivateQuery(q))
+			{
+			// We don't need the zone data as we use it only for the Host information which we
+			// don't need if we are not going to use TLS connections.
+			if (q->nta)
+				{
+				if (q->nta != zoneInfo) LogMsg("LLQGotZoneData: nta (%p) != zoneInfo (%p)  %##s (%s)", q->nta, zoneInfo, q->qname.c, DNSTypeName(q->qtype));
+				CancelGetZoneData(m, q->nta);
+				q->nta = mDNSNULL;
+				}
+			}
+		q->ntries = 0;
+		debugf("LLQGotZoneData %#a:%d", &q->servAddr, mDNSVal16(q->servPort));
+		startLLQHandshake(m, q);
+		}
+	else
+		{
+		if (q->nta)
+			{
+			if (q->nta != zoneInfo) LogMsg("LLQGotZoneData: nta (%p) != zoneInfo (%p)  %##s (%s)", q->nta, zoneInfo, q->qname.c, DNSTypeName(q->qtype));
+			CancelGetZoneData(m, q->nta);
+			q->nta = mDNSNULL;
+			}
+		StartLLQPolling(m,q);
+		if (err == mStatus_NoSuchNameErr) 
+			{
+			// this actually failed, so mark it by setting address to all ones 
+			q->servAddr.type = mDNSAddrType_IPv4; 
+			q->servAddr.ip.v4 = onesIPv4Addr; 
+			}
+		}
+
+	mDNS_Unlock(m);
+	}
+
+// Called in normal callback context (i.e. mDNS_busy and mDNS_reentrancy are both 1)
+mDNSlocal void PrivateQueryGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneInfo)
+	{
+	DNSQuestion *q = (DNSQuestion *) zoneInfo->ZoneDataContext;
+
+	LogInfo("PrivateQueryGotZoneData %##s (%s) err %d Zone %##s Private %d", q->qname.c, DNSTypeName(q->qtype), err, zoneInfo->ZoneName.c, zoneInfo->ZonePrivate);
+
+	if (q->nta != zoneInfo) LogMsg("PrivateQueryGotZoneData:ERROR!!: nta (%p) != zoneInfo (%p)  %##s (%s)", q->nta, zoneInfo, q->qname.c, DNSTypeName(q->qtype));
+
+	if (err || !zoneInfo || mDNSAddressIsZero(&zoneInfo->Addr) || mDNSIPPortIsZero(zoneInfo->Port) || !zoneInfo->Host.c[0])
+		{
+		LogInfo("PrivateQueryGotZoneData: ERROR!! %##s (%s) invoked with error code %d %p %#a:%d",
+			q->qname.c, DNSTypeName(q->qtype), err, zoneInfo,
+			zoneInfo ? &zoneInfo->Addr : mDNSNULL,
+			zoneInfo ? mDNSVal16(zoneInfo->Port) : 0);
+		CancelGetZoneData(m, q->nta);
+		q->nta = mDNSNULL;
+		return;
+		}
+
+	if (!zoneInfo->ZonePrivate)
+		{
+		debugf("Private port lookup failed -- retrying without TLS -- %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+		q->AuthInfo      = mDNSNULL;		// Clear AuthInfo so we try again non-private
+		q->ThisQInterval = InitialQuestionInterval;
+		q->LastQTime     = m->timenow - q->ThisQInterval;
+		CancelGetZoneData(m, q->nta);
+		q->nta = mDNSNULL;
+		mDNS_Lock(m);
+		SetNextQueryTime(m, q);
+		mDNS_Unlock(m);
+		return;
+		// Next call to uDNS_CheckCurrentQuestion() will do this as a non-private query
+		}
+
+	if (!PrivateQuery(q))
+		{
+		LogMsg("PrivateQueryGotZoneData: ERROR!! Not a private query %##s (%s) AuthInfo %p", q->qname.c, DNSTypeName(q->qtype), q->AuthInfo);
+		CancelGetZoneData(m, q->nta);
+		q->nta = mDNSNULL;
+		return;
+		}
+
+	q->TargetQID = mDNS_NewMessageID(m);
+	if (q->tcp) { DisposeTCPConn(q->tcp); q->tcp = mDNSNULL; }
+	if (!q->nta) { LogMsg("PrivateQueryGotZoneData:ERROR!! nta is NULL for %##s (%s)", q->qname.c, DNSTypeName(q->qtype)); return; }
+	q->tcp = MakeTCPConn(m, mDNSNULL, mDNSNULL, kTCPSocketFlags_UseTLS, &zoneInfo->Addr, zoneInfo->Port, &q->nta->Host, q, mDNSNULL);
+	if (q->nta) { CancelGetZoneData(m, q->nta); q->nta = mDNSNULL; }
+	}
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark - Dynamic Updates
+#endif
+
+// Called in normal callback context (i.e. mDNS_busy and mDNS_reentrancy are both 1)
+mDNSexport void RecordRegistrationGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneData)
+	{
+	AuthRecord *newRR = (AuthRecord*)zoneData->ZoneDataContext;
+	AuthRecord *ptr;
+	int c1, c2;
+
+	if (newRR->nta != zoneData)
+		LogMsg("RecordRegistrationGotZoneData: nta (%p) != zoneData (%p)  %##s (%s)", newRR->nta, zoneData, newRR->resrec.name->c, DNSTypeName(newRR->resrec.rrtype));
+
+	if (m->mDNS_busy != m->mDNS_reentrancy)
+		LogMsg("RecordRegistrationGotZoneData: mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
+
+	// make sure record is still in list (!!!)
+	for (ptr = m->ResourceRecords; ptr; ptr = ptr->next) if (ptr == newRR) break;
+	if (!ptr)
+		{
+		LogMsg("RecordRegistrationGotZoneData - RR no longer in list.  Discarding.");
+		CancelGetZoneData(m, newRR->nta);
+		newRR->nta = mDNSNULL;
+		return;
+		}
+
+	// check error/result
+	if (err)
+		{
+		if (err != mStatus_NoSuchNameErr) LogMsg("RecordRegistrationGotZoneData: error %d", err);
+		CancelGetZoneData(m, newRR->nta);
+		newRR->nta = mDNSNULL;
+		return;
+		}
+
+	if (!zoneData) { LogMsg("ERROR: RecordRegistrationGotZoneData invoked with NULL result and no error"); return; }
+
+	if (newRR->resrec.rrclass != zoneData->ZoneClass)
+		{
+		LogMsg("ERROR: New resource record's class (%d) does not match zone class (%d)", newRR->resrec.rrclass, zoneData->ZoneClass);
+		CancelGetZoneData(m, newRR->nta);
+		newRR->nta = mDNSNULL;
+		return;
+		}
+
+	// Don't try to do updates to the root name server.
+	// We might be tempted also to block updates to any single-label name server (e.g. com, edu, net, etc.) but some
+	// organizations use their own private pseudo-TLD, like ".home", etc, and we don't want to block that.
+	if (zoneData->ZoneName.c[0] == 0)
+		{
+		LogInfo("RecordRegistrationGotZoneData: No name server found claiming responsibility for \"%##s\"!", newRR->resrec.name->c);
+		CancelGetZoneData(m, newRR->nta);
+		newRR->nta = mDNSNULL;
+		return;
+		}
+
+	// Store discovered zone data
+	c1 = CountLabels(newRR->resrec.name);
+	c2 = CountLabels(&zoneData->ZoneName);
+	if (c2 > c1)
+		{
+		LogMsg("RecordRegistrationGotZoneData: Zone \"%##s\" is longer than \"%##s\"", zoneData->ZoneName.c, newRR->resrec.name->c);
+		CancelGetZoneData(m, newRR->nta);
+		newRR->nta = mDNSNULL;
+		return;
+		}
+	newRR->zone = SkipLeadingLabels(newRR->resrec.name, c1-c2);
+	if (!SameDomainName(newRR->zone, &zoneData->ZoneName))
+		{
+		LogMsg("RecordRegistrationGotZoneData: Zone \"%##s\" does not match \"%##s\" for \"%##s\"", newRR->zone->c, zoneData->ZoneName.c, newRR->resrec.name->c);
+		CancelGetZoneData(m, newRR->nta);
+		newRR->nta = mDNSNULL;
+		return;
+		}
+
+	if (mDNSIPPortIsZero(zoneData->Port) || mDNSAddressIsZero(&zoneData->Addr) || !zoneData->Host.c[0])
+		{
+		LogInfo("RecordRegistrationGotZoneData: No _dns-update._udp service found for \"%##s\"!", newRR->resrec.name->c);
+		CancelGetZoneData(m, newRR->nta);
+		newRR->nta = mDNSNULL;
+		return;
+		}
+
+	newRR->Private      = zoneData->ZonePrivate;
+	debugf("RecordRegistrationGotZoneData: Set zone information for %##s %##s to %#a:%d",
+		newRR->resrec.name->c, zoneData->ZoneName.c, &zoneData->Addr, mDNSVal16(zoneData->Port));
+
+	// If we are deregistering, uDNS_DeregisterRecord will do that as it has the zone data now.
+	if (newRR->state == regState_DeregPending)
+		{
+		mDNS_Lock(m);
+		uDNS_DeregisterRecord(m, newRR);
+		mDNS_Unlock(m);
+		return;
+		}
+
+	if (newRR->resrec.rrtype == kDNSType_SRV)
+		{
+		const domainname *target;
+		// Reevaluate the target always as NAT/Target could have changed while
+		// we were fetching zone data.
+		mDNS_Lock(m);
+		target = GetServiceTarget(m, newRR);
+		mDNS_Unlock(m);
+		if (!target || target->c[0] == 0)
+			{
+			domainname *t = GetRRDomainNameTarget(&newRR->resrec);
+			LogInfo("RecordRegistrationGotZoneData - no target for %##s", newRR->resrec.name->c);
+			if (t) t->c[0] = 0;
+			newRR->resrec.rdlength = newRR->resrec.rdestimate = 0;
+			newRR->state = regState_NoTarget;
+			CancelGetZoneData(m, newRR->nta);
+			newRR->nta = mDNSNULL;
+			return;
+			}
+		}
+	// If we have non-zero service port (always?)
+	// and a private address, and update server is non-private
+	// and this service is AutoTarget
+	// then initiate a NAT mapping request. On completion it will do SendRecordRegistration() for us
+	if (newRR->resrec.rrtype == kDNSType_SRV && !mDNSIPPortIsZero(newRR->resrec.rdata->u.srv.port) &&
+		mDNSv4AddrIsRFC1918(&m->AdvertisedV4.ip.v4) && newRR->nta && !mDNSAddrIsRFC1918(&newRR->nta->Addr) &&
+		newRR->AutoTarget == Target_AutoHostAndNATMAP)
+		{
+		DomainAuthInfo *AuthInfo;
+		AuthInfo = GetAuthInfoForName(m, newRR->resrec.name);
+		if (AuthInfo && AuthInfo->AutoTunnel)
+			{
+			domainname *t = GetRRDomainNameTarget(&newRR->resrec);
+			LogMsg("RecordRegistrationGotZoneData: ERROR!! AutoTunnel has Target_AutoHostAndNATMAP for %s", ARDisplayString(m, newRR));
+			if (t) t->c[0] = 0;
+			newRR->resrec.rdlength = newRR->resrec.rdestimate = 0;
+			newRR->state = regState_NoTarget;
+			CancelGetZoneData(m, newRR->nta);
+			newRR->nta = mDNSNULL;
+			return;
+			}
+		// During network transitions, we are called multiple times in different states. Setup NAT
+		// state just once for this record.
+		if (!newRR->NATinfo.clientContext)
+			{
+			LogInfo("RecordRegistrationGotZoneData StartRecordNatMap %s", ARDisplayString(m, newRR));
+			newRR->state = regState_NATMap;
+			StartRecordNatMap(m, newRR);
+			return;
+			}
+		else LogInfo("RecordRegistrationGotZoneData: StartRecordNatMap for %s, state %d, context %p", ARDisplayString(m, newRR), newRR->state, newRR->NATinfo.clientContext);
+		}
+	mDNS_Lock(m);
+	// We want IsRecordMergeable to check whether it is a record whose update can be
+	// sent with others. We set the time before we call IsRecordMergeable, so that
+	// it does not fail this record based on time. We are interested in other checks
+	// at this time. If a previous update resulted in error, then don't reset the
+	// interval. Preserve the back-off so that we don't keep retrying aggressively.
+	if (newRR->updateError == mStatus_NoError)
+		{
+		newRR->ThisAPInterval = INIT_RECORD_REG_INTERVAL;
+		newRR->LastAPTime = m->timenow - INIT_RECORD_REG_INTERVAL;
+		}
+	if (IsRecordMergeable(m, newRR, m->timenow + MERGE_DELAY_TIME))
+		{
+		// Delay the record registration by MERGE_DELAY_TIME so that we can merge them
+		// into one update
+		LogInfo("RecordRegistrationGotZoneData: Delayed registration for %s", ARDisplayString(m, newRR));
+		newRR->LastAPTime += MERGE_DELAY_TIME;
+		}
+	mDNS_Unlock(m);
+	}
+
+mDNSlocal void SendRecordDeregistration(mDNS *m, AuthRecord *rr)
+	{
+	mDNSu8 *ptr = m->omsg.data;
+	mDNSu8 *limit;
+	DomainAuthInfo *AuthInfo;
+
+	if (m->mDNS_busy != m->mDNS_reentrancy+1)
+		LogMsg("SendRecordDeRegistration: Lock not held! mDNS_busy (%ld) mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
+
+	if (!rr->nta || mDNSIPv4AddressIsZero(rr->nta->Addr.ip.v4))
+		{
+		LogMsg("SendRecordDeRegistration: No zone info for Resource record %s RecordType %d", ARDisplayString(m, rr), rr->resrec.RecordType);
+		return;
+		}
+
+	limit = ptr + AbsoluteMaxDNSMessageData;
+	AuthInfo = GetAuthInfoForName_internal(m, rr->resrec.name);
+	limit -= RRAdditionalSize(m, AuthInfo);
+
+	rr->updateid = mDNS_NewMessageID(m);
+	InitializeDNSMessage(&m->omsg.h, rr->updateid, UpdateReqFlags);
+
+	// set zone
+	ptr = putZone(&m->omsg, ptr, limit, rr->zone, mDNSOpaque16fromIntVal(rr->resrec.rrclass));
+	if (!ptr) goto exit;
+
+	ptr = BuildUpdateMessage(m, ptr, rr, limit);
+
+	if (!ptr) goto exit;
+
+	if (rr->Private)
+		{
+		LogInfo("SendRecordDeregistration TCP %p %s", rr->tcp, ARDisplayString(m, rr));
+		if (rr->tcp) LogInfo("SendRecordDeregistration: Disposing existing TCP connection for %s", ARDisplayString(m, rr));
+		if (rr->tcp) { DisposeTCPConn(rr->tcp); rr->tcp = mDNSNULL; }
+		if (!rr->nta) { LogMsg("SendRecordDeregistration:Private:ERROR!! nta is NULL for %s", ARDisplayString(m, rr)); return; }
+		rr->tcp = MakeTCPConn(m, &m->omsg, ptr, kTCPSocketFlags_UseTLS, &rr->nta->Addr, rr->nta->Port, &rr->nta->Host, mDNSNULL, rr);
+		}
+	else
+		{
+		mStatus err;
+		LogInfo("SendRecordDeregistration UDP %s", ARDisplayString(m, rr));
+		if (!rr->nta) { LogMsg("SendRecordDeregistration:ERROR!! nta is NULL for %s", ARDisplayString(m, rr)); return; }
+		err = mDNSSendDNSMessage(m, &m->omsg, ptr, mDNSInterface_Any, mDNSNULL, &rr->nta->Addr, rr->nta->Port, mDNSNULL, GetAuthInfoForName_internal(m, rr->resrec.name));
+		if (err) debugf("ERROR: SendRecordDeregistration - mDNSSendDNSMessage - %d", err);
+		//if (rr->state == regState_DeregPending) CompleteDeregistration(m, rr);		// Don't touch rr after this
+		}
+	SetRecordRetry(m, rr, 0);
+	return;
+exit:
+	LogMsg("SendRecordDeregistration: Error formatting message for %s", ARDisplayString(m, rr));
+	}
+
+mDNSexport mStatus uDNS_DeregisterRecord(mDNS *const m, AuthRecord *const rr)
+	{
+	DomainAuthInfo *info;
+
+	LogInfo("uDNS_DeregisterRecord: Resource Record %s, state %d", ARDisplayString(m, rr), rr->state);
+
+	switch (rr->state)
+		{
+		case regState_Refresh:
+		case regState_Pending:
+		case regState_UpdatePending:
+		case regState_Registered: break;
+		case regState_DeregPending: break;
+
+		case regState_NATError: 
+		case regState_NATMap:
+		// A record could be in NoTarget to start with if the corresponding SRV record could not find a target.
+		// It is also possible to reenter the NoTarget state when we move to a network with a NAT that has
+		// no NAT-PMP/UPnP support. In that case before we entered NoTarget, we already deregistered with
+		// the server.
+		case regState_NoTarget: 
+		case regState_Unregistered:
+		case regState_Zero:
+		default:
+			LogInfo("uDNS_DeregisterRecord: State %d for %##s type %s", rr->state, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
+			// This function may be called during sleep when there are no sleep proxy servers
+			if (rr->resrec.RecordType == kDNSRecordTypeDeregistering) CompleteDeregistration(m, rr);
+			return mStatus_NoError;
+		}
+
+	// If a current group registration is pending, we can't send this deregisration till that registration
+	// has reached the server i.e., the ordering is important. Previously, if we did not send this
+	// registration in a group, then the previous connection will be torn down as part of sending the
+	// deregistration. If we send this in a group, we need to locate the resource record that was used
+	// to send this registration and terminate that connection. This means all the updates on that might
+	// be lost (assuming the response is not waiting for us at the socket) and the retry will send the
+	// update again sometime in the near future.
+	//
+	// NOTE: SSL handshake failures normally free the TCP connection immediately. Hence, you may not
+	// find the TCP below there. This case can happen only when tcp is trying to actively retransmit
+	// the request or SSL negotiation taking time i.e resource record is actively trying to get the
+	// message to the server. During that time a deregister has to happen.
+
+	if (!mDNSOpaque16IsZero(rr->updateid))
+		{
+		AuthRecord *anchorRR;
+		mDNSBool found = mDNSfalse;
+		for (anchorRR = m->ResourceRecords; anchorRR; anchorRR = anchorRR->next)
+			{
+			if (AuthRecord_uDNS(rr) && mDNSSameOpaque16(anchorRR->updateid, rr->updateid) && anchorRR->tcp)
+				{
+				LogInfo("uDNS_DeregisterRecord: Found Anchor RR %s terminated", ARDisplayString(m, anchorRR));
+				if (found)
+					LogMsg("uDNS_DeregisterRecord: ERROR: Another anchorRR %s found", ARDisplayString(m, anchorRR));
+				DisposeTCPConn(anchorRR->tcp);
+				anchorRR->tcp = mDNSNULL;
+				found = mDNStrue;
+				}
+			}
+		if (!found) LogInfo("uDNSDeregisterRecord: Cannot find the anchor Resource Record for %s, not an error", ARDisplayString(m, rr));
+		}
+
+	// Retry logic for deregistration should be no different from sending registration the first time.
+	// Currently ThisAPInterval most likely is set to the refresh interval
+	rr->state          = regState_DeregPending;
+	rr->ThisAPInterval = INIT_RECORD_REG_INTERVAL;
+	rr->LastAPTime     = m->timenow - INIT_RECORD_REG_INTERVAL;
+	info = GetAuthInfoForName_internal(m, rr->resrec.name);
+	if (IsRecordMergeable(m, rr, m->timenow + MERGE_DELAY_TIME))
+		{
+		// Delay the record deregistration by MERGE_DELAY_TIME so that we can merge them
+		// into one update. If the domain is being deleted, delay by 2 * MERGE_DELAY_TIME
+		// so that we can merge all the AutoTunnel records and the service records in
+		// one update (they get deregistered a little apart)
+		if (info && info->deltime) rr->LastAPTime += (2 * MERGE_DELAY_TIME);
+		else rr->LastAPTime += MERGE_DELAY_TIME;
+		}
+	// IsRecordMergeable could have returned false for several reasons e.g., DontMerge is set or
+	// no zone information. Most likely it is the latter, CheckRecordUpdates will fetch the zone
+	// data when it encounters this record.
+
+	if (m->NextuDNSEvent - (rr->LastAPTime + rr->ThisAPInterval) >= 0)
+		m->NextuDNSEvent = (rr->LastAPTime + rr->ThisAPInterval);
+
+	return mStatus_NoError;
+	}
+
+mDNSexport mStatus uDNS_UpdateRecord(mDNS *m, AuthRecord *rr)
+	{
+	LogInfo("uDNS_UpdateRecord: Resource Record %##s, state %d", rr->resrec.name->c, rr->state);
+	switch(rr->state)
+		{
+		case regState_DeregPending:
+		case regState_Unregistered:
+			// not actively registered
+			goto unreg_error;
+
+		case regState_NATMap:
+		case regState_NoTarget:
+			// change rdata directly since it hasn't been sent yet
+			if (rr->UpdateCallback) rr->UpdateCallback(m, rr, rr->resrec.rdata, rr->resrec.rdlength);
+			SetNewRData(&rr->resrec, rr->NewRData, rr->newrdlength);
+			rr->NewRData = mDNSNULL;
+			return mStatus_NoError;
+
+		case regState_Pending:
+		case regState_Refresh:
+		case regState_UpdatePending:
+			// registration in-flight. queue rdata and return
+			if (rr->QueuedRData && rr->UpdateCallback)
+				// if unsent rdata is already queued, free it before we replace it
+				rr->UpdateCallback(m, rr, rr->QueuedRData, rr->QueuedRDLen);
+			rr->QueuedRData = rr->NewRData;
+			rr->QueuedRDLen = rr->newrdlength;
+			rr->NewRData = mDNSNULL;
+			return mStatus_NoError;
+
+		case regState_Registered:
+			rr->OrigRData = rr->resrec.rdata;
+			rr->OrigRDLen = rr->resrec.rdlength;
+			rr->InFlightRData = rr->NewRData;
+			rr->InFlightRDLen = rr->newrdlength;
+			rr->NewRData = mDNSNULL;
+			rr->state = regState_UpdatePending;
+			rr->ThisAPInterval = INIT_RECORD_REG_INTERVAL;
+			rr->LastAPTime = m->timenow - INIT_RECORD_REG_INTERVAL;
+			return mStatus_NoError;
+
+		case regState_NATError:
+			LogMsg("ERROR: uDNS_UpdateRecord called for record %##s with bad state regState_NATError", rr->resrec.name->c);
+			return mStatus_UnknownErr;	// states for service records only
+
+		default: LogMsg("uDNS_UpdateRecord: Unknown state %d for %##s", rr->state, rr->resrec.name->c);
+		}
+
+	unreg_error:
+	LogMsg("uDNS_UpdateRecord: Requested update of record %##s type %d, in erroneous state %d",
+		   rr->resrec.name->c, rr->resrec.rrtype, rr->state);
+	return mStatus_Invalid;
+	}
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark - Periodic Execution Routines
+#endif
+
+mDNSlocal const mDNSu8 *mDNS_WABLabels[] =
+	{
+	(const mDNSu8 *)"\001b",
+	(const mDNSu8 *)"\002db",
+	(const mDNSu8 *)"\002lb",
+	(const mDNSu8 *)"\001r",
+	(const mDNSu8 *)"\002dr",
+	(const mDNSu8 *)mDNSNULL,
+	};
+
+// Returns true if it is a WAB question
+mDNSlocal mDNSBool WABQuestion(const domainname *qname)
+	{
+ 	const mDNSu8 *sd = (const mDNSu8 *)"\007_dns-sd";
+	const mDNSu8 *prot = (const mDNSu8 *)"\004_udp";
+	const domainname *d = qname;
+	const mDNSu8 *label;
+	int i = 0;
+
+	// We need at least 3 labels (WAB prefix) + one more label to make
+	// a meaningful WAB query
+	if (CountLabels(qname) < 4) { debugf("WABQuestion: question %##s, not enough labels", qname->c); return mDNSfalse; }
+
+	label = (const mDNSu8 *)d;
+	while (mDNS_WABLabels[i] != (const mDNSu8 *)mDNSNULL)
+		{
+		if (SameDomainLabel(mDNS_WABLabels[i], label)) {debugf("WABquestion: WAB question %##s, label1 match", qname->c); break;}
+		i++;
+		}
+	if (mDNS_WABLabels[i] == (const mDNSu8 *)mDNSNULL)
+		{
+		debugf("WABquestion: Not a WAB question %##s, label1 mismatch", qname->c);
+		return mDNSfalse;
+		}
+	// CountLabels already verified the number of labels 
+	d = (const domainname *)(d->c + 1 + d->c[0]);	// Second Label
+	label = (const mDNSu8 *)d;
+	if (!SameDomainLabel(label, sd)){ debugf("WABquestion: Not a WAB question %##s, label2 mismatch", qname->c);return(mDNSfalse); }
+	debugf("WABquestion: WAB question %##s, label2 match", qname->c);
+
+	d = (const domainname *)(d->c + 1 + d->c[0]); 	// Third Label
+	label = (const mDNSu8 *)d;
+	if (!SameDomainLabel(label, prot)){ debugf("WABquestion: Not a WAB question %##s, label3 mismatch", qname->c);return(mDNSfalse); }
+	debugf("WABquestion: WAB question %##s, label3 match", qname->c);
+
+	LogInfo("WABquestion: Question %##s is a WAB question", qname->c);
+
+	return mDNStrue;
+	}
+
+// The question to be checked is not passed in as an explicit parameter;
+// instead it is implicit that the question to be checked is m->CurrentQuestion.
+mDNSexport void uDNS_CheckCurrentQuestion(mDNS *const m)
+	{
+	DNSQuestion *q = m->CurrentQuestion;
+	if (m->timenow - NextQSendTime(q) < 0) return;
+
+	if (q->LongLived)
+		{
+		switch (q->state)
+			{
+			case LLQ_InitialRequest:   startLLQHandshake(m, q); break;
+			case LLQ_SecondaryRequest:
+									   // For PrivateQueries, we need to start the handshake again as we don't do the Challenge/Response step
+									   if (PrivateQuery(q))
+										   startLLQHandshake(m, q);
+									   else
+									  	   sendChallengeResponse(m, q, mDNSNULL);
+									   break;
+			case LLQ_Established:      sendLLQRefresh(m, q); break;
+			case LLQ_Poll:             break;	// Do nothing (handled below)
+			}
+		}
+
+	// We repeat the check above (rather than just making this the "else" case) because startLLQHandshake can change q->state to LLQ_Poll
+	if (!(q->LongLived && q->state != LLQ_Poll))
+		{
+		if (q->unansweredQueries >= MAX_UCAST_UNANSWERED_QUERIES)
+			{
+			DNSServer *orig = q->qDNSServer;
+			if (orig)
+				LogInfo("uDNS_CheckCurrentQuestion: Sent %d unanswered queries for %##s (%s) to %#a:%d (%##s)",
+					q->unansweredQueries, q->qname.c, DNSTypeName(q->qtype), &orig->addr, mDNSVal16(orig->port), orig->domain.c);
+
+			PenalizeDNSServer(m, q);
+			q->noServerResponse = 1;
+			}
+		// There are two cases here.
+		//
+		// 1. We have only one DNS server for this question. It is not responding even after we sent MAX_UCAST_UNANSWERED_QUERIES.
+		//    In that case, we need to keep retrying till we get a response. But we need to backoff as we retry. We set
+		//    noServerResponse in the block above and below we do not touch the question interval. When we come here, we
+		//    already waited for the response. We need to send another query right at this moment. We do that below by
+		//    reinitializing dns servers and reissuing the query.
+		//
+		// 2. We have more than one DNS server. If at least one server did not respond, we would have set noServerResponse
+		//    either now (the last server in the list) or before (non-last server in the list). In either case, if we have
+		//    reached the end of DNS server list, we need to try again from the beginning. Ideally we should try just the
+		//    servers that did not respond, but for simplicity we try all the servers. Once we reached the end of list, we
+		//    set triedAllServersOnce so that we don't try all the servers aggressively. See PenalizeDNSServer.
+		if (!q->qDNSServer && q->noServerResponse)
+			{
+			DNSServer *new;
+			DNSQuestion *qptr;
+			q->triedAllServersOnce = 1;
+			// Re-initialize all DNS servers for this question. If we have a DNSServer, DNSServerChangeForQuestion will
+			// handle all the work including setting the new DNS server.
+			SetValidDNSServers(m, q);
+			new = GetServerForQuestion(m, q);
+			if (new)
+				{
+				LogInfo("uDNS_checkCurrentQuestion: Retrying question %p %##s (%s) DNS Server %#a:%d ThisQInterval %d",
+					q, q->qname.c, DNSTypeName(q->qtype), new ? &new->addr : mDNSNULL, mDNSVal16(new ? new->port : zeroIPPort), q->ThisQInterval);
+				DNSServerChangeForQuestion(m, q, new);
+				}
+			for (qptr = q->next ; qptr; qptr = qptr->next)
+				if (qptr->DuplicateOf == q) { qptr->validDNSServers = q->validDNSServers; qptr->qDNSServer = q->qDNSServer; }
+			}
+		if (q->qDNSServer && q->qDNSServer->teststate != DNSServer_Disabled)
+			{
+			mDNSu8 *end = m->omsg.data;
+			mStatus err = mStatus_NoError;
+			mDNSBool private = mDNSfalse;
+
+			InitializeDNSMessage(&m->omsg.h, q->TargetQID, uQueryFlags);
+
+			if (q->qDNSServer->teststate != DNSServer_Untested || NoTestQuery(q))
+				{
+				end = putQuestion(&m->omsg, m->omsg.data, m->omsg.data + AbsoluteMaxDNSMessageData, &q->qname, q->qtype, q->qclass);
+				private = PrivateQuery(q);
+				}
+			else if (m->timenow - q->qDNSServer->lasttest >= INIT_UCAST_POLL_INTERVAL)	// Make sure at least three seconds has elapsed since last test query
+				{
+				LogInfo("Sending DNS test query to %#a:%d", &q->qDNSServer->addr, mDNSVal16(q->qDNSServer->port));
+				q->ThisQInterval = INIT_UCAST_POLL_INTERVAL / QuestionIntervalStep;
+				q->qDNSServer->lasttest = m->timenow;
+				end = putQuestion(&m->omsg, m->omsg.data, m->omsg.data + AbsoluteMaxDNSMessageData, DNSRelayTestQuestion, kDNSType_PTR, kDNSClass_IN);
+				q->qDNSServer->testid = m->omsg.h.id;
+				}
+
+			if (end > m->omsg.data && (q->qDNSServer->teststate != DNSServer_Failed || NoTestQuery(q)))
+				{
+				//LogMsg("uDNS_CheckCurrentQuestion %p %d %p %##s (%s)", q, NextQSendTime(q) - m->timenow, private, q->qname.c, DNSTypeName(q->qtype));
+				if (private)
+					{
+					if (q->nta) CancelGetZoneData(m, q->nta);
+					q->nta = StartGetZoneData(m, &q->qname, q->LongLived ? ZoneServiceLLQ : ZoneServiceQuery, PrivateQueryGotZoneData, q);
+					if (q->state == LLQ_Poll) q->ThisQInterval = (LLQ_POLL_INTERVAL + mDNSRandom(LLQ_POLL_INTERVAL/10)) / QuestionIntervalStep;
+					}
+				else
+					{
+				    debugf("uDNS_CheckCurrentQuestion sending %p %##s (%s) %#a:%d UnansweredQueries %d",
+				    	q, q->qname.c, DNSTypeName(q->qtype),
+				    	q->qDNSServer ? &q->qDNSServer->addr : mDNSNULL, mDNSVal16(q->qDNSServer ? q->qDNSServer->port : zeroIPPort), q->unansweredQueries);
+					if (!q->LocalSocket) q->LocalSocket = mDNSPlatformUDPSocket(m, zeroIPPort);
+					if (!q->LocalSocket) err = mStatus_NoMemoryErr;	// If failed to make socket (should be very rare), we'll try again next time
+					else err = mDNSSendDNSMessage(m, &m->omsg, end, q->qDNSServer->interface, q->LocalSocket, &q->qDNSServer->addr, q->qDNSServer->port, mDNSNULL, mDNSNULL);
+					}
+				}
+
+			if (err) debugf("ERROR: uDNS_idle - mDNSSendDNSMessage - %d", err); // surpress syslog messages if we have no network
+			else
+				{
+				q->ThisQInterval = q->ThisQInterval * QuestionIntervalStep;	// Only increase interval if send succeeded
+				q->unansweredQueries++;
+				if (q->ThisQInterval > MAX_UCAST_POLL_INTERVAL)
+					q->ThisQInterval = MAX_UCAST_POLL_INTERVAL;
+				if (private && q->state != LLQ_Poll)
+					{
+					// We don't want to retransmit too soon. Hence, we always schedule our first
+					// retransmisson at 3 seconds rather than one second
+					if (q->ThisQInterval < (3 * mDNSPlatformOneSecond))
+						q->ThisQInterval = q->ThisQInterval * QuestionIntervalStep;
+					if (q->ThisQInterval > LLQ_POLL_INTERVAL)
+						q->ThisQInterval = LLQ_POLL_INTERVAL;
+					LogInfo("uDNS_CheckCurrentQuestion: private non polling question for %##s (%s) will be retried in %d ms", q->qname.c, DNSTypeName(q->qtype), q->ThisQInterval);
+					}
+				debugf("Increased ThisQInterval to %d for %##s (%s)", q->ThisQInterval, q->qname.c, DNSTypeName(q->qtype));
+				}
+			q->LastQTime = m->timenow;
+			SetNextQueryTime(m, q);
+			}
+		else
+			{
+			// If we have no server for this query, or the only server is a disabled one, then we deliver
+			// a transient failure indication to the client. This is important for things like iPhone
+			// where we want to return timely feedback to the user when no network is available.
+			// After calling MakeNegativeCacheRecord() we store the resulting record in the
+			// cache so that it will be visible to other clients asking the same question.
+			// (When we have a group of identical questions, only the active representative of the group gets
+			// passed to uDNS_CheckCurrentQuestion -- we only want one set of query packets hitting the wire --
+			// but we want *all* of the questions to get answer callbacks.)
+
+			CacheRecord *rr;
+			const mDNSu32 slot = HashSlot(&q->qname);
+			CacheGroup *const cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname);
+			if (cg)
+				for (rr = cg->members; rr; rr=rr->next)
+					if (SameNameRecordAnswersQuestion(&rr->resrec, q)) mDNS_PurgeCacheResourceRecord(m, rr);
+
+			if (!q->qDNSServer)
+				{
+				if (!mDNSOpaque64IsZero(&q->validDNSServers))
+					LogMsg("uDNS_CheckCurrentQuestion: ERROR!!: valid DNSServer bits not zero 0x%x, 0x%x for question %##s (%s)",
+						q->validDNSServers.l[1], q->validDNSServers.l[0], q->qname.c, DNSTypeName(q->qtype));
+				// If we reached the end of list while picking DNS servers, then we don't want to deactivate the
+				// question. Try after 60 seconds. We find this by looking for valid DNSServers for this question,
+				// if we find any, then we must have tried them before we came here. This avoids maintaining
+				// another state variable to see if we had valid DNS servers for this question.
+				SetValidDNSServers(m, q);	
+				if (mDNSOpaque64IsZero(&q->validDNSServers))
+					{
+					LogInfo("uDNS_CheckCurrentQuestion: no DNS server for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+					q->ThisQInterval = 0;
+					}
+				else
+					{
+					DNSQuestion *qptr;
+					// Pretend that we sent this question. As this is an ActiveQuestion, the NextScheduledQuery should
+					// be set properly. Also, we need to properly backoff in cases where we don't set the question to
+					// MaxQuestionInterval when we answer the question e.g., LongLived, we need to keep backing off
+					q->ThisQInterval = q->ThisQInterval * QuestionIntervalStep;
+					q->LastQTime = m->timenow;
+					SetNextQueryTime(m, q);
+					// Pick a new DNS server now. Otherwise, when the cache is 80% of its expiry, we will try
+					// to send a query and come back to the same place here and log the above message.
+					q->qDNSServer = GetServerForQuestion(m, q);
+					for (qptr = q->next ; qptr; qptr = qptr->next)
+						if (qptr->DuplicateOf == q) { qptr->validDNSServers = q->validDNSServers; qptr->qDNSServer = q->qDNSServer; }
+					LogInfo("uDNS_checkCurrentQuestion: Tried all DNS servers, retry question %p SuppressUnusable %d %##s (%s) with DNS Server %#a:%d after 60 seconds, ThisQInterval %d",
+						q, q->SuppressUnusable, q->qname.c, DNSTypeName(q->qtype),
+						q->qDNSServer ? &q->qDNSServer->addr : mDNSNULL, mDNSVal16(q->qDNSServer ? q->qDNSServer->port : zeroIPPort), q->ThisQInterval);
+					}
+				}
+			else
+				{
+				q->ThisQInterval = 0;
+				LogMsg("uDNS_CheckCurrentQuestion DNS server %#a:%d for %##s is disabled", &q->qDNSServer->addr, mDNSVal16(q->qDNSServer->port), q->qname.c);
+				}
+
+			// For some of the WAB queries that we generate form within the mDNSResponder, most of the home routers
+			// don't understand and return ServFail/NXDomain. In those cases, we don't want to try too often. We try
+			// every fifteen minutes in that case
+			MakeNegativeCacheRecord(m, &m->rec.r, &q->qname, q->qnamehash, q->qtype, q->qclass, (WABQuestion(&q->qname) ? 60 * 15 : 60), mDNSInterface_Any, q->qDNSServer);
+			q->unansweredQueries = 0;
+			// We're already using the m->CurrentQuestion pointer, so CacheRecordAdd can't use it to walk the question list.
+			// To solve this problem we set rr->DelayDelivery to a nonzero value (which happens to be 'now') so that we
+			// momentarily defer generating answer callbacks until mDNS_Execute time.
+			CreateNewCacheEntry(m, slot, cg, NonZeroTime(m->timenow));
+			ScheduleNextCacheCheckTime(m, slot, NonZeroTime(m->timenow));
+			m->rec.r.resrec.RecordType = 0;		// Clear RecordType to show we're not still using it
+			// MUST NOT touch m->CurrentQuestion (or q) after this -- client callback could have deleted it
+			}
+		}
+	}
+
+mDNSexport void CheckNATMappings(mDNS *m)
+	{
+	mStatus err = mStatus_NoError;
+	mDNSBool rfc1918 = mDNSv4AddrIsRFC1918(&m->AdvertisedV4.ip.v4);
+	mDNSBool HaveRoutable = !rfc1918 && !mDNSIPv4AddressIsZero(m->AdvertisedV4.ip.v4);
+	m->NextScheduledNATOp = m->timenow + 0x3FFFFFFF;
+
+	if (HaveRoutable) m->ExternalAddress = m->AdvertisedV4.ip.v4;
+
+	if (m->NATTraversals && rfc1918)			// Do we need to open NAT-PMP socket to receive multicast announcements from router?
+		{
+		if (m->NATMcastRecvskt == mDNSNULL)		// If we are behind a NAT and the socket hasn't been opened yet, open it
+			{
+			// we need to log a message if we can't get our socket, but only the first time (after success)
+			static mDNSBool needLog = mDNStrue;
+			m->NATMcastRecvskt = mDNSPlatformUDPSocket(m, NATPMPAnnouncementPort);
+			if (!m->NATMcastRecvskt)
+				{
+				if (needLog)
+					{
+					LogMsg("CheckNATMappings: Failed to allocate port 5350 UDP multicast socket for NAT-PMP announcements");
+					needLog = mDNSfalse;
+					}
+				}
+			else
+				needLog = mDNStrue;				
+			}
+		}
+	else										// else, we don't want to listen for announcements, so close them if they're open
+		{
+		if (m->NATMcastRecvskt) { mDNSPlatformUDPClose(m->NATMcastRecvskt); m->NATMcastRecvskt = mDNSNULL; }
+		if (m->SSDPSocket)      { debugf("CheckNATMappings destroying SSDPSocket %p", &m->SSDPSocket); mDNSPlatformUDPClose(m->SSDPSocket); m->SSDPSocket = mDNSNULL; }
+		}
+
+	if (!m->NATTraversals)
+		m->retryGetAddr = m->timenow + 0x78000000;
+	else
+		{
+		if (m->timenow - m->retryGetAddr >= 0)
+			{
+			err = uDNS_SendNATMsg(m, mDNSNULL);		// Will also do UPnP discovery for us, if necessary
+			if (!err)
+				{
+				if      (m->retryIntervalGetAddr < NATMAP_INIT_RETRY)             m->retryIntervalGetAddr = NATMAP_INIT_RETRY;
+				else if (m->retryIntervalGetAddr < NATMAP_MAX_RETRY_INTERVAL / 2) m->retryIntervalGetAddr *= 2;
+				else                                                              m->retryIntervalGetAddr = NATMAP_MAX_RETRY_INTERVAL;
+				}
+			LogInfo("CheckNATMappings retryGetAddr sent address request err %d interval %d", err, m->retryIntervalGetAddr);
+
+			// Always update m->retryGetAddr, even if we fail to send the packet. Otherwise in cases where we can't send the packet
+			// (like when we have no active interfaces) we'll spin in an infinite loop repeatedly failing to send the packet
+			m->retryGetAddr = m->timenow + m->retryIntervalGetAddr;
+			}
+		// Even when we didn't send the GetAddr packet, still need to make sure NextScheduledNATOp is set correctly
+		if (m->NextScheduledNATOp - m->retryGetAddr > 0)
+			m->NextScheduledNATOp = m->retryGetAddr;
+		}
+
+	if (m->CurrentNATTraversal) LogMsg("WARNING m->CurrentNATTraversal already in use");
+	m->CurrentNATTraversal = m->NATTraversals;
+
+	while (m->CurrentNATTraversal)
+		{
+		NATTraversalInfo *cur = m->CurrentNATTraversal;
+		m->CurrentNATTraversal = m->CurrentNATTraversal->next;
+
+		if (HaveRoutable)		// If not RFC 1918 address, our own address and port are effectively our external address and port
+			{
+			cur->ExpiryTime = 0;
+			cur->NewResult  = mStatus_NoError;
+			}
+		else if (cur->Protocol)		// Check if it's time to send port mapping packets
+			{
+			if (m->timenow - cur->retryPortMap >= 0)						// Time to do something with this mapping
+				{
+				if (cur->ExpiryTime && cur->ExpiryTime - m->timenow < 0)	// Mapping has expired
+					{
+					cur->ExpiryTime    = 0;
+					cur->retryInterval = NATMAP_INIT_RETRY;
+					}
+
+				//LogMsg("uDNS_SendNATMsg");
+				err = uDNS_SendNATMsg(m, cur);
+
+				if (cur->ExpiryTime)						// If have active mapping then set next renewal time halfway to expiry
+					NATSetNextRenewalTime(m, cur);
+				else										// else no mapping; use exponential backoff sequence
+					{
+					if      (cur->retryInterval < NATMAP_INIT_RETRY            ) cur->retryInterval = NATMAP_INIT_RETRY;
+					else if (cur->retryInterval < NATMAP_MAX_RETRY_INTERVAL / 2) cur->retryInterval *= 2;
+					else                                                         cur->retryInterval = NATMAP_MAX_RETRY_INTERVAL;
+					cur->retryPortMap = m->timenow + cur->retryInterval;
+					}
+				}
+
+			if (m->NextScheduledNATOp - cur->retryPortMap > 0)
+				m->NextScheduledNATOp = cur->retryPortMap;
+			}
+
+		// Notify the client if necessary. We invoke the callback if:
+		// (1) we have an ExternalAddress, or we've tried and failed a couple of times to discover it
+		// and (2) the client doesn't want a mapping, or the client won't need a mapping, or the client has a successful mapping, or we've tried and failed a couple of times
+		// and (3) we have new data to give the client that's changed since the last callback
+		// Time line is: Send, Wait 500ms, Send, Wait 1sec, Send, Wait 2sec, Send
+		// At this point we've sent three requests without an answer, we've just sent our fourth request,
+		// retryIntervalGetAddr is now 4 seconds, which is greater than NATMAP_INIT_RETRY * 8 (2 seconds),
+		// so we return an error result to the caller.
+		if (!mDNSIPv4AddressIsZero(m->ExternalAddress) || m->retryIntervalGetAddr > NATMAP_INIT_RETRY * 8)
+			{
+			const mStatus EffectiveResult = cur->NewResult ? cur->NewResult : mDNSv4AddrIsRFC1918(&m->ExternalAddress) ? mStatus_DoubleNAT : mStatus_NoError;
+			const mDNSIPPort ExternalPort = HaveRoutable ? cur->IntPort :
+				!mDNSIPv4AddressIsZero(m->ExternalAddress) && cur->ExpiryTime ? cur->RequestedPort : zeroIPPort;
+			if (!cur->Protocol || HaveRoutable || cur->ExpiryTime || cur->retryInterval > NATMAP_INIT_RETRY * 8)
+				if (!mDNSSameIPv4Address(cur->ExternalAddress, m->ExternalAddress) ||
+					!mDNSSameIPPort     (cur->ExternalPort,       ExternalPort)    ||
+					cur->Result != EffectiveResult)
+					{
+					//LogMsg("NAT callback %d %d %d", cur->Protocol, cur->ExpiryTime, cur->retryInterval);
+					if (cur->Protocol && mDNSIPPortIsZero(ExternalPort) && !mDNSIPv4AddressIsZero(m->Router.ip.v4))
+						{
+						if (!EffectiveResult)
+							LogInfo("CheckNATMapping: Failed to obtain NAT port mapping %p from router %#a external address %.4a internal port %5d interval %d error %d",
+								cur, &m->Router, &m->ExternalAddress, mDNSVal16(cur->IntPort), cur->retryInterval, EffectiveResult);
+						else
+							LogMsg("CheckNATMapping: Failed to obtain NAT port mapping %p from router %#a external address %.4a internal port %5d interval %d error %d",
+								cur, &m->Router, &m->ExternalAddress, mDNSVal16(cur->IntPort), cur->retryInterval, EffectiveResult);
+						}
+
+					cur->ExternalAddress = m->ExternalAddress;
+					cur->ExternalPort    = ExternalPort;
+					cur->Lifetime        = cur->ExpiryTime && !mDNSIPPortIsZero(ExternalPort) ?
+						(cur->ExpiryTime - m->timenow + mDNSPlatformOneSecond/2) / mDNSPlatformOneSecond : 0;
+					cur->Result          = EffectiveResult;
+					mDNS_DropLockBeforeCallback();		// Allow client to legally make mDNS API calls from the callback
+					if (cur->clientCallback)
+						cur->clientCallback(m, cur);
+					mDNS_ReclaimLockAfterCallback();	// Decrement mDNS_reentrancy to block mDNS API calls again
+					// MUST NOT touch cur after invoking the callback
+					}
+			}
+		}
+	}
+
+mDNSlocal mDNSs32 CheckRecordUpdates(mDNS *m)
+	{
+	AuthRecord *rr;
+	mDNSs32 nextevent = m->timenow + 0x3FFFFFFF;
+
+	CheckGroupRecordUpdates(m);
+
+	for (rr = m->ResourceRecords; rr; rr = rr->next)
+		{
+		if (!AuthRecord_uDNS(rr)) continue;
+		if (rr->state == regState_NoTarget) {debugf("CheckRecordUpdates: Record %##s in NoTarget", rr->resrec.name->c); continue;}
+		// While we are waiting for the port mapping, we have nothing to do. The port mapping callback
+		// will take care of this
+		if (rr->state == regState_NATMap) {debugf("CheckRecordUpdates: Record %##s in NATMap", rr->resrec.name->c); continue;}
+		if (rr->state == regState_Pending || rr->state == regState_DeregPending || rr->state == regState_UpdatePending ||
+			rr->state == regState_Refresh || rr->state == regState_Registered)
+			{
+			if (rr->LastAPTime + rr->ThisAPInterval - m->timenow <= 0)
+				{
+				if (rr->tcp) { DisposeTCPConn(rr->tcp); rr->tcp = mDNSNULL; }
+				if (!rr->nta || mDNSIPv4AddressIsZero(rr->nta->Addr.ip.v4))
+					{
+					// Zero out the updateid so that if we have a pending response from the server, it won't
+					// be accepted as a valid response. If we accept the response, we might free the new "nta"
+					if (rr->nta) { rr->updateid = zeroID; CancelGetZoneData(m, rr->nta); }
+					rr->nta = StartGetZoneData(m, rr->resrec.name, ZoneServiceUpdate, RecordRegistrationGotZoneData, rr);
+
+					// We have just started the GetZoneData. We need to wait for it to finish. SetRecordRetry here
+					// schedules the update timer to fire in the future.
+					//
+					// There are three cases.
+					//
+					// 1) When the updates are sent the first time, the first retry is intended to be at three seconds
+					//    in the future. But by calling SetRecordRetry here we set it to nine seconds. But it does not
+					//    matter because when the answer comes back, RecordRegistrationGotZoneData resets the interval
+					//    back to INIT_RECORD_REG_INTERVAL. This also gives enough time for the query.
+					//
+					// 2) In the case of update errors (updateError), this causes further backoff as
+					//    RecordRegistrationGotZoneData does not reset the timer. This is intentional as in the case of
+					//    errors, we don't want to update aggressively.
+					//
+					// 3) We might be refreshing the update. This is very similar to case (1). RecordRegistrationGotZoneData
+					//    resets it back to INIT_RECORD_REG_INTERVAL.
+					// 
+					SetRecordRetry(m, rr, 0);
+					}
+				else if (rr->state == regState_DeregPending) SendRecordDeregistration(m, rr);
+				else SendRecordRegistration(m, rr);
+				}
+			}
+		if (nextevent - (rr->LastAPTime + rr->ThisAPInterval) > 0)
+			nextevent = (rr->LastAPTime + rr->ThisAPInterval);
+		}
+	return nextevent;
+	}
+
+mDNSexport void uDNS_Tasks(mDNS *const m)
+	{
+	mDNSs32 nexte;
+	DNSServer *d;
+
+	m->NextuDNSEvent = m->timenow + 0x3FFFFFFF;
+
+	nexte = CheckRecordUpdates(m);
+	if (m->NextuDNSEvent - nexte > 0)
+		m->NextuDNSEvent = nexte;
+
+	for (d = m->DNSServers; d; d=d->next)
+		if (d->penaltyTime)
+			{
+			if (m->timenow - d->penaltyTime >= 0)
+				{
+				LogInfo("DNS server %#a:%d out of penalty box", &d->addr, mDNSVal16(d->port));
+				d->penaltyTime = 0;
+				}
+			else
+				if (m->NextuDNSEvent - d->penaltyTime > 0)
+					m->NextuDNSEvent = d->penaltyTime;
+			}
+
+	if (m->CurrentQuestion)
+		LogMsg("uDNS_Tasks ERROR m->CurrentQuestion already set: %##s (%s)", m->CurrentQuestion->qname.c, DNSTypeName(m->CurrentQuestion->qtype));
+	m->CurrentQuestion = m->Questions;
+	while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions)
+		{
+		DNSQuestion *const q = m->CurrentQuestion;
+		if (ActiveQuestion(q) && !mDNSOpaque16IsZero(q->TargetQID))
+			{
+			uDNS_CheckCurrentQuestion(m);
+			if (q == m->CurrentQuestion)
+				if (m->NextuDNSEvent - NextQSendTime(q) > 0)
+					m->NextuDNSEvent = NextQSendTime(q);
+			}
+		// If m->CurrentQuestion wasn't modified out from under us, advance it now
+		// We can't do this at the start of the loop because uDNS_CheckCurrentQuestion()
+		// depends on having m->CurrentQuestion point to the right question
+		if (m->CurrentQuestion == q)
+			m->CurrentQuestion = q->next;
+		}
+	m->CurrentQuestion = mDNSNULL;
+	}
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark - Startup, Shutdown, and Sleep
+#endif
+
+mDNSexport void SleepRecordRegistrations(mDNS *m)
+	{
+	AuthRecord *rr;
+	for (rr = m->ResourceRecords; rr; rr=rr->next)
+		{
+		if (AuthRecord_uDNS(rr))
+			{
+			// Zero out the updateid so that if we have a pending response from the server, it won't
+			// be accepted as a valid response.
+			if (rr->nta) { rr->updateid = zeroID; CancelGetZoneData(m, rr->nta); rr->nta = mDNSNULL; }
+
+			if (rr->NATinfo.clientContext)
+				{
+				mDNS_StopNATOperation_internal(m, &rr->NATinfo);
+				rr->NATinfo.clientContext = mDNSNULL;
+				}
+			// We are waiting to update the resource record. The original data of the record is
+			// in OrigRData and the updated value is in InFlightRData. Free the old and the new
+			// one will be registered when we come back.
+			if (rr->state == regState_UpdatePending)
+				{
+				// act as if the update succeeded, since we're about to delete the name anyway
+				rr->state = regState_Registered;
+				// deallocate old RData
+				if (rr->UpdateCallback) rr->UpdateCallback(m, rr, rr->OrigRData, rr->OrigRDLen);
+				SetNewRData(&rr->resrec, rr->InFlightRData, rr->InFlightRDLen);
+				rr->OrigRData = mDNSNULL;
+				rr->InFlightRData = mDNSNULL;
+				}
+
+			// If we have not begun the registration process i.e., never sent a registration packet,
+			// then uDNS_DeregisterRecord will not send a deregistration
+			uDNS_DeregisterRecord(m, rr);
+			
+			// When we wake, we call ActivateUnicastRegistration which starts at StartGetZoneData
+			}
+		}
+	}
+
+mDNSexport void mDNS_AddSearchDomain(const domainname *const domain, mDNSInterfaceID InterfaceID)
+	{
+	SearchListElem **p;
+	SearchListElem *tmp = mDNSNULL;
+
+	// Check to see if we already have this domain in our list
+	for (p = &SearchList; *p; p = &(*p)->next)
+		if (((*p)->InterfaceID == InterfaceID) && SameDomainName(&(*p)->domain, domain))
+			{
+			// If domain is already in list, and marked for deletion, unmark the delete
+			// Be careful not to touch the other flags that may be present
+			LogInfo("mDNS_AddSearchDomain already in list %##s", domain->c);
+			if ((*p)->flag & SLE_DELETE) (*p)->flag &= ~SLE_DELETE;
+			tmp = *p;
+			*p = tmp->next;
+			tmp->next = mDNSNULL;
+			break;
+			}
+
+	
+	// move to end of list so that we maintain the same order
+	while (*p) p = &(*p)->next;
+	
+	if (tmp) *p = tmp; 
+	else
+		{
+		// if domain not in list, add to list, mark as add (1)
+		*p = mDNSPlatformMemAllocate(sizeof(SearchListElem));
+		if (!*p) { LogMsg("ERROR: mDNS_AddSearchDomain - malloc"); return; }
+		mDNSPlatformMemZero(*p, sizeof(SearchListElem));
+		AssignDomainName(&(*p)->domain, domain);
+		(*p)->next = mDNSNULL;
+		(*p)->InterfaceID = InterfaceID;
+		LogInfo("mDNS_AddSearchDomain created new %##s, InterfaceID %p", domain->c, InterfaceID);
+		}
+	}
+
+mDNSlocal void FreeARElemCallback(mDNS *const m, AuthRecord *const rr, mStatus result)
+	{
+	(void)m;	// unused
+	if (result == mStatus_MemFree) mDNSPlatformMemFree(rr->RecordContext);
+	}
+
+mDNSlocal void FoundDomain(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
+	{
+	SearchListElem *slElem = question->QuestionContext;
+	mStatus err;
+	const char *name;
+
+	if (answer->rrtype != kDNSType_PTR) return;
+	if (answer->RecordType == kDNSRecordTypePacketNegative) return;
+	if (answer->InterfaceID == mDNSInterface_LocalOnly) return;
+
+	if      (question == &slElem->BrowseQ)          name = mDNS_DomainTypeNames[mDNS_DomainTypeBrowse];
+	else if (question == &slElem->DefBrowseQ)       name = mDNS_DomainTypeNames[mDNS_DomainTypeBrowseDefault];
+	else if (question == &slElem->AutomaticBrowseQ) name = mDNS_DomainTypeNames[mDNS_DomainTypeBrowseAutomatic];
+	else if (question == &slElem->RegisterQ)        name = mDNS_DomainTypeNames[mDNS_DomainTypeRegistration];
+	else if (question == &slElem->DefRegisterQ)     name = mDNS_DomainTypeNames[mDNS_DomainTypeRegistrationDefault];
+	else { LogMsg("FoundDomain - unknown question"); return; }
+
+	LogInfo("FoundDomain: %p %s %s Q %##s A %s", answer->InterfaceID, AddRecord ? "Add" : "Rmv", name, question->qname.c, RRDisplayString(m, answer));
+
+	if (AddRecord)
+		{
+		ARListElem *arElem = mDNSPlatformMemAllocate(sizeof(ARListElem));
+		if (!arElem) { LogMsg("ERROR: FoundDomain out of memory"); return; }
+		mDNS_SetupResourceRecord(&arElem->ar, mDNSNULL, mDNSInterface_LocalOnly, kDNSType_PTR, 7200, kDNSRecordTypeShared, AuthRecordLocalOnly, FreeARElemCallback, arElem);
+		MakeDomainNameFromDNSNameString(&arElem->ar.namestorage, name);
+		AppendDNSNameString            (&arElem->ar.namestorage, "local");
+		AssignDomainName(&arElem->ar.resrec.rdata->u.name, &answer->rdata->u.name);
+		LogInfo("FoundDomain: Registering %s", ARDisplayString(m, &arElem->ar));
+		err = mDNS_Register(m, &arElem->ar);
+		if (err) { LogMsg("ERROR: FoundDomain - mDNS_Register returned %d", err); mDNSPlatformMemFree(arElem); return; }
+		arElem->next = slElem->AuthRecs;
+		slElem->AuthRecs = arElem;
+		}
+	else
+		{
+		ARListElem **ptr = &slElem->AuthRecs;
+		while (*ptr)
+			{
+			if (SameDomainName(&(*ptr)->ar.resrec.rdata->u.name, &answer->rdata->u.name))
+				{
+				ARListElem *dereg = *ptr;
+				*ptr = (*ptr)->next;
+				LogInfo("FoundDomain: Deregistering %s", ARDisplayString(m, &dereg->ar));
+				err = mDNS_Deregister(m, &dereg->ar);
+				if (err) LogMsg("ERROR: FoundDomain - mDNS_Deregister returned %d", err);
+				// Memory will be freed in the FreeARElemCallback
+				}
+			else
+				ptr = &(*ptr)->next;
+			}
+		}
+	}
+
+#if APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING
+mDNSexport void udns_validatelists(void *const v)
+	{
+	mDNS *const m = v;
+
+	NATTraversalInfo *n;
+	for (n = m->NATTraversals; n; n=n->next)
+		if (n->next == (NATTraversalInfo *)~0 || n->clientCallback == (NATTraversalClientCallback)~0)
+			LogMemCorruption("m->NATTraversals: %p is garbage", n);
+
+	DNSServer *d;
+	for (d = m->DNSServers; d; d=d->next)
+		if (d->next == (DNSServer *)~0 || d->teststate > DNSServer_Disabled)
+			LogMemCorruption("m->DNSServers: %p is garbage (%d)", d, d->teststate);
+
+	DomainAuthInfo *info;
+	for (info = m->AuthInfoList; info; info = info->next)
+		if (info->next == (DomainAuthInfo *)~0 || info->AutoTunnel == (const char*)~0)
+			LogMemCorruption("m->AuthInfoList: %p is garbage (%X)", info, info->AutoTunnel);
+
+	HostnameInfo *hi;
+	for (hi = m->Hostnames; hi; hi = hi->next)
+		if (hi->next == (HostnameInfo *)~0 || hi->StatusCallback == (mDNSRecordCallback*)~0)
+			LogMemCorruption("m->Hostnames: %p is garbage", n);
+
+	SearchListElem *ptr;
+	for (ptr = SearchList; ptr; ptr = ptr->next)
+		if (ptr->next == (SearchListElem *)~0 || ptr->AuthRecs == (void*)~0)
+			LogMemCorruption("SearchList: %p is garbage (%X)", ptr, ptr->AuthRecs);
+	}
+#endif
+
+// This should probably move to the UDS daemon -- the concept of legacy clients and automatic registration / automatic browsing
+// is really a UDS API issue, not something intrinsic to uDNS
+
+mDNSexport mStatus uDNS_SetupSearchDomains(mDNS *const m, int action)
+	{
+	SearchListElem **p = &SearchList, *ptr;
+	mStatus err;
+
+	// step 1: mark each element for removal
+	for (ptr = SearchList; ptr; ptr = ptr->next) ptr->flag |= SLE_DELETE;
+
+	// Make sure we have the search domains from the platform layer so that if we start the WAB
+	// queries below, we have the latest information
+	mDNS_Lock(m);
+	mDNSPlatformSetDNSConfig(m, mDNSfalse, mDNStrue, mDNSNULL, mDNSNULL, mDNSNULL);
+	mDNS_Unlock(m);
+
+	if (action & UDNS_START_WAB_QUERY)
+		m->StartWABQueries = mDNStrue;
+
+	// delete elems marked for removal, do queries for elems marked add
+	while (*p)
+		{
+		ptr = *p;
+		LogInfo("uDNS_SetupSearchDomains:action %d: Flags %d,  AuthRecs %p, InterfaceID %p %##s", action, ptr->flag, ptr->AuthRecs, ptr->InterfaceID, ptr->domain.c);
+		if (ptr->flag & SLE_DELETE)
+			{
+			ARListElem *arList = ptr->AuthRecs;
+			ptr->AuthRecs = mDNSNULL;
+			*p = ptr->next;
+
+			// If the user has "local" in their DNS searchlist, we ignore that for the purposes of domain enumeration queries
+			// We suppressed the domain enumeration for scoped search domains below. When we enable that
+			// enable this.
+			if ((ptr->flag & SLE_WAB_QUERY_STARTED) && 
+				!SameDomainName(&ptr->domain, &localdomain) && (ptr->InterfaceID == mDNSInterface_Any))
+				{
+				mDNS_StopGetDomains(m, &ptr->BrowseQ);
+				mDNS_StopGetDomains(m, &ptr->RegisterQ);
+				mDNS_StopGetDomains(m, &ptr->DefBrowseQ);
+				mDNS_StopGetDomains(m, &ptr->DefRegisterQ);
+				mDNS_StopGetDomains(m, &ptr->AutomaticBrowseQ);
+				}
+
+			mDNSPlatformMemFree(ptr);
+
+	        // deregister records generated from answers to the query
+			while (arList)
+				{
+				ARListElem *dereg = arList;
+				arList = arList->next;
+				debugf("Deregistering PTR %##s -> %##s", dereg->ar.resrec.name->c, dereg->ar.resrec.rdata->u.name.c);
+				err = mDNS_Deregister(m, &dereg->ar);
+				if (err) LogMsg("uDNS_SetupSearchDomains:: ERROR!! mDNS_Deregister returned %d", err);
+				// Memory will be freed in the FreeARElemCallback
+				}
+			continue;
+			}
+
+		if ((action & UDNS_START_WAB_QUERY) && !(ptr->flag & SLE_WAB_QUERY_STARTED))
+			{
+			// If the user has "local" in their DNS searchlist, we ignore that for the purposes of domain enumeration queries.
+			// Also, suppress the domain enumeration for scoped search domains for now until there is a need.
+			if (!SameDomainName(&ptr->domain, &localdomain) && (ptr->InterfaceID == mDNSInterface_Any))
+				{
+				mStatus err1, err2, err3, err4, err5;
+				err1 = mDNS_GetDomains(m, &ptr->BrowseQ,          mDNS_DomainTypeBrowse,              &ptr->domain, ptr->InterfaceID, FoundDomain, ptr);
+				err2 = mDNS_GetDomains(m, &ptr->DefBrowseQ,       mDNS_DomainTypeBrowseDefault,       &ptr->domain, ptr->InterfaceID, FoundDomain, ptr);
+				err3 = mDNS_GetDomains(m, &ptr->RegisterQ,        mDNS_DomainTypeRegistration,        &ptr->domain, ptr->InterfaceID, FoundDomain, ptr);
+				err4 = mDNS_GetDomains(m, &ptr->DefRegisterQ,     mDNS_DomainTypeRegistrationDefault, &ptr->domain, ptr->InterfaceID, FoundDomain, ptr);
+				err5 = mDNS_GetDomains(m, &ptr->AutomaticBrowseQ, mDNS_DomainTypeBrowseAutomatic,     &ptr->domain, ptr->InterfaceID, FoundDomain, ptr);
+				if (err1 || err2 || err3 || err4 || err5)
+					LogMsg("uDNS_SetupSearchDomains: GetDomains for domain %##s returned error(s):\n"
+						   "%d (mDNS_DomainTypeBrowse)\n"
+						   "%d (mDNS_DomainTypeBrowseDefault)\n"
+						   "%d (mDNS_DomainTypeRegistration)\n"
+						   "%d (mDNS_DomainTypeRegistrationDefault)"
+						   "%d (mDNS_DomainTypeBrowseAutomatic)\n",
+						   ptr->domain.c, err1, err2, err3, err4, err5);
+				ptr->flag |= SLE_WAB_QUERY_STARTED;
+				}
+			}
+
+		p = &ptr->next;
+		}
+	return mStatus_NoError;
+	}
+
+mDNSexport domainname  *uDNS_GetNextSearchDomain(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSs8 *searchIndex, mDNSBool ignoreDotLocal)
+	{
+	SearchListElem *p = SearchList;
+	int count = *searchIndex;
+	(void) m; // unused
+
+	if (count < 0) { LogMsg("uDNS_GetNextSearchDomain: count %d less than zero", count); return mDNSNULL; }
+
+	// skip the  domains that we already looked at before
+	for (; count; count--) p = p->next;
+
+	while (p)
+		{
+		int labels = CountLabels(&p->domain);
+		if (labels > 0)
+			{
+			const domainname *d = SkipLeadingLabels(&p->domain, labels - 1);
+			if (SameDomainLabel(d->c, (const mDNSu8 *)"\x4""arpa"))
+				{
+				LogInfo("uDNS_GetNextSearchDomain: skipping search domain %##s, InterfaceID %p", p->domain.c, p->InterfaceID);
+				(*searchIndex)++;
+				p = p->next;
+				continue;
+				}
+			if (ignoreDotLocal && SameDomainLabel(d->c, (const mDNSu8 *)"\x5""local"))
+				{
+				LogInfo("uDNS_GetNextSearchDomain: skipping local domain %##s, InterfaceID %p", p->domain.c, p->InterfaceID);
+				(*searchIndex)++;
+				p = p->next;
+				continue;
+				}
+			}
+		// Point to the next one in the list which we will look at next time.
+		(*searchIndex)++;
+		// When we are appending search domains in a ActiveDirectory domain, the question's InterfaceID
+		// set to mDNSInterface_Unicast. Match the unscoped entries in that case.
+		if (((InterfaceID == mDNSInterface_Unicast) && (p->InterfaceID == mDNSInterface_Any)) ||
+			p->InterfaceID == InterfaceID)
+			{
+			LogInfo("uDNS_GetNextSearchDomain returning domain %##s, InterfaceID %p", p->domain.c, p->InterfaceID);
+			return &p->domain;
+			}
+		LogInfo("uDNS_GetNextSearchDomain skipping domain %##s, InterfaceID %p", p->domain.c, p->InterfaceID);
+		p = p->next;
+		}
+	return mDNSNULL;
+	}
+
+mDNSlocal void FlushAddressCacheRecords(mDNS *const m)
+	{
+	mDNSu32 slot;
+	CacheGroup *cg;
+	CacheRecord *cr;
+	FORALL_CACHERECORDS(slot, cg, cr)
+		{
+		if (cr->resrec.InterfaceID) continue;
+
+		// If a resource record can answer A or AAAA, they need to be flushed so that we will
+		// never used to deliver an ADD or RMV
+		if (RRTypeAnswersQuestionType(&cr->resrec, kDNSType_A) ||
+			RRTypeAnswersQuestionType(&cr->resrec, kDNSType_AAAA))
+			{
+			LogInfo("FlushAddressCacheRecords: Purging Resourcerecord %s", CRDisplayString(m, cr));
+			mDNS_PurgeCacheResourceRecord(m, cr);
+			}
+		}
+	}
+
+// Retry questions which has seach domains appended
+mDNSexport void RetrySearchDomainQuestions(mDNS *const m)
+	{
+	// Purge all the A/AAAA cache records and restart the queries. mDNSCoreRestartAddressQueries
+	// does this. When we restart the question,  we first want to try the new search domains rather
+	// than use the entries that is already in the cache. When we appended search domains, we might
+	// have created cache entries which is no longer valid as there are new search domains now
+
+	LogInfo("RetrySearchDomainQuestions: Calling mDNSCoreRestartAddressQueries");
+	mDNSCoreRestartAddressQueries(m, mDNStrue, FlushAddressCacheRecords, mDNSNULL, mDNSNULL);
+	}
+
+// Construction of Default Browse domain list (i.e. when clients pass NULL) is as follows:
+// 1) query for b._dns-sd._udp.local on LocalOnly interface
+//    (.local manually generated via explicit callback)
+// 2) for each search domain (from prefs pane), query for b._dns-sd._udp.<searchdomain>.
+// 3) for each result from (2), register LocalOnly PTR record b._dns-sd._udp.local. -> <result>
+// 4) result above should generate a callback from question in (1).  result added to global list
+// 5) global list delivered to client via GetSearchDomainList()
+// 6) client calls to enumerate domains now go over LocalOnly interface
+//    (!!!KRS may add outgoing interface in addition)
+
+struct CompileTimeAssertionChecks_uDNS
+	{
+	// Check our structures are reasonable sizes. Including overly-large buffers, or embedding
+	// other overly-large structures instead of having a pointer to them, can inadvertently
+	// cause structure sizes (and therefore memory usage) to balloon unreasonably.
+	char sizecheck_tcpInfo_t     [(sizeof(tcpInfo_t)      <=  9056) ? 1 : -1];
+	char sizecheck_SearchListElem[(sizeof(SearchListElem) <=  5000) ? 1 : -1];
+	};
diff --git a/mdnsresponder/mDNSCore/uDNS.h b/mdnsresponder/mDNSCore/uDNS.h
new file mode 100755
index 0000000..2dfaf51
--- /dev/null
+++ b/mdnsresponder/mDNSCore/uDNS.h
@@ -0,0 +1,132 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+#ifndef __UDNS_H_
+#define __UDNS_H_
+
+#include "mDNSEmbeddedAPI.h"
+#include "DNSCommon.h"
+
+#ifdef	__cplusplus
+	extern "C" {
+#endif
+
+#define RESTART_GOODBYE_DELAY    (6 * mDNSPlatformOneSecond) // delay after restarting LLQ before nuking previous known answers (avoids flutter if we restart before we have networking up)
+#define INIT_UCAST_POLL_INTERVAL (3 * mDNSPlatformOneSecond) // this interval is used after send failures on network transitions
+	                                                         // which typically heal quickly, so we start agressively and exponentially back off
+#define MAX_UCAST_POLL_INTERVAL (60 * 60 * mDNSPlatformOneSecond)
+//#define MAX_UCAST_POLL_INTERVAL (1 * 60 * mDNSPlatformOneSecond)
+#define LLQ_POLL_INTERVAL       (15 * 60 * mDNSPlatformOneSecond) // Polling interval for zones w/ an advertised LLQ port (ie not static zones) if LLQ fails due to NAT, etc.
+#define RESPONSE_WINDOW (60 * mDNSPlatformOneSecond)         // require server responses within one minute of request
+#define MAX_UCAST_UNANSWERED_QUERIES 2                       // the number of unanswered queries from any one uDNS server before trying another server
+#define DNSSERVER_PENALTY_TIME (60 * mDNSPlatformOneSecond) // number of seconds for which new questions don't pick this server
+
+#define DEFAULT_UPDATE_LEASE 7200
+
+#define QuestionIntervalStep 3
+#define QuestionIntervalStep2 (QuestionIntervalStep*QuestionIntervalStep)
+#define QuestionIntervalStep3 (QuestionIntervalStep*QuestionIntervalStep*QuestionIntervalStep)
+#define InitialQuestionInterval ((mDNSPlatformOneSecond + QuestionIntervalStep-1) / QuestionIntervalStep)
+
+// For Unicast record registrations, we initialize the interval to 1 second. When we send any query for
+// the record registration e.g., GetZoneData, we always back off by QuestionIntervalStep
+// so that the first retry does not happen until 3 seconds which should be enough for TCP/TLS to be done.
+#define INIT_RECORD_REG_INTERVAL (1 * mDNSPlatformOneSecond)
+#define MAX_RECORD_REG_INTERVAL	(15 * 60 * mDNSPlatformOneSecond)
+#define MERGE_DELAY_TIME	(1 * mDNSPlatformOneSecond)
+
+// If we are refreshing, we do it at least 5 times with a min update frequency of 
+// 5 minutes
+#define MAX_UPDATE_REFRESH_COUNT	5
+#define MIN_UPDATE_REFRESH_TIME		(5 * 60 * mDNSPlatformOneSecond)
+
+// For questions that use kDNSServiceFlagsTimeout and we don't have a matching resolver e.g., no dns servers,
+// then use the default value of 30 seconds
+#define DEFAULT_UDNS_TIMEOUT	30 // in seconds
+
+// Entry points into unicast-specific routines
+
+extern void LLQGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneInfo);
+extern void startLLQHandshake(mDNS *m, DNSQuestion *q);
+extern void sendLLQRefresh(mDNS *m, DNSQuestion *q);
+
+extern void SleepRecordRegistrations(mDNS *m);
+
+// uDNS_UpdateRecord
+// following fields must be set, and the update validated, upon entry.
+// rr->NewRData
+// rr->newrdlength
+// rr->UpdateCallback
+
+extern mStatus uDNS_UpdateRecord(mDNS *m, AuthRecord *rr);
+
+extern void SetNextQueryTime(mDNS *const m, const DNSQuestion *const q);
+extern CacheGroup *CacheGroupForName(const mDNS *const m, const mDNSu32 slot, const mDNSu32 namehash, const domainname *const name);
+extern mStatus mDNS_Register_internal(mDNS *const m, AuthRecord *const rr);
+extern mStatus mDNS_Deregister_internal(mDNS *const m, AuthRecord *const rr, mDNS_Dereg_type drt);
+extern mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const question);
+extern mStatus mDNS_StopQuery_internal(mDNS *const m, DNSQuestion *const question);
+extern mStatus mDNS_StartNATOperation_internal(mDNS *const m, NATTraversalInfo *traversal);
+
+extern void RecordRegistrationGotZoneData(mDNS *const m, mStatus err, const ZoneData *zoneData);
+extern mStatus uDNS_DeregisterRecord(mDNS *const m, AuthRecord *const rr);
+extern const domainname *GetServiceTarget(mDNS *m, AuthRecord *const rr);
+extern void uDNS_CheckCurrentQuestion(mDNS *const m);
+
+// integer fields of msg header must be in HOST byte order before calling this routine
+extern void uDNS_ReceiveMsg(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end,
+	const mDNSAddr *const srcaddr, const mDNSIPPort srcport);
+
+extern void uDNS_Tasks(mDNS *const m);
+extern void UpdateAllSRVRecords(mDNS *m);
+extern void CheckNATMappings(mDNS *m);
+
+extern mStatus         uDNS_SetupDNSConfig(mDNS *const m);
+
+// uDNS_SetupSearchDomains by default adds search domains. It also can be called with one or
+// more values for "action" which does the following:
+//
+// -UDNS_START_WAB_QUERY - start Wide Area Bonjour (domain enumeration) queries
+
+#define UDNS_START_WAB_QUERY    0x00000001
+
+extern mStatus         uDNS_SetupSearchDomains(mDNS *const m, int action);
+extern domainname      *uDNS_GetNextSearchDomain(mDNS *const m, mDNSInterfaceID InterfaceID, mDNSs8 *searchIndex, mDNSBool ignoreDotLocal);
+
+typedef enum
+	{
+	uDNS_LLQ_Not = 0,	// Normal uDNS answer: Flush any stale records from cache, and respect record TTL
+	uDNS_LLQ_Ignore,	// LLQ initial challenge packet: ignore -- has no useful records for us
+	uDNS_LLQ_Entire,	// LLQ initial set of answers: Flush any stale records from cache, but assume TTL is 2 x LLQ refresh interval
+	uDNS_LLQ_Events		// LLQ event packet: don't flush cache; assume TTL is 2 x LLQ refresh interval
+	} uDNS_LLQType;
+
+extern uDNS_LLQType    uDNS_recvLLQResponse(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *const srcaddr, const mDNSIPPort srcport, DNSQuestion **matchQuestion);
+extern DomainAuthInfo *GetAuthInfoForName_internal(mDNS *m, const domainname *const name);
+extern DomainAuthInfo *GetAuthInfoForQuestion(mDNS *m, const DNSQuestion *const q);
+extern void DisposeTCPConn(struct tcpInfo_t *tcp);
+
+// NAT traversal
+extern void	uDNS_ReceiveNATPMPPacket(mDNS *m, const mDNSInterfaceID InterfaceID, mDNSu8 *pkt, mDNSu16 len);	// Called for each received NAT-PMP packet
+extern void	natTraversalHandleAddressReply(mDNS *const m, mDNSu16 err, mDNSv4Addr ExtAddr);
+extern void	natTraversalHandlePortMapReply(mDNS *const m, NATTraversalInfo *n, const mDNSInterfaceID InterfaceID, mDNSu16 err, mDNSIPPort extport, mDNSu32 lease);
+
+#ifdef	__cplusplus
+	}
+#endif
+
+#endif // __UDNS_H_
diff --git a/mdnsresponder/mDNSPosix/Client.c b/mdnsresponder/mDNSPosix/Client.c
new file mode 100755
index 0000000..c21b1ee
--- /dev/null
+++ b/mdnsresponder/mDNSPosix/Client.c
@@ -0,0 +1,223 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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 <assert.h>
+#include <signal.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+#include "mDNSEmbeddedAPI.h"// Defines the interface to the mDNS core code
+#include "mDNSPosix.h"    // Defines the specific types needed to run mDNS on this platform
+#include "ExampleClientApp.h"
+
+// Globals
+static mDNS mDNSStorage;       // mDNS core uses this to store its globals
+static mDNS_PlatformSupport PlatformStorage;  // Stores this platform's globals
+#define RR_CACHE_SIZE 500
+static CacheEntity gRRCache[RR_CACHE_SIZE];
+
+mDNSexport const char ProgramName[] = "mDNSClientPosix";
+
+static const char *gProgramName = ProgramName;
+
+static void BrowseCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
+    // A callback from the core mDNS code that indicates that we've received a 
+    // response to our query.  Note that this code runs on the main thread 
+    // (in fact, there is only one thread!), so we can safely printf the results.
+{
+    domainlabel name;
+    domainname  type;
+    domainname  domain;
+	char nameC  [MAX_DOMAIN_LABEL+1];			// Unescaped name: up to 63 bytes plus C-string terminating NULL.
+	char typeC  [MAX_ESCAPED_DOMAIN_NAME];
+	char domainC[MAX_ESCAPED_DOMAIN_NAME];
+    const char *state;
+
+	(void)m;		// Unused
+	(void)question;	// Unused
+
+    assert(answer->rrtype == kDNSType_PTR);
+
+    DeconstructServiceName(&answer->rdata->u.name, &name, &type, &domain);
+
+    ConvertDomainLabelToCString_unescaped(&name, nameC);
+    ConvertDomainNameToCString(&type, typeC);
+    ConvertDomainNameToCString(&domain, domainC);
+
+    // If the TTL has hit 0, the service is no longer available.
+    if (!AddRecord) {
+        state = "Lost ";
+    } else {
+        state = "Found";
+    }
+    fprintf(stderr, "*** %s name = '%s', type = '%s', domain = '%s'\n", state, nameC, typeC, domainC);
+}
+
+static mDNSBool CheckThatServiceTypeIsUsable(const char *serviceType, mDNSBool printExplanation)
+    // Checks that serviceType is a reasonable service type 
+    // label and, if it isn't and printExplanation is true, prints 
+    // an explanation of why not.
+{
+    mDNSBool result;
+    
+    result = mDNStrue;
+    if (result && strlen(serviceType) > 63) {
+        if (printExplanation) {
+            fprintf(stderr, 
+                    "%s: Service type specified by -t is too long (must be 63 characters or less)\n", 
+                    gProgramName);
+        }
+        result = mDNSfalse;
+    }
+    if (result && serviceType[0] == 0) {
+        if (printExplanation) {
+            fprintf(stderr, 
+                    "%s: Service type specified by -t can't be empty\n", 
+                    gProgramName);
+        }
+        result = mDNSfalse;
+    }
+    return result;
+}
+
+static const char kDefaultServiceType[] = "_afpovertcp._tcp";
+static const char kDefaultDomain[]      = "local.";
+
+static void PrintUsage()
+{
+    fprintf(stderr, 
+            "Usage: %s [-v level] [-t type] [-d domain]\n",
+            gProgramName);
+    fprintf(stderr, "          -v verbose mode, level is a number from 0 to 2\n");
+    fprintf(stderr, "             0 = no debugging info (default)\n");
+    fprintf(stderr, "             1 = standard debugging info\n");
+    fprintf(stderr, "             2 = intense debugging info\n");
+    fprintf(stderr, "          -t uses 'type' as the service type (default is '%s')\n", kDefaultServiceType);
+    fprintf(stderr, "          -d uses 'domain' as the domain to browse (default is '%s')\n", kDefaultDomain);
+}
+
+static const char *gServiceType      = kDefaultServiceType;
+static const char *gServiceDomain    = kDefaultDomain;
+
+static void ParseArguments(int argc, char **argv)
+    // Parses our command line arguments into the global variables 
+    // listed above.
+{
+    int ch;
+    
+    // Set gProgramName to the last path component of argv[0]
+    
+    gProgramName = strrchr(argv[0], '/');
+    if (gProgramName == NULL) {
+        gProgramName = argv[0];
+    } else {
+        gProgramName += 1;
+    }
+    
+    // Parse command line options using getopt.
+    
+    do {
+        ch = getopt(argc, argv, "v:t:d:");
+        if (ch != -1) {
+            switch (ch) {
+                case 'v':
+                    gMDNSPlatformPosixVerboseLevel = atoi(optarg);
+                    if (gMDNSPlatformPosixVerboseLevel < 0 || gMDNSPlatformPosixVerboseLevel > 2) {
+                        fprintf(stderr, 
+                                "%s: Verbose mode must be in the range 0..2\n", 
+                                gProgramName);
+                        exit(1);
+                    }
+                    break;
+                case 't':
+                    gServiceType = optarg;
+                    if ( ! CheckThatServiceTypeIsUsable(gServiceType, mDNStrue) ) {
+                        exit(1);
+                    }
+                    break;
+                case 'd':
+                    gServiceDomain = optarg;
+                    break;
+                case '?':
+                default:
+                    PrintUsage();
+                    exit(1);
+                    break;
+            }
+        }
+    } while (ch != -1);
+
+    // Check for any left over command line arguments.
+    
+    if (optind != argc) {
+        fprintf(stderr, "%s: Unexpected argument '%s'\n", gProgramName, argv[optind]);
+        exit(1);
+    }
+}
+
+int main(int argc, char **argv)
+    // The program's main entry point.  The program does a trivial 
+    // mDNS query, looking for all AFP servers in the local domain.
+{
+    int     result;
+    mStatus     status;
+    DNSQuestion question;
+    domainname  type;
+    domainname  domain;
+
+    // Parse our command line arguments.  This won't come back if there's an error.
+    ParseArguments(argc, argv);
+
+    // Initialise the mDNS core.
+	status = mDNS_Init(&mDNSStorage, &PlatformStorage,
+    	gRRCache, RR_CACHE_SIZE,
+    	mDNS_Init_DontAdvertiseLocalAddresses,
+    	mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext);
+    if (status == mStatus_NoError) {
+    
+        // Construct and start the query.
+        
+        MakeDomainNameFromDNSNameString(&type, gServiceType);
+        MakeDomainNameFromDNSNameString(&domain, gServiceDomain);
+
+        status = mDNS_StartBrowse(&mDNSStorage, &question, &type, &domain, mDNSInterface_Any, mDNSfalse, BrowseCallback, NULL);
+    
+        // Run the platform main event loop until the user types ^C. 
+        // The BrowseCallback routine is responsible for printing 
+        // any results that we find.
+        
+        if (status == mStatus_NoError) {
+            fprintf(stderr, "Hit ^C when you're bored waiting for responses.\n");
+        	ExampleClientEventLoop(&mDNSStorage);
+            mDNS_StopQuery(&mDNSStorage, &question);
+			mDNS_Close(&mDNSStorage);
+        }
+    }
+    
+    if (status == mStatus_NoError) {
+        result = 0;
+    } else {
+        result = 2;
+    }
+    if ( (result != 0) || (gMDNSPlatformPosixVerboseLevel > 0) ) {
+        fprintf(stderr, "%s: Finished with status %d, result %d\n", gProgramName, (int)status, result);
+    }
+
+    return 0;
+}
diff --git a/mdnsresponder/mDNSPosix/ExampleClientApp.c b/mdnsresponder/mDNSPosix/ExampleClientApp.c
new file mode 100644
index 0000000..e05b541
--- /dev/null
+++ b/mdnsresponder/mDNSPosix/ExampleClientApp.c
@@ -0,0 +1,91 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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 <stdio.h>			// For printf()
+#include <stdlib.h>			// For exit() etc.
+#include <string.h>			// For strlen() etc.
+#include <unistd.h>			// For select()
+#include <errno.h>			// For errno, EINTR
+#include <netinet/in.h>		// For INADDR_NONE
+#include <arpa/inet.h>		// For inet_addr()
+#include <netdb.h>			// For gethostbyname()
+#include <signal.h>			// For SIGINT, etc.
+
+#include "mDNSEmbeddedAPI.h"  // Defines the interface to the client layer above
+#include "mDNSPosix.h"      // Defines the specific types needed to run mDNS on this platform
+
+//*******************************************************************************************
+// Main
+
+static volatile mDNSBool StopNow;
+
+mDNSlocal void HandleSIG(int signal)
+	{
+	(void)signal;	// Unused
+	debugf("%s","");
+	debugf("HandleSIG");
+	StopNow = mDNStrue;
+	}
+
+mDNSexport void ExampleClientEventLoop(mDNS *const m)
+	{
+	signal(SIGINT, HandleSIG);	// SIGINT is what you get for a Ctrl-C
+	signal(SIGTERM, HandleSIG);
+
+	while (!StopNow)
+		{
+		int nfds = 0;
+		fd_set readfds;
+		struct timeval timeout;
+		int result;
+		
+		// 1. Set up the fd_set as usual here.
+		// This example client has no file descriptors of its own,
+		// but a real application would call FD_SET to add them to the set here
+		FD_ZERO(&readfds);
+		
+		// 2. Set up the timeout.
+		// This example client has no other work it needs to be doing,
+		// so we set an effectively infinite timeout
+		timeout.tv_sec = 0x3FFFFFFF;
+		timeout.tv_usec = 0;
+		
+		// 3. Give the mDNSPosix layer a chance to add its information to the fd_set and timeout
+		mDNSPosixGetFDSet(m, &nfds, &readfds, &timeout);
+		
+		// 4. Call select as normal
+		verbosedebugf("select(%d, %d.%06d)", nfds, timeout.tv_sec, timeout.tv_usec);
+		result = select(nfds, &readfds, NULL, NULL, &timeout);
+		
+		if (result < 0)
+			{
+			verbosedebugf("select() returned %d errno %d", result, errno);
+			if (errno != EINTR) StopNow = mDNStrue;
+			}
+		else
+			{
+			// 5. Call mDNSPosixProcessFDSet to let the mDNSPosix layer do its work
+			mDNSPosixProcessFDSet(m, &readfds);
+			
+			// 6. This example client has no other work it needs to be doing,
+			// but a real client would do its work here
+			// ... (do work) ...
+			}
+		}
+
+	debugf("Exiting");
+	}
diff --git a/mdnsresponder/mDNSPosix/ExampleClientApp.h b/mdnsresponder/mDNSPosix/ExampleClientApp.h
new file mode 100644
index 0000000..ab643af
--- /dev/null
+++ b/mdnsresponder/mDNSPosix/ExampleClientApp.h
@@ -0,0 +1,18 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+extern void ExampleClientEventLoop(mDNS *const m);
diff --git a/mdnsresponder/mDNSPosix/Identify.c b/mdnsresponder/mDNSPosix/Identify.c
new file mode 100644
index 0000000..66f7410
--- /dev/null
+++ b/mdnsresponder/mDNSPosix/Identify.c
@@ -0,0 +1,378 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ *
+ * Formatting notes:
+ * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion
+ * on C indentation can be found on the web, such as <http://www.kafejo.com/komp/1tbs.htm>,
+ * but for the sake of brevity here I will say just this: Curly braces are not syntactially
+ * part of an "if" statement; they are the beginning and ending markers of a compound statement;
+ * therefore common sense dictates that if they are part of a compound statement then they
+ * should be indented to the same level as everything else in that compound statement.
+ * Indenting curly braces at the same level as the "if" implies that curly braces are
+ * part of the "if", which is false. (This is as misleading as people who write "char* x,y;"
+ * thinking that variables x and y are both of type "char*" -- and anyone who doesn't
+ * understand why variable y is not of type "char*" just proves the point that poor code
+ * layout leads people to unfortunate misunderstandings about how the C language really works.)
+ */
+
+//*************************************************************************************************************
+// Incorporate mDNS.c functionality
+
+// We want to use the functionality provided by "mDNS.c",
+// except we'll sneak a peek at the packets before forwarding them to the normal mDNSCoreReceive() routine
+#define mDNSCoreReceive __MDNS__mDNSCoreReceive
+#include "mDNS.c"
+#undef mDNSCoreReceive
+
+//*************************************************************************************************************
+// Headers
+
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <netinet/in_systm.h>		// For n_long, required by <netinet/ip.h> below
+#include <netinet/ip.h>				// For IPTOS_LOWDELAY etc.
+#include <arpa/inet.h>
+#include <signal.h>
+
+#include "mDNSEmbeddedAPI.h"// Defines the interface to the mDNS core code
+#include "mDNSPosix.h"    // Defines the specific types needed to run mDNS on this platform
+#include "ExampleClientApp.h"
+
+//*************************************************************************************************************
+// Globals
+
+static mDNS mDNSStorage;       // mDNS core uses this to store its globals
+static mDNS_PlatformSupport PlatformStorage;  // Stores this platform's globals
+#define RR_CACHE_SIZE 500
+static CacheEntity gRRCache[RR_CACHE_SIZE];
+mDNSexport const char ProgramName[] = "mDNSIdentify";
+
+static volatile int StopNow;	// 0 means running, 1 means stop because we got an answer, 2 means stop because of Ctrl-C
+static volatile int NumAnswers, NumAddr, NumAAAA, NumHINFO;
+static char hostname[MAX_ESCAPED_DOMAIN_NAME], hardware[256], software[256];
+static mDNSAddr lastsrc, hostaddr, target;
+static mDNSOpaque16 lastid, id;
+
+//*************************************************************************************************************
+// Utilities
+
+// Special version of printf that knows how to print IP addresses, DNS-format name strings, etc.
+mDNSlocal mDNSu32 mprintf(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2);
+mDNSlocal mDNSu32 mprintf(const char *format, ...)
+	{
+	mDNSu32 length;
+	unsigned char buffer[512];
+	va_list ptr;
+	va_start(ptr,format);
+	length = mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr);
+	va_end(ptr);
+	printf("%s", buffer);
+	return(length);
+	}
+
+//*************************************************************************************************************
+// Main code
+
+mDNSexport void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end,
+	const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *const dstaddr, const mDNSIPPort dstport,
+	const mDNSInterfaceID InterfaceID)
+	{
+	(void)dstaddr; // Unused
+	// Snag copy of header ID, then call through
+	lastid = msg->h.id;
+	lastsrc = *srcaddr;
+
+	// We *want* to allow off-net unicast responses here.
+	// For now, the simplest way to allow that is to pretend it was received via multicast so that mDNSCore doesn't reject the packet
+	__MDNS__mDNSCoreReceive(m, msg, end, srcaddr, srcport, &AllDNSLinkGroup_v4, dstport, InterfaceID);
+	}
+
+mDNSlocal void NameCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
+	{
+	(void)m;		// Unused
+	(void)question;	// Unused
+	(void)AddRecord;// Unused
+	if (!id.NotAnInteger) id = lastid;
+	if (answer->rrtype == kDNSType_PTR || answer->rrtype == kDNSType_CNAME)
+		{
+		ConvertDomainNameToCString(&answer->rdata->u.name, hostname);
+		StopNow = 1;
+		mprintf("%##s %s %##s\n", answer->name->c, DNSTypeName(answer->rrtype), answer->rdata->u.name.c);
+		}
+	}
+
+mDNSlocal void InfoCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
+	{
+	(void)m;		// Unused
+	(void)question;	// Unused
+	(void)AddRecord;// Unused
+	if (answer->rrtype == kDNSType_A)
+		{
+		if (!id.NotAnInteger) id = lastid;
+		NumAnswers++;
+		NumAddr++;
+		mprintf("%##s %s %.4a\n", answer->name->c, DNSTypeName(answer->rrtype), &answer->rdata->u.ipv4);
+		hostaddr.type = mDNSAddrType_IPv4;	// Prefer v4 target to v6 target, for now
+		hostaddr.ip.v4 = answer->rdata->u.ipv4;
+		}
+	else if (answer->rrtype == kDNSType_AAAA)
+		{
+		if (!id.NotAnInteger) id = lastid;
+		NumAnswers++;
+		NumAAAA++;
+		mprintf("%##s %s %.16a\n", answer->name->c, DNSTypeName(answer->rrtype), &answer->rdata->u.ipv6);
+		if (!hostaddr.type)	// Prefer v4 target to v6 target, for now
+			{
+			hostaddr.type = mDNSAddrType_IPv6;
+			hostaddr.ip.v6 = answer->rdata->u.ipv6;
+			}
+		}
+	else if (answer->rrtype == kDNSType_HINFO)
+		{
+		mDNSu8 *p = answer->rdata->u.data;
+		strncpy(hardware, (char*)(p+1), p[0]);
+		hardware[p[0]] = 0;
+		p += 1 + p[0];
+		strncpy(software, (char*)(p+1), p[0]);
+		software[p[0]] = 0;
+		NumAnswers++;
+		NumHINFO++;
+		}
+
+	// If we've got everything we're looking for, don't need to wait any more
+	if (/*NumHINFO && */ (NumAddr || NumAAAA)) StopNow = 1;
+	}
+
+mDNSlocal void ServicesCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
+	{
+	(void)m;		// Unused
+	(void)question;	// Unused
+	(void)AddRecord;// Unused
+	// Right now the mDNSCore targeted-query code is incomplete --
+	// it issues targeted queries, but accepts answers from anywhere
+	// For now, we'll just filter responses here so we don't get confused by responses from someone else
+	if (answer->rrtype == kDNSType_PTR && mDNSSameAddress(&lastsrc, &target))
+		{
+		NumAnswers++;
+		mprintf("%##s %s %##s\n", answer->name->c, DNSTypeName(answer->rrtype), answer->rdata->u.name.c);
+		}
+	}
+
+mDNSlocal void WaitForAnswer(mDNS *const m, int seconds)
+	{
+	struct timeval end;
+	gettimeofday(&end, NULL);
+	end.tv_sec += seconds;
+	StopNow = 0;
+	NumAnswers = 0;
+	while (!StopNow)
+		{
+		int nfds = 0;
+		fd_set readfds;
+		struct timeval now, remain = end;
+		int result;
+
+		FD_ZERO(&readfds);
+		gettimeofday(&now, NULL);
+		if (remain.tv_usec < now.tv_usec) { remain.tv_usec += 1000000; remain.tv_sec--; }
+		if (remain.tv_sec < now.tv_sec)
+			{
+			if (!NumAnswers) printf("No response after %d seconds\n", seconds);
+			return;
+			}
+		remain.tv_usec -= now.tv_usec;
+		remain.tv_sec  -= now.tv_sec;
+		mDNSPosixGetFDSet(m, &nfds, &readfds, &remain);
+		result = select(nfds, &readfds, NULL, NULL, &remain);
+		if (result >= 0) mDNSPosixProcessFDSet(m, &readfds);
+		else if (errno != EINTR) StopNow = 2;
+		}
+	}
+
+mDNSlocal mStatus StartQuery(DNSQuestion *q, char *qname, mDNSu16 qtype, const mDNSAddr *target, mDNSQuestionCallback callback)
+	{
+	lastsrc = zeroAddr;
+	if (qname) MakeDomainNameFromDNSNameString(&q->qname, qname);
+	q->InterfaceID      = mDNSInterface_Any;
+	q->Target           = target ? *target : zeroAddr;
+	q->TargetPort       = MulticastDNSPort;
+	q->TargetQID        = zeroID;
+	q->qtype            = qtype;
+	q->qclass           = kDNSClass_IN;
+	q->LongLived        = mDNSfalse;
+	q->ExpectUnique     = mDNSfalse;	// Don't want to stop after the first response packet
+	q->ForceMCast       = mDNStrue;		// Query via multicast, even for apparently uDNS names like 1.1.1.17.in-addr.arpa.
+	q->ReturnIntermed   = mDNStrue;
+	q->SuppressUnusable = mDNSfalse;
+	q->SearchListIndex  = 0;
+	q->AppendSearchDomains = 0;
+	q->RetryWithSearchDomains = mDNSfalse;
+	q->TimeoutQuestion  = 0;
+	q->WakeOnResolve    = 0;
+	q->qnameOrig        = mDNSNULL;
+	q->QuestionCallback = callback;
+	q->QuestionContext  = NULL;
+
+	//mprintf("%##s %s ?\n", q->qname.c, DNSTypeName(qtype));
+	return(mDNS_StartQuery(&mDNSStorage, q));
+	}
+
+mDNSlocal void DoOneQuery(DNSQuestion *q, char *qname, mDNSu16 qtype, const mDNSAddr *target, mDNSQuestionCallback callback)
+	{
+	mStatus status = StartQuery(q, qname, qtype, target, callback);
+	if (status != mStatus_NoError)
+		StopNow = 2;
+	else
+		{
+		WaitForAnswer(&mDNSStorage, 4);
+		mDNS_StopQuery(&mDNSStorage, q);
+		}
+	}
+
+mDNSlocal int DoQuery(DNSQuestion *q, char *qname, mDNSu16 qtype, const mDNSAddr *target, mDNSQuestionCallback callback)
+	{
+	DoOneQuery(q, qname, qtype, target, callback);
+	if (StopNow == 0 && NumAnswers == 0 && target && target->type)
+		{
+		mprintf("%##s %s Trying multicast\n", q->qname.c, DNSTypeName(q->qtype));
+		DoOneQuery(q, qname, qtype, NULL, callback);
+		}
+	if (StopNow == 0 && NumAnswers == 0)
+		mprintf("%##s %s *** No Answer ***\n", q->qname.c, DNSTypeName(q->qtype));
+	return(StopNow);
+	}
+
+mDNSlocal void HandleSIG(int signal)
+	{
+	(void)signal;	// Unused
+	debugf("%s","");
+	debugf("HandleSIG");
+	StopNow = 2;
+	}
+
+mDNSexport int main(int argc, char **argv)
+	{
+	const char *progname = strrchr(argv[0], '/') ? strrchr(argv[0], '/') + 1 : argv[0];
+	int this_arg = 1;
+	mStatus status;
+	struct in_addr s4;
+#if HAVE_IPV6
+	struct in6_addr s6;
+#endif
+	char buffer[256];
+	DNSQuestion q;
+
+	if (argc < 2) goto usage;
+	
+	// Since this is a special command-line tool, we want LogMsg() errors to go to stderr, not syslog
+	mDNS_DebugMode = mDNStrue;
+	
+    // Initialise the mDNS core.
+	status = mDNS_Init(&mDNSStorage, &PlatformStorage,
+    	gRRCache, RR_CACHE_SIZE,
+    	mDNS_Init_DontAdvertiseLocalAddresses,
+    	mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext);
+	if (status) { fprintf(stderr, "Daemon start: mDNS_Init failed %d\n", (int)status); return(status); }
+
+	signal(SIGINT, HandleSIG);	// SIGINT is what you get for a Ctrl-C
+	signal(SIGTERM, HandleSIG);
+
+	while (this_arg < argc)
+		{
+		char *arg = argv[this_arg++];
+		if (this_arg > 2) printf("\n");
+
+		lastid = id = zeroID;
+		hostaddr = target = zeroAddr;
+		hostname[0] = hardware[0] = software[0] = 0;
+		NumAddr = NumAAAA = NumHINFO = 0;
+
+		if (inet_pton(AF_INET, arg, &s4) == 1)
+			{
+			mDNSu8 *p = (mDNSu8 *)&s4;
+			// Note: This is reverse order compared to a normal dotted-decimal IP address, so we can't use our customary "%.4a" format code
+			mDNS_snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d.in-addr.arpa.", p[3], p[2], p[1], p[0]);
+			printf("%s\n", buffer);
+			target.type = mDNSAddrType_IPv4;
+			target.ip.v4.NotAnInteger = s4.s_addr;
+			DoQuery(&q, buffer, kDNSType_PTR, &target, NameCallback);
+			if (StopNow == 2) break;
+			}
+#if HAVE_IPV6
+		else if (inet_pton(AF_INET6, arg, &s6) == 1)
+			{
+			int i;
+			mDNSu8 *p = (mDNSu8 *)&s6;
+			for (i = 0; i < 16; i++)
+				{
+				static const char hexValues[] = "0123456789ABCDEF";
+				buffer[i * 4    ] = hexValues[p[15-i] & 0x0F];
+				buffer[i * 4 + 1] = '.';
+				buffer[i * 4 + 2] = hexValues[p[15-i] >> 4];
+				buffer[i * 4 + 3] = '.';
+				}
+			mDNS_snprintf(&buffer[64], sizeof(buffer)-64, "ip6.arpa.");
+			target.type = mDNSAddrType_IPv6;
+			mDNSPlatformMemCopy(&target.ip.v6, &s6, sizeof(target.ip.v6));
+			DoQuery(&q, buffer, kDNSType_PTR, &target, NameCallback);
+			if (StopNow == 2) break;
+			}
+#endif
+		else {
+			if (strlen(arg) >= sizeof(hostname)) {
+				fprintf(stderr, "hostname must be < %d characters\n", (int)sizeof(hostname));
+				goto usage;
+			}
+			strcpy(hostname, arg);
+		}
+	
+		// Now we have the host name; get its A, AAAA, and HINFO
+		if (hostname[0]) DoQuery(&q, hostname, kDNSQType_ANY, &target, InfoCallback);
+		if (StopNow == 2) break;
+	
+		if (hardware[0] || software[0])
+			{
+			printf("HINFO Hardware: %s\n", hardware);
+			printf("HINFO Software: %s\n", software);
+			}
+		else if (NumAnswers) printf("%s has no HINFO record\n", hostname);
+		else printf("Incorrect dot-local hostname, address, or no mDNSResponder running on that machine\n");
+
+		if (NumAnswers)
+			{
+			// Because of the way we use lastsrc in ServicesCallback, we need to clear the cache to make sure we're getting fresh answers
+			mDNS *const m = &mDNSStorage;
+			mDNSu32 slot;
+			CacheGroup *cg;
+			CacheRecord *rr;
+			FORALL_CACHERECORDS(slot, cg, rr) mDNS_PurgeCacheResourceRecord(m, rr);
+			if (target.type == 0) target = hostaddr;		// Make sure the services query is targeted
+			DoQuery(&q, "_services._dns-sd._udp.local.", kDNSType_PTR, &target, ServicesCallback);
+			if (StopNow == 2) break;
+			}
+		}
+
+	mDNS_Close(&mDNSStorage);
+	return(0);
+
+usage:
+	fprintf(stderr, "Usage: %s <dot-local hostname> or <IPv4 address> or <IPv6 address> ...\n", progname);
+	return(-1);
+	}
diff --git a/mdnsresponder/mDNSPosix/Makefile b/mdnsresponder/mDNSPosix/Makefile
new file mode 100755
index 0000000..55d7f8d
--- /dev/null
+++ b/mdnsresponder/mDNSPosix/Makefile
@@ -0,0 +1,520 @@
+# -*- tab-width: 4 -*-
+#
+# Copyright (c) 2002-2004, Apple Computer, Inc. All rights reserved.
+#
+# Redistribution and use in source and binary forms, with or without 
+# modification, are permitted provided that the following conditions are met:
+#
+# 1.  Redistributions of source code must retain the above copyright notice, 
+#     this list of conditions and the following disclaimer. 
+# 2.  Redistributions in binary form must reproduce the above copyright notice, 
+#     this list of conditions and the following disclaimer in the documentation 
+#     and/or other materials provided with the distribution. 
+# 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of its 
+#     contributors may be used to endorse or promote products derived from this 
+#     software without specific prior written permission. 
+#
+# THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 
+# EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
+# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
+# DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 
+# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
+# (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
+# LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 
+# ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
+# SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+#
+# IMPORTANT NOTE: This is a Makefile for *GNU make*
+# On some systems, a different program may be the default "make" command.
+# If "make os=xxx" gives lots of errors like "Missing dependency operator",
+# then try typing "gmake os=xxx" instead.
+#
+# This Makefile builds an mDNSResponder daemon and a libdns_sd.so shared library 
+# for Linux. It also builds several example programs for embedded systems. 
+#
+# Make with no arguments to build all production targets.
+# 'make DEBUG=1' to build debugging targets.
+# 'make clean' or 'make clean DEBUG=1' to delete prod/debug objects & targets
+# 'sudo make install [DEBUG=1]' to install mdnsd daemon and libdns_sd.
+#
+# Notes:
+# $@ means "The file name of the target of the rule"
+# $< means "The name of the first prerequisite"
+# $* means "The stem with which an implicit rule matches"
+# $+ means "The names of all the prerequisites, with spaces between them, exactly as given"
+# For more magic automatic variables, see
+# <http://www.gnu.org/software/make/manual/html_node/Automatic-Variables.html>
+
+#############################################################################
+
+LIBVERS = 1
+
+COREDIR = ../mDNSCore
+SHAREDDIR ?= ../mDNSShared
+JDK = /usr/jdk
+
+CC = @cc
+BISON = @bison
+FLEX = @flex
+LD = ld -shared
+CP = cp
+RM = rm
+LN = ln -s -f
+CFLAGS_COMMON = -I$(COREDIR) -I$(SHAREDDIR) -I$(OBJDIR) -fwrapv -W -Wall -DPID_FILE=\"/var/run/mdnsd.pid\" -DMDNS_UDS_SERVERPATH=\"/var/run/mdnsd\"
+CFLAGS_PTHREAD =
+LINKOPTS =
+LINKOPTS_PTHREAD = -lpthread
+LDSUFFIX = so
+JAVACFLAGS_OS = -fPIC -shared -ldns_sd
+
+# Set up diverging paths for debug vs. prod builds
+DEBUG=0
+ifeq ($(DEBUG),1)
+CFLAGS_DEBUG = -g -DMDNS_DEBUGMSGS=2 
+OBJDIR = objects/debug
+BUILDDIR = build/debug
+STRIP = echo 
+else
+# We use -Os for two reasons:
+# 1. We want to make small binaries, suitable for putting into hardware devices
+# 2. Some of the code analysis warnings only work when some form of optimization is enabled
+CFLAGS_DEBUG = -Os -DMDNS_DEBUGMSGS=0 
+OBJDIR ?= objects/prod
+BUILDDIR ?= build/prod
+STRIP = strip -S 
+endif
+
+# Configure per-OS peculiarities
+ifeq ($(os),solaris)
+CFLAGS_DEBUG = -O0 -DMDNS_DEBUGMSGS=0
+CFLAGS_OS = -DNOT_HAVE_DAEMON -DNOT_HAVE_SA_LEN -DNOT_HAVE_SOCKLEN_T -DNOT_HAVE_IF_NAMETOINDEX \
+	 -DLOG_PERROR=0 -D_XPG4_2 -D__EXTENSIONS__ -DHAVE_BROKEN_RECVIF_NAME -DTARGET_OS_SOLARIS
+CC = gcc
+LD = gcc -shared
+LINKOPTS = -lsocket -lnsl -lresolv
+JAVACFLAGS_OS += -I$(JDK)/include/solaris
+ifneq ($(DEBUG),1)
+STRIP = strip
+endif
+else
+
+# any target that contains the string "linux"
+ifeq ($(findstring linux,$(os)),linux)
+CFLAGS_OS = -D_GNU_SOURCE -DHAVE_IPV6 -DNOT_HAVE_SA_LEN -DUSES_NETLINK -DHAVE_LINUX -DTARGET_OS_LINUX -fno-strict-aliasing
+LD = gcc -shared
+FLEXFLAGS_OS = -l
+JAVACFLAGS_OS += -I$(JDK)/include/linux
+
+# uClibc does not support Name Service Switch
+ifneq ($(os),linux-uclibc)
+OPTIONALTARG = nss_mdns
+OPTINSTALL   = InstalledNSS
+endif
+else
+
+ifeq ($(os),netbsd)
+CFLAGS_OS =
+LDCONFIG = ldconfig
+else
+
+ifeq ($(os),freebsd)
+# If not already defined, set LOCALBASE to /usr/local
+LOCALBASE?=/usr/local
+INSTBASE=$(LOCALBASE)
+CFLAGS_OS = -DHAVE_IPV6
+# FreeBSD 4 requires threaded code to be compiled and linked using the "-pthread" option,
+# and requires that the "-lpthread" link option NOT be used
+# This appies only to FreeBSD -- "man cc" on FreeBSD says:
+#   FreeBSD SPECIFIC OPTIONS
+#     -pthread
+#       Link a user-threaded process against libc_r instead of libc.
+CFLAGS_PTHREAD   = -pthread -D_THREAD_SAFE
+LINKOPTS_PTHREAD = -pthread
+JAVACFLAGS_OS += -I$(JDK)/include/freebsd
+LDCONFIG = ldconfig
+else
+
+ifeq ($(os),openbsd)
+CFLAGS_OS = -DHAVE_BROKEN_RECVDSTADDR
+LDCONFIG = ldconfig
+else
+
+ifeq ($(os),x)
+# We have to define __MAC_OS_X_VERSION_MIN_REQUIRED=__MAC_OS_X_VERSION_10_4 or on Leopard
+# we get build failures: ‘daemon’ is deprecated (declared at /usr/include/stdlib.h:283)
+CFLAGS_OS = -DHAVE_IPV6 -no-cpp-precomp -Werror -Wdeclaration-after-statement \
+	-D__MAC_OS_X_VERSION_MIN_REQUIRED=__MAC_OS_X_VERSION_10_4 \
+	-D__APPLE_USE_RFC_2292 #-Wunreachable-code
+CC = gcc
+LD = $(CC) -dynamiclib
+LINKOPTS = -lSystem
+LDSUFFIX = dylib
+JDK = /System/Library/Frameworks/JavaVM.framework/Home
+JAVACFLAGS_OS = -dynamiclib -I/System/Library/Frameworks/JavaVM.framework/Headers -framework JavaVM 
+else
+
+$(error ERROR: Must specify target OS on command-line, e.g. "make os=x [target]".\
+Supported operating systems include: x, linux, linux-uclibc, netbsd, freebsd, openbsd, solaris) 
+endif
+endif
+endif
+endif
+endif
+endif
+
+NSSLIBNAME  := libnss_mdns
+NSSVERSION  := 0.2
+NSSLIBFILE  := $(NSSLIBNAME)-$(NSSVERSION).so
+NSSLINKNAME := $(NSSLIBNAME).so.2
+NSSINSTPATH := /lib
+
+# If not otherwise defined, we install into /usr/lib and /usr/include
+# and our startup script is called mdns (e.g. /etc/init.d/mdns)
+INSTBASE?=/usr
+STARTUPSCRIPTNAME?=mdns
+
+ifeq ($(HAVE_IPV6),1)
+CFLAGS_OS += -DHAVE_IPV6=1
+else
+ifeq ($(HAVE_IPV6),0)
+CFLAGS_OS += -DHAVE_IPV6=0
+endif
+endif
+
+# If directory /usr/share/man exists, then we install man pages into that, else /usr/man
+ifeq ($(wildcard /usr/share/man), /usr/share/man)
+MANPATH := /usr/share/man
+else
+MANPATH := /usr/man
+endif
+
+# If directories /etc/init.d/rc*.d exist, then we install into that (Suse)
+ifeq ($(wildcard /etc/init.d/rc2.d/), /etc/init.d/rc2.d/)
+STARTUPSCRIPTDIR = /etc/init.d
+RUNLEVELSCRIPTSDIR = /etc/init.d
+else
+# else if directory /etc/rc.d/init.d/ exists, then we install into that (old Linux)
+ifeq ($(wildcard /etc/rc.d/init.d/), /etc/rc.d/init.d/)
+STARTUPSCRIPTDIR = /etc/rc.d/init.d
+RUNLEVELSCRIPTSDIR = /etc/rc.d
+else
+# else if directory /etc/init.d/ exists, then we install into that (new Linux)
+ifeq ($(wildcard /etc/init.d/), /etc/init.d/)
+STARTUPSCRIPTDIR = /etc/init.d
+RUNLEVELSCRIPTSDIR = /etc
+else
+# else install into /etc/rc.d/ (*BSD)
+STARTUPSCRIPTDIR = $(INSTBASE)/etc/rc.d
+endif
+endif
+endif
+
+CFLAGS = $(CFLAGS_COMMON) $(CFLAGS_OS) $(CFLAGS_DEBUG)
+
+#############################################################################
+
+all: setup Daemon libdns_sd Clients SAClient SAResponder SAProxyResponder Identify NetMonitor dnsextd $(OPTIONALTARG)
+
+install: setup InstalledDaemon InstalledStartup InstalledLib InstalledManPages InstalledClients $(OPTINSTALL)
+
+# 'setup' sets up the build directory structure the way we want
+setup:
+	@if test ! -d $(OBJDIR)   ; then mkdir -p $(OBJDIR)   ; fi
+	@if test ! -d $(BUILDDIR) ; then mkdir -p $(BUILDDIR) ; fi
+
+# clean removes targets and objects
+clean:
+	@if test -d $(OBJDIR)   ; then rm -r $(OBJDIR)   ; fi
+	@if test -d $(BUILDDIR) ; then rm -r $(BUILDDIR) ; fi
+	@$(MAKE) -C ../Clients clean
+
+#############################################################################
+
+# daemon target builds the daemon
+DAEMONOBJS = $(OBJDIR)/PosixDaemon.c.o $(OBJDIR)/mDNSPosix.c.o $(OBJDIR)/mDNSUNP.c.o $(OBJDIR)/mDNS.c.o \
+             $(OBJDIR)/DNSDigest.c.o $(OBJDIR)/uDNS.c.o $(OBJDIR)/DNSCommon.c.o $(OBJDIR)/uds_daemon.c.o \
+             $(OBJDIR)/mDNSDebug.c.o $(OBJDIR)/dnssd_ipc.c.o $(OBJDIR)/GenLinkedList.c.o $(OBJDIR)/PlatformCommon.c.o 
+
+# dnsextd target build dnsextd
+DNSEXTDOBJ = $(OBJDIR)/mDNSPosix.c.o $(OBJDIR)/mDNSUNP.c.o $(OBJDIR)/mDNSDebug.c.o $(OBJDIR)/GenLinkedList.c.o $(OBJDIR)/DNSDigest.c.o \
+             $(OBJDIR)/DNSCommon.c.o $(OBJDIR)/PlatformCommon.c.o $(OBJDIR)/dnsextd_parser.y.o $(OBJDIR)/dnsextd_lexer.l.o
+
+Daemon: setup $(BUILDDIR)/mdnsd
+	@echo "Responder daemon done"
+
+$(BUILDDIR)/mdnsd: $(DAEMONOBJS)
+	$(CC) -o $@ $+ $(LINKOPTS)
+	@$(STRIP) $@
+
+# libdns_sd target builds the client library
+libdns_sd: setup $(BUILDDIR)/libdns_sd.$(LDSUFFIX)
+	@echo "Client library done"
+
+CLIENTLIBOBJS = $(OBJDIR)/dnssd_clientlib.c.so.o $(OBJDIR)/dnssd_clientstub.c.so.o $(OBJDIR)/dnssd_ipc.c.so.o
+
+$(BUILDDIR)/libdns_sd.$(LDSUFFIX): $(CLIENTLIBOBJS)
+	@$(LD) $(LINKOPTS) -o $@ $+
+	@$(STRIP) $@
+
+Clients: setup libdns_sd ../Clients/build/dns-sd
+	@echo "Clients done"
+
+../Clients/build/dns-sd:
+	@$(MAKE) -C ../Clients
+
+# nss_mdns target builds the Name Service Switch module
+nss_mdns: setup $(BUILDDIR)/$(NSSLIBFILE)
+	@echo "Name Service Switch module done"
+
+$(BUILDDIR)/$(NSSLIBFILE): $(CLIENTLIBOBJS) $(OBJDIR)/nss_mdns.c.so.o
+	@$(LD) $(LINKOPTS) -o $@ $+
+	@$(STRIP) $@
+
+#############################################################################
+
+# The Install targets place built stuff in their proper places
+InstalledDaemon: $(INSTBASE)/sbin/mdnsd
+	@echo $+ " installed"
+
+InstalledLib: $(INSTBASE)/lib/libdns_sd.$(LDSUFFIX).$(LIBVERS) $(INSTBASE)/include/dns_sd.h
+	@echo $+ " installed"
+
+InstalledStartup: $(STARTUPSCRIPTDIR)/$(STARTUPSCRIPTNAME)
+	@echo $+ " installed"
+
+InstalledManPages: $(MANPATH)/man8/mdnsd.8
+	@echo $+ " installed"
+
+InstalledClients: $(INSTBASE)/bin/dns-sd
+	@echo $+ " installed"
+
+InstalledNSS: $(NSSINSTPATH)/$(NSSLINKNAME) /etc/nss_mdns.conf $(MANPATH)/man5/nss_mdns.conf.5 $(MANPATH)/man8/libnss_mdns.8
+	@echo $+ " installed"
+
+# Note: If daemon already installed, we make sure it's stopped before overwriting it
+$(INSTBASE)/sbin/mdnsd: $(BUILDDIR)/mdnsd
+	@if test -x $@; then $(STARTUPSCRIPTDIR)/$(STARTUPSCRIPTNAME) stop; fi
+	$(CP) $< $@
+
+$(INSTBASE)/lib/libdns_sd.$(LDSUFFIX).$(LIBVERS): $(BUILDDIR)/libdns_sd.$(LDSUFFIX)
+	$(CP) $< $@
+	$(LN) $@ $(INSTBASE)/lib/libdns_sd.$(LDSUFFIX)
+ifdef LDCONFIG
+    # -m means 'merge into existing database', -R means 'rescan directories'
+	$(LDCONFIG) -mR
+endif
+
+$(INSTBASE)/include/dns_sd.h: $(SHAREDDIR)/dns_sd.h
+	$(CP) $< $@
+
+# We make this target dependent on $(INSTBASE)/sbin/mdnsd because we need to ensure
+# that the daemon is installed *before* we try to execute the command to start it.
+$(STARTUPSCRIPTDIR)/$(STARTUPSCRIPTNAME): mdnsd.sh $(STARTUPSCRIPTDIR) $(INSTBASE)/sbin/mdnsd
+	$(CP) $< $@
+	chmod ugo+x $@
+	$@ start
+ifdef RUNLEVELSCRIPTSDIR
+ifeq ($(wildcard $(RUNLEVELSCRIPTSDIR)/runlevels/default), $(RUNLEVELSCRIPTSDIR)/runlevels/default)
+	$(LN) $@ $(RUNLEVELSCRIPTSDIR)/runlevels/default/mdns
+else
+	$(LN) $@ $(RUNLEVELSCRIPTSDIR)/rc2.d/S52mdns
+	$(LN) $@ $(RUNLEVELSCRIPTSDIR)/rc3.d/S52mdns
+	$(LN) $@ $(RUNLEVELSCRIPTSDIR)/rc4.d/S52mdns
+	$(LN) $@ $(RUNLEVELSCRIPTSDIR)/rc5.d/S52mdns
+	$(LN) $@ $(RUNLEVELSCRIPTSDIR)/rc0.d/K16mdns
+	$(LN) $@ $(RUNLEVELSCRIPTSDIR)/rc6.d/K16mdns
+endif
+endif
+
+$(MANPATH)/man5/%.5: %.5
+	cp $< $@
+	chmod 444 $@
+
+$(MANPATH)/man8/%.8: %.8
+	cp $< $@
+	chmod 444 $@
+
+$(MANPATH)/man8/mdnsd.8: $(SHAREDDIR)/mDNSResponder.8
+	cp $< $@
+	chmod 444 $@
+
+$(INSTBASE)/bin/dns-sd: ../Clients/build/dns-sd
+	$(CP) $< $@
+
+$(NSSINSTPATH)/$(NSSLINKNAME): $(NSSINSTPATH)/$(NSSLIBFILE)
+	$(LN) $< $@
+	ldconfig
+
+$(NSSINSTPATH)/$(NSSLIBFILE): $(BUILDDIR)/$(NSSLIBFILE)
+	$(CP) $< $@
+	chmod 444 $@
+
+/etc/nss_mdns.conf: nss_mdns.conf
+	$(CP) $< $@
+	chmod 444 $@
+	# Check the nsswitch.conf file.
+	# If 'mdns' does not already appear on the "hosts:" line, then add it right before 'dns'
+	cp -f /etc/nsswitch.conf /etc/nsswitch.conf.pre-mdns
+	sed -e '/mdns/!s/^\(hosts:.*\)dns\(.*\)/\1mdns dns\2/' /etc/nsswitch.conf.pre-mdns > /etc/nsswitch.conf
+
+#############################################################################
+
+# The following targets build Java wrappers for the dns-sd.h API.
+# Note that the JavaForXcode targets are used when building the project for OS X using Xcode
+
+JAVAC = $(JDK)/bin/javac
+JAVAH = $(JDK)/bin/javah
+JAVADOC = $(JDK)/bin/javadoc
+JAR = $(JDK)/bin/jar
+JAVACFLAGS = $(CFLAGS) $(JAVACFLAGS_OS) -I$(JDK)/include
+
+JavaForXcode_: setup $(BUILDDIR)/dns_sd.jar $(PROJECT_DERIVED_FILE_DIR)/DNSSD.java.h
+	@echo $@ done
+	
+$(PROJECT_DERIVED_FILE_DIR)/DNSSD.java.h: $(OBJDIR)/DNSSD.java.h
+	@if test ! -d $(PROJECT_DERIVED_FILE_DIR) ; then mkdir -p $(PROJECT_DERIVED_FILE_DIR) ; fi
+	$(CP) $< $@
+
+JavaForXcode_clean:
+	@if test -d $(OBJDIR) ; then rm -r $(OBJDIR) ; fi
+	@if test -f $(PROJECT_DERIVED_FILE_DIR)/DNSSD.java.h ; then $(RM) $(PROJECT_DERIVED_FILE_DIR)/DNSSD.java.h ; fi
+	@if test -f $(BUILDDIR)/dns_sd.jar ; then $(RM) $(BUILDDIR)/dns_sd.jar ; fi
+	@echo $@ done
+
+JavaForXcode_installhdrs:
+	@echo $@ NOOP
+
+JavaForXcode_install: JavaForXcode_ $(DSTROOT)/$(SYSTEM_LIBRARY_DIR)/Java/Extensions/dns_sd.jar
+	@echo $@ done
+
+$(DSTROOT)/$(SYSTEM_LIBRARY_DIR)/Java/Extensions/dns_sd.jar: $(BUILDDIR)/dns_sd.jar
+	@if test ! -d $(DSTROOT)/$(SYSTEM_LIBRARY_DIR)/Java/Extensions ; then mkdir -p $(DSTROOT)/$(SYSTEM_LIBRARY_DIR)/Java/Extensions ; fi
+	$(CP) $< $@
+
+Java: setup $(BUILDDIR)/dns_sd.jar $(BUILDDIR)/libjdns_sd.$(LDSUFFIX)
+	@echo "Java wrappers done"
+
+JAVASRC	= $(SHAREDDIR)/Java
+JARCONTENTS =	$(OBJDIR)/com/apple/dnssd/DNSSDService.class \
+				$(OBJDIR)/com/apple/dnssd/DNSSDException.class \
+				$(OBJDIR)/com/apple/dnssd/DNSRecord.class \
+				$(OBJDIR)/com/apple/dnssd/TXTRecord.class \
+				$(OBJDIR)/com/apple/dnssd/DNSSDRegistration.class \
+				$(OBJDIR)/com/apple/dnssd/BaseListener.class \
+				$(OBJDIR)/com/apple/dnssd/BrowseListener.class \
+				$(OBJDIR)/com/apple/dnssd/ResolveListener.class \
+				$(OBJDIR)/com/apple/dnssd/RegisterListener.class \
+				$(OBJDIR)/com/apple/dnssd/QueryListener.class \
+				$(OBJDIR)/com/apple/dnssd/DomainListener.class \
+				$(OBJDIR)/com/apple/dnssd/RegisterRecordListener.class \
+				$(OBJDIR)/com/apple/dnssd/DNSSDRecordRegistrar.class \
+				$(OBJDIR)/com/apple/dnssd/DNSSD.class
+
+$(BUILDDIR)/dns_sd.jar: $(JARCONTENTS) setup
+	$(JAR) -cf $@ -C $(OBJDIR) com
+
+$(BUILDDIR)/libjdns_sd.$(LDSUFFIX): $(JAVASRC)/JNISupport.c $(OBJDIR)/DNSSD.java.h setup libdns_sd
+	$(CC) -o $@ $< $(JAVACFLAGS) -I$(OBJDIR) -L$(BUILDDIR)
+
+$(OBJDIR)/com/apple/dnssd/%.class:	$(JAVASRC)/%.java
+	$(JAVAC) -d $(OBJDIR) -classpath $(OBJDIR) $<
+
+$(OBJDIR)/DNSSD.java.h: $(OBJDIR)/com/apple/dnssd/DNSSD.class
+	$(JAVAH) -force -classpath $(OBJDIR) -o $@ \
+		com.apple.dnssd.AppleDNSSD \
+		com.apple.dnssd.AppleBrowser \
+		com.apple.dnssd.AppleResolver \
+		com.apple.dnssd.AppleRegistration \
+		com.apple.dnssd.AppleQuery \
+		com.apple.dnssd.AppleDomainEnum \
+		com.apple.dnssd.AppleService \
+		com.apple.dnssd.AppleDNSRecord \
+		com.apple.dnssd.AppleRecordRegistrar
+
+#############################################################################
+
+# The following target builds documentation for the Java wrappers.
+
+JavaDoc: Java setup
+	$(JAVADOC) $(JAVASRC)/*.java -classpath $(OBJDIR) -d $(BUILDDIR) -public
+
+#############################################################################
+
+# The following targets build embedded example programs
+SPECIALOBJ = $(OBJDIR)/mDNSPosix.c.o $(OBJDIR)/mDNSUNP.c.o $(OBJDIR)/mDNSDebug.c.o $(OBJDIR)/GenLinkedList.c.o \
+	$(OBJDIR)/DNSDigest.c.o $(OBJDIR)/uDNS.c.o $(OBJDIR)/DNSCommon.c.o $(OBJDIR)/PlatformCommon.c.o
+COMMONOBJ  = $(SPECIALOBJ) $(OBJDIR)/mDNS.c.o
+APPOBJ     = $(COMMONOBJ) $(OBJDIR)/ExampleClientApp.c.o
+
+SAClient: setup $(BUILDDIR)/mDNSClientPosix
+	@echo "Embedded Standalone Client done"
+
+SAResponder: setup $(BUILDDIR)/mDNSResponderPosix
+	@echo "Embedded Standalone Responder done"
+
+SAProxyResponder: setup $(BUILDDIR)/mDNSProxyResponderPosix
+	@echo "Embedded Standalone ProxyResponder done"
+
+Identify: setup $(BUILDDIR)/mDNSIdentify
+	@echo "Identify done"
+
+NetMonitor: setup $(BUILDDIR)/mDNSNetMonitor
+	@echo "NetMonitor done"
+
+dnsextd: setup $(BUILDDIR)/dnsextd
+	@echo "dnsextd done"
+
+$(BUILDDIR)/mDNSClientPosix:         $(APPOBJ)     $(OBJDIR)/Client.c.o
+	$(CC) $+ -o $@ $(LINKOPTS)
+
+$(BUILDDIR)/mDNSResponderPosix:      $(COMMONOBJ)  $(OBJDIR)/Responder.c.o
+	$(CC) $+ -o $@ $(LINKOPTS)
+
+$(BUILDDIR)/mDNSProxyResponderPosix: $(COMMONOBJ)  $(OBJDIR)/ProxyResponder.c.o
+	$(CC) $+ -o $@ $(LINKOPTS)
+
+$(BUILDDIR)/mDNSIdentify:            $(SPECIALOBJ) $(OBJDIR)/Identify.c.o
+	$(CC) $+ -o $@ $(LINKOPTS)
+
+$(OBJDIR)/Identify.c.o:              $(COREDIR)/mDNS.c # Note: Identify.c textually imports mDNS.c
+
+$(BUILDDIR)/mDNSNetMonitor:          $(SPECIALOBJ) $(OBJDIR)/NetMonitor.c.o
+	$(CC) $+ -o $@ $(LINKOPTS)
+
+$(OBJDIR)/NetMonitor.c.o:            $(COREDIR)/mDNS.c # Note: NetMonitor.c textually imports mDNS.c
+
+$(BUILDDIR)/dnsextd:                 $(DNSEXTDOBJ) $(OBJDIR)/dnsextd.c.threadsafe.o
+	$(CC) $+ -o $@ $(LINKOPTS) $(LINKOPTS_PTHREAD)
+
+#############################################################################
+
+# Implicit rules
+$(OBJDIR)/%.c.o:	%.c
+	$(CC) $(CFLAGS) -c -o $@ $<
+
+$(OBJDIR)/%.c.o:	$(COREDIR)/%.c
+	$(CC) $(CFLAGS) -c -o $@ $<
+
+$(OBJDIR)/%.c.o:	$(SHAREDDIR)/%.c
+	$(CC) $(CFLAGS) -c -o $@ $<
+
+$(OBJDIR)/%.c.threadsafe.o:	%.c
+	$(CC) $(CFLAGS) $(CFLAGS_PTHREAD) -D_REENTRANT -c -o $@ $<
+
+$(OBJDIR)/%.c.threadsafe.o:	$(SHAREDDIR)/%.c
+	$(CC) $(CFLAGS) $(CFLAGS_PTHREAD) -D_REENTRANT -c -o $@ $<
+
+$(OBJDIR)/%.c.so.o:	%.c
+	$(CC) $(CFLAGS) -c -fPIC -o $@ $<
+
+$(OBJDIR)/%.c.so.o:	$(SHAREDDIR)/%.c
+	$(CC) $(CFLAGS) -c -fPIC -o $@ $<
+
+$(OBJDIR)/%.y.o: $(SHAREDDIR)/%.y
+	$(BISON)              -o $(OBJDIR)/$*.c -d $<
+	$(CC) $(CFLAGS) -c -o $@ $(OBJDIR)/$*.c
+
+$(OBJDIR)/%.l.o: $(SHAREDDIR)/%.l
+	$(FLEX) $(FLEXFLAGS_OS) -i             -o$(OBJDIR)/$*.l.c $<
+	$(CC) $(CFLAGS) -Wno-error -c -o $@ $(OBJDIR)/$*.l.c
diff --git a/mdnsresponder/mDNSPosix/NetMonitor.c b/mdnsresponder/mDNSPosix/NetMonitor.c
new file mode 100644
index 0000000..a10a4d7
--- /dev/null
+++ b/mdnsresponder/mDNSPosix/NetMonitor.c
@@ -0,0 +1,1010 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ *
+ * Formatting notes:
+ * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion
+ * on C indentation can be found on the web, such as <http://www.kafejo.com/komp/1tbs.htm>,
+ * but for the sake of brevity here I will say just this: Curly braces are not syntactially
+ * part of an "if" statement; they are the beginning and ending markers of a compound statement;
+ * therefore common sense dictates that if they are part of a compound statement then they
+ * should be indented to the same level as everything else in that compound statement.
+ * Indenting curly braces at the same level as the "if" implies that curly braces are
+ * part of the "if", which is false. (This is as misleading as people who write "char* x,y;"
+ * thinking that variables x and y are both of type "char*" -- and anyone who doesn't
+ * understand why variable y is not of type "char*" just proves the point that poor code
+ * layout leads people to unfortunate misunderstandings about how the C language really works.)
+ */
+
+//*************************************************************************************************************
+// Incorporate mDNS.c functionality
+
+// We want to use much of the functionality provided by "mDNS.c",
+// except we'll steal the packets that would be sent to normal mDNSCoreReceive() routine
+#define mDNSCoreReceive __NOT__mDNSCoreReceive__NOT__
+#include "mDNS.c"
+#undef mDNSCoreReceive
+
+//*************************************************************************************************************
+// Headers
+
+#include <stdio.h>			// For printf()
+#include <stdlib.h>			// For malloc()
+#include <string.h>			// For strrchr(), strcmp()
+#include <time.h>			// For "struct tm" etc.
+#include <signal.h>			// For SIGINT, SIGTERM
+#if defined(WIN32)
+// Both mDNS.c and mDNSWin32.h declare UDPSocket_struct type resulting in a compile-time error, so 
+// trick the compiler when including mDNSWin32.h
+#	define UDPSocket_struct _UDPSocket_struct
+#	include <mDNSEmbeddedAPI.h>
+#	include <mDNSWin32.h>
+#	include <PosixCompat.h>
+#	define IFNAMSIZ 256
+static HANDLE gStopEvent = INVALID_HANDLE_VALUE;
+static BOOL WINAPI ConsoleControlHandler( DWORD inControlEvent ) { SetEvent( gStopEvent ); return TRUE; }
+void setlinebuf( FILE * fp ) {}
+#else
+#	include <netdb.h>			// For gethostbyname()
+#	include <sys/socket.h>		// For AF_INET, AF_INET6, etc.
+#	include <net/if.h>			// For IF_NAMESIZE
+#	include <netinet/in.h>		// For INADDR_NONE
+#	include <arpa/inet.h>		// For inet_addr()
+#	include "mDNSPosix.h"      // Defines the specific types needed to run mDNS on this platform
+#endif
+#include "ExampleClientApp.h"
+
+//*************************************************************************************************************
+// Types and structures
+
+enum
+	{
+	// Primitive operations
+	OP_probe        = 0,
+	OP_goodbye      = 1,
+
+	// These are meta-categories;
+	// Query and Answer operations are actually subdivided into two classes:
+	// Browse  query/answer and
+	// Resolve query/answer
+	OP_query        = 2,
+	OP_answer       = 3,
+
+	// The "Browse" variants of query/answer
+	OP_browsegroup  = 2,
+	OP_browseq      = 2,
+	OP_browsea      = 3,
+
+	// The "Resolve" variants of query/answer
+	OP_resolvegroup = 4,
+	OP_resolveq     = 4,
+	OP_resolvea     = 5,
+
+	OP_NumTypes = 6
+	};
+
+typedef struct ActivityStat_struct ActivityStat;
+struct ActivityStat_struct
+	{
+	ActivityStat *next;
+	domainname srvtype;
+	int printed;
+	int totalops;
+	int stat[OP_NumTypes];
+	};
+
+typedef struct FilterList_struct FilterList;
+struct FilterList_struct
+	{
+	FilterList *next;
+	mDNSAddr FilterAddr;
+	};
+
+//*************************************************************************************************************
+// Constants
+
+#define kReportTopServices 15
+#define kReportTopHosts    15
+
+//*************************************************************************************************************
+// Globals
+
+mDNS mDNSStorage;						// mDNS core uses this to store its globals
+static mDNS_PlatformSupport PlatformStorage;	// Stores this platform's globals
+mDNSexport const char ProgramName[] = "mDNSNetMonitor";
+
+struct timeval tv_start, tv_end, tv_interval;
+static int FilterInterface = 0;
+static FilterList *Filters;
+#define ExactlyOneFilter (Filters && !Filters->next)
+
+static int NumPktQ, NumPktL, NumPktR, NumPktB;	// Query/Legacy/Response/Bad
+static int NumProbes, NumGoodbyes, NumQuestions, NumLegacy, NumAnswers, NumAdditionals;
+
+static ActivityStat *stats;
+
+#define OPBanner "Total Ops   Probe   Goodbye  BrowseQ  BrowseA ResolveQ ResolveA"
+
+//*************************************************************************************************************
+// Utilities
+
+// Special version of printf that knows how to print IP addresses, DNS-format name strings, etc.
+mDNSlocal mDNSu32 mprintf(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2);
+mDNSlocal mDNSu32 mprintf(const char *format, ...)
+	{
+	mDNSu32 length;
+	unsigned char buffer[512];
+	va_list ptr;
+	va_start(ptr,format);
+	length = mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr);
+	va_end(ptr);
+	printf("%s", buffer);
+	return(length);
+	}
+
+//*************************************************************************************************************
+// Host Address List
+//
+// Would benefit from a hash
+
+typedef enum
+	{
+	HostPkt_Q        = 0,		// Query
+	HostPkt_L        = 1,		// Legacy Query
+	HostPkt_R        = 2,		// Response
+	HostPkt_B        = 3,		// Bad
+	HostPkt_NumTypes = 4
+	} HostPkt_Type;
+
+typedef struct
+	{
+	mDNSAddr addr;
+	unsigned long pkts[HostPkt_NumTypes];
+	unsigned long totalops;
+	unsigned long stat[OP_NumTypes];
+	domainname hostname;
+	domainname revname;
+	UTF8str255 HIHardware;
+	UTF8str255 HISoftware;
+	mDNSu32    NumQueries;
+	mDNSs32    LastQuery;
+	} HostEntry;
+
+#define HostEntryTotalPackets(H) ((H)->pkts[HostPkt_Q] + (H)->pkts[HostPkt_L] + (H)->pkts[HostPkt_R] + (H)->pkts[HostPkt_B])
+
+typedef struct
+	{
+	long		num;
+	long		max;
+	HostEntry	*hosts;
+	} HostList;
+
+static HostList IPv4HostList = { 0, 0, 0 };
+static HostList IPv6HostList = { 0, 0, 0 };
+
+mDNSlocal HostEntry *FindHost(const mDNSAddr *addr, HostList *list)
+	{
+	long	i;
+	
+	for (i = 0; i < list->num; i++)
+		{
+		HostEntry *entry = list->hosts + i;
+		if (mDNSSameAddress(addr, &entry->addr))
+			return entry;
+		}
+
+	return NULL;
+	}
+	
+mDNSlocal HostEntry *AddHost(const mDNSAddr *addr, HostList *list)
+	{
+	int i;
+	HostEntry *entry;
+	if (list->num >= list->max)
+		{
+		long newMax = list->max + 64;
+		HostEntry *newHosts = realloc(list->hosts, newMax * sizeof(HostEntry));
+		if (newHosts == NULL)
+			return NULL;
+		list->max = newMax;
+		list->hosts = newHosts;
+		}
+
+	entry = list->hosts + list->num++;
+
+	entry->addr = *addr;
+	for (i=0; i<HostPkt_NumTypes; i++) entry->pkts[i] = 0;
+	entry->totalops = 0;
+	for (i=0; i<OP_NumTypes;      i++) entry->stat[i] = 0;
+	entry->hostname.c[0] = 0;
+	entry->revname.c[0] = 0;
+	entry->HIHardware.c[0] = 0;
+	entry->HISoftware.c[0] = 0;
+	entry->NumQueries = 0;
+
+	if (entry->addr.type == mDNSAddrType_IPv4)
+		{
+		mDNSv4Addr ip = entry->addr.ip.v4;
+		char buffer[32];
+		// Note: This is reverse order compared to a normal dotted-decimal IP address, so we can't use our customary "%.4a" format code
+		mDNS_snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d.in-addr.arpa.", ip.b[3], ip.b[2], ip.b[1], ip.b[0]);
+		MakeDomainNameFromDNSNameString(&entry->revname, buffer);
+		}
+
+	return(entry);
+	}
+
+mDNSlocal HostEntry *GotPacketFromHost(const mDNSAddr *addr, HostPkt_Type t, mDNSOpaque16 id)
+	{
+	if (ExactlyOneFilter) return(NULL);
+	else
+		{
+		HostList *list = (addr->type == mDNSAddrType_IPv4) ? &IPv4HostList : &IPv6HostList;
+		HostEntry *entry = FindHost(addr, list);
+		if (!entry) entry = AddHost(addr, list);
+		if (!entry) return(NULL);
+		// Don't count our own interrogation packets
+		if (id.NotAnInteger != 0xFFFF) entry->pkts[t]++;
+		return(entry);
+		}
+	}
+
+mDNSlocal void RecordHostInfo(HostEntry *entry, const ResourceRecord *const pktrr)
+	{
+	if (!entry->hostname.c[0])
+		{
+		if (pktrr->rrtype == kDNSType_A || pktrr->rrtype == kDNSType_AAAA)
+			{
+			// Should really check that the rdata in the address record matches the source address of this packet
+			entry->NumQueries = 0;
+			AssignDomainName(&entry->hostname, pktrr->name);
+			}
+
+		if (pktrr->rrtype == kDNSType_PTR)
+			if (SameDomainName(&entry->revname, pktrr->name))
+				{
+				entry->NumQueries = 0;
+				AssignDomainName(&entry->hostname, &pktrr->rdata->u.name);
+				}
+		}
+	else if (pktrr->rrtype == kDNSType_HINFO)
+		{
+		RDataBody *rd = &pktrr->rdata->u;
+		mDNSu8 *rdend = (mDNSu8 *)rd + pktrr->rdlength;
+		mDNSu8 *hw = rd->txt.c;
+		mDNSu8 *sw = hw + 1 + (mDNSu32)hw[0];
+		if (sw + 1 + sw[0] <= rdend)
+			{
+			AssignDomainName(&entry->hostname, pktrr->name);
+			mDNSPlatformMemCopy(entry->HIHardware.c, hw, 1 + (mDNSu32)hw[0]);
+			mDNSPlatformMemCopy(entry->HISoftware.c, sw, 1 + (mDNSu32)sw[0]);
+			}
+		}
+	}
+
+mDNSlocal void SendUnicastQuery(mDNS *const m, HostEntry *entry, domainname *name, mDNSu16 rrtype, mDNSInterfaceID InterfaceID)
+	{
+	const mDNSOpaque16 id = { { 0xFF, 0xFF } };
+	DNSMessage query;
+	mDNSu8       *qptr        = query.data;
+	const mDNSu8 *const limit = query.data + sizeof(query.data);
+	const mDNSAddr *target    = &entry->addr;
+	InitializeDNSMessage(&query.h, id, QueryFlags);
+	qptr = putQuestion(&query, qptr, limit, name, rrtype, kDNSClass_IN);
+	entry->LastQuery = m->timenow;
+	entry->NumQueries++;
+
+	// Note: When there are multiple mDNSResponder agents running on a single machine
+	// (e.g. Apple mDNSResponder plus a SliMP3 server with embedded mDNSResponder)
+	// it is possible that unicast queries may not go to the primary system responder.
+	// We try the first query using unicast, but if that doesn't work we try again via multicast.
+	if (entry->NumQueries > 2)
+		{
+		target = &AllDNSLinkGroup_v4;
+		}
+	else
+		{
+		//mprintf("%#a Q\n", target);
+		InterfaceID = mDNSInterface_Any;	// Send query from our unicast reply socket
+		}
+
+	mDNSSendDNSMessage(&mDNSStorage, &query, qptr, InterfaceID, mDNSNULL, target, MulticastDNSPort, mDNSNULL, mDNSNULL);
+	}
+
+mDNSlocal void AnalyseHost(mDNS *const m, HostEntry *entry, const mDNSInterfaceID InterfaceID)
+	{
+	// If we've done four queries without answer, give up
+	if (entry->NumQueries >= 4) return;
+
+	// If we've done a query in the last second, give the host a chance to reply before trying again
+	if (entry->NumQueries && m->timenow - entry->LastQuery < mDNSPlatformOneSecond) return;
+
+	// If we don't know the host name, try to find that first
+	if (!entry->hostname.c[0])
+		{
+		if (entry->revname.c[0])
+			{
+			SendUnicastQuery(m, entry, &entry->revname, kDNSType_PTR, InterfaceID);
+			//mprintf("%##s PTR %d\n", entry->revname.c, entry->NumQueries);
+			}
+		}
+	// If we have the host name but no HINFO, now ask for that
+	else if (!entry->HIHardware.c[0])
+		{
+		SendUnicastQuery(m, entry, &entry->hostname, kDNSType_HINFO, InterfaceID);
+		//mprintf("%##s HINFO %d\n", entry->hostname.c, entry->NumQueries);
+		}
+	}
+
+mDNSlocal int CompareHosts(const void *p1, const void *p2)
+	{
+	return (int)(HostEntryTotalPackets((HostEntry *)p2) - HostEntryTotalPackets((HostEntry *)p1));
+	}
+
+mDNSlocal void ShowSortedHostList(HostList *list, int max)
+	{
+	HostEntry *e, *end = &list->hosts[(max < list->num) ? max : list->num];
+	qsort(list->hosts, list->num, sizeof(HostEntry), CompareHosts);
+	if (list->num) mprintf("\n%-25s%s%s\n", "Source Address", OPBanner, "    Pkts    Query   LegacyQ Response");
+	for (e = &list->hosts[0]; e < end; e++)
+		{
+		int len = mprintf("%#-25a", &e->addr);
+		if (len > 25) mprintf("\n%25s", "");
+		mprintf("%8lu %8lu %8lu %8lu %8lu %8lu %8lu", e->totalops,
+			e->stat[OP_probe], e->stat[OP_goodbye],
+			e->stat[OP_browseq], e->stat[OP_browsea],
+			e->stat[OP_resolveq], e->stat[OP_resolvea]);
+		mprintf(" %8lu %8lu %8lu %8lu",
+			HostEntryTotalPackets(e), e->pkts[HostPkt_Q], e->pkts[HostPkt_L], e->pkts[HostPkt_R]);
+		if (e->pkts[HostPkt_B]) mprintf("Bad: %8lu", e->pkts[HostPkt_B]);
+		mprintf("\n");
+		if (!e->HISoftware.c[0] && e->NumQueries > 2)
+			mDNSPlatformMemCopy(&e->HISoftware, "\x27*** Unknown (Jaguar, Windows, etc.) ***", 0x28);
+		if (e->hostname.c[0] || e->HIHardware.c[0] || e->HISoftware.c[0])
+			mprintf("%##-45s %#-14s %#s\n", e->hostname.c, e->HIHardware.c, e->HISoftware.c);
+		}
+	}
+
+//*************************************************************************************************************
+// Receive and process packets
+
+mDNSexport mDNSBool ExtractServiceType(const domainname *const fqdn, domainname *const srvtype)
+	{
+	int i, len;
+	const mDNSu8 *src = fqdn->c;
+	mDNSu8 *dst = srvtype->c;
+	
+	len = *src;
+	if (len == 0 || len >= 0x40) return(mDNSfalse);
+	if (src[1] != '_') src += 1 + len;
+	
+	len = *src;
+	if (len == 0 || len >= 0x40 || src[1] != '_') return(mDNSfalse);
+	for (i=0; i<=len; i++) *dst++ = *src++;
+
+	len = *src;
+	if (len == 0 || len >= 0x40 || src[1] != '_') return(mDNSfalse);
+	for (i=0; i<=len; i++) *dst++ = *src++;
+
+	*dst++ = 0;		// Put the null root label on the end of the service type
+
+	return(mDNStrue);
+	}
+
+mDNSlocal void recordstat(HostEntry *entry, const domainname *fqdn, int op, mDNSu16 rrtype)
+	{
+	ActivityStat **s = &stats;
+	domainname srvtype;
+
+	if (op != OP_probe)
+		{
+		if (rrtype == kDNSType_SRV || rrtype == kDNSType_TXT) op = op - OP_browsegroup + OP_resolvegroup;
+		else if (rrtype != kDNSType_PTR) return;
+		}
+	
+	if (!ExtractServiceType(fqdn, &srvtype)) return;
+
+	while (*s && !SameDomainName(&(*s)->srvtype, &srvtype)) s = &(*s)->next;
+	if (!*s)
+		{
+		int i;
+		*s = malloc(sizeof(ActivityStat));
+		if (!*s) exit(-1);
+		(*s)->next     = NULL;
+		(*s)->srvtype  = srvtype;
+		(*s)->printed  = 0;
+		(*s)->totalops = 0;
+		for (i=0; i<OP_NumTypes; i++) (*s)->stat[i] = 0;
+		}
+
+	(*s)->totalops++;
+	(*s)->stat[op]++;
+	if (entry)
+		{
+		entry->totalops++;
+		entry->stat[op]++;
+		}
+	}
+
+mDNSlocal void printstats(int max)
+	{
+	int i;
+	if (!stats) return;
+	for (i=0; i<max; i++)
+		{
+		int max = 0;
+		ActivityStat *s, *m = NULL;
+		for (s = stats; s; s=s->next)
+			if (!s->printed && max < s->totalops)
+				{ m = s; max = s->totalops; }
+		if (!m) return;
+		m->printed = mDNStrue;
+		if (i==0) mprintf("%-25s%s\n", "Service Type", OPBanner);
+		mprintf("%##-25s%8d %8d %8d %8d %8d %8d %8d\n", m->srvtype.c, m->totalops, m->stat[OP_probe],
+			m->stat[OP_goodbye], m->stat[OP_browseq], m->stat[OP_browsea], m->stat[OP_resolveq], m->stat[OP_resolvea]);
+		}
+	}
+
+mDNSlocal const mDNSu8 *FindUpdate(mDNS *const m, const DNSMessage *const query, const mDNSu8 *ptr, const mDNSu8 *const end,
+	DNSQuestion *q, LargeCacheRecord *pkt)
+	{
+	int i;
+	for (i = 0; i < query->h.numAuthorities; i++)
+		{
+		const mDNSu8 *p2 = ptr;
+		ptr = GetLargeResourceRecord(m, query, ptr, end, q->InterfaceID, kDNSRecordTypePacketAuth, pkt);
+		if (!ptr) break;
+		if (m->rec.r.resrec.RecordType != kDNSRecordTypePacketNegative && ResourceRecordAnswersQuestion(&pkt->r.resrec, q)) return(p2);
+		}
+	return(mDNSNULL);
+	}
+
+mDNSlocal void DisplayPacketHeader(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end, const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSInterfaceID InterfaceID)
+	{
+	const char *const ptype =   (msg->h.flags.b[0] & kDNSFlag0_QR_Response)             ? "-R- " :
+								(srcport.NotAnInteger == MulticastDNSPort.NotAnInteger) ? "-Q- " : "-LQ-";
+
+	struct timeval tv;
+	struct tm tm;
+	const mDNSu32 index = mDNSPlatformInterfaceIndexfromInterfaceID(m, InterfaceID, mDNSfalse);
+	char if_name[IFNAMSIZ];		// Older Linux distributions don't define IF_NAMESIZE
+	if_indextoname(index, if_name);
+	gettimeofday(&tv, NULL);
+	localtime_r((time_t*)&tv.tv_sec, &tm);
+	mprintf("\n%d:%02d:%02d.%06d Interface %d/%s\n", tm.tm_hour, tm.tm_min, tm.tm_sec, tv.tv_usec, index, if_name);
+
+	mprintf("%#-16a %s             Q:%3d  Ans:%3d  Auth:%3d  Add:%3d  Size:%5d bytes",
+		srcaddr, ptype, msg->h.numQuestions, msg->h.numAnswers, msg->h.numAuthorities, msg->h.numAdditionals, end - (mDNSu8 *)msg);
+
+	if (msg->h.id.NotAnInteger) mprintf("  ID:%u", mDNSVal16(msg->h.id));
+
+	if (!mDNSAddrIsDNSMulticast(dstaddr)) mprintf("   To: %#a", dstaddr);
+
+	if (msg->h.flags.b[0] & kDNSFlag0_TC)
+		{
+		if (msg->h.flags.b[0] & kDNSFlag0_QR_Response) mprintf("   Truncated");
+		else                                           mprintf("   Truncated (KA list continues in next packet)");
+		}
+	mprintf("\n");
+	}
+
+mDNSlocal void DisplayResourceRecord(const mDNSAddr *const srcaddr, const char *const op, const ResourceRecord *const pktrr)
+	{
+	static const char hexchars[16] = "0123456789ABCDEF";
+	#define MaxWidth 132
+	char buffer[MaxWidth+8];
+	char *p = buffer;
+
+	RDataBody *rd = &pktrr->rdata->u;
+	mDNSu8 *rdend = (mDNSu8 *)rd + pktrr->rdlength;
+	int n = mprintf("%#-16a %-5s %-5s%5lu %##s -> ", srcaddr, op, DNSTypeName(pktrr->rrtype), pktrr->rroriginalttl, pktrr->name->c);
+
+	if (pktrr->RecordType == kDNSRecordTypePacketNegative) { mprintf("**** ERROR: FAILED TO READ RDATA ****\n"); return; }
+
+	// The kDNSType_OPT case below just calls GetRRDisplayString_rdb
+	// Perhaps more (or all?) of the cases should do that?
+	switch(pktrr->rrtype)
+		{
+		case kDNSType_A:	n += mprintf("%.4a", &rd->ipv4); break;
+		case kDNSType_PTR:	n += mprintf("%##.*s", MaxWidth - n, rd->name.c); break;
+		case kDNSType_HINFO:// same as kDNSType_TXT below
+		case kDNSType_TXT:	{
+							mDNSu8 *t = rd->txt.c;
+							while (t < rdend && t[0] && p < buffer+MaxWidth)
+								{
+								int i;
+								for (i=1; i<=t[0] && p < buffer+MaxWidth; i++)
+									{
+									if (t[i] == '\\') *p++ = '\\';
+									if (t[i] >= ' ') *p++ = t[i];
+									else
+										{
+										*p++ = '\\';
+										*p++ = '0';
+										*p++ = 'x';
+										*p++ = hexchars[t[i] >> 4];
+										*p++ = hexchars[t[i] & 0xF];
+										}
+									}
+								t += 1+t[0];
+								if (t < rdend && t[0]) { *p++ = '\\'; *p++ = ' '; }
+								}
+							*p++ = 0;
+							n += mprintf("%.*s", MaxWidth - n, buffer);
+							} break;
+		case kDNSType_AAAA:	n += mprintf("%.16a", &rd->ipv6); break;
+		case kDNSType_SRV:	n += mprintf("%##s:%d", rd->srv.target.c, mDNSVal16(rd->srv.port)); break;
+		case kDNSType_OPT:	{
+							char b[MaxMsg];
+							// Quick hack: we don't want the prefix that GetRRDisplayString_rdb puts at the start of its
+							// string, because it duplicates the name and rrtype we already display, so we compute the
+							// length of that prefix and strip that many bytes off the beginning of the string we display.
+							mDNSu32 striplen = mDNS_snprintf(b, MaxMsg-1, "%4d %##s %s ", pktrr->rdlength, pktrr->name->c, DNSTypeName(pktrr->rrtype));
+							GetRRDisplayString_rdb(pktrr, &pktrr->rdata->u, b);
+							n += mprintf("%.*s", MaxWidth - n, b + striplen);
+							} break;
+		case kDNSType_NSEC:	{
+							int i;
+							for (i=0; i<255; i++)
+								if (rd->nsec.bitmap[i>>3] & (128 >> (i&7)))
+									n += mprintf("%s ", DNSTypeName(i));
+							} break;
+		default:			{
+							mDNSu8 *s = rd->data;
+							while (s < rdend && p < buffer+MaxWidth)
+								{
+								if (*s == '\\') *p++ = '\\';
+								if (*s >= ' ') *p++ = *s;
+								else
+									{
+									*p++ = '\\';
+									*p++ = '0';
+									*p++ = 'x';
+									*p++ = hexchars[*s >> 4];
+									*p++ = hexchars[*s & 0xF];
+									}
+								s++;
+								}
+							*p++ = 0;
+							n += mprintf("%.*s", MaxWidth - n, buffer);
+							} break;
+		}
+	
+	mprintf("\n");
+	}
+
+mDNSlocal void HexDump(const mDNSu8 *ptr, const mDNSu8 *const end)
+	{
+	while (ptr < end)
+		{
+		int i;
+		for (i=0; i<16; i++)
+			if (&ptr[i] < end) mprintf("%02X ", ptr[i]);
+			else mprintf("   ");
+		for (i=0; i<16; i++)
+			if (&ptr[i] < end) mprintf("%c", ptr[i] <= ' ' || ptr[i] >= 126 ? '.' : ptr[i]);
+		ptr += 16;
+		mprintf("\n");
+		}
+	}
+
+mDNSlocal void DisplayError(const mDNSAddr *srcaddr, const mDNSu8 *ptr, const mDNSu8 *const end, char *msg)
+	{
+	mprintf("%#-16a **** ERROR: FAILED TO READ %s **** \n", srcaddr, msg);
+	HexDump(ptr, end);
+	}
+
+mDNSlocal void DisplayQuery(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *const end,
+	const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSInterfaceID InterfaceID)
+	{
+	int i;
+	const mDNSu8 *ptr = msg->data;
+	const mDNSu8 *auth = LocateAuthorities(msg, end);
+	mDNSBool MQ = (srcport.NotAnInteger == MulticastDNSPort.NotAnInteger);
+	HostEntry *entry = GotPacketFromHost(srcaddr, MQ ? HostPkt_Q : HostPkt_L, msg->h.id);
+	LargeCacheRecord pkt;
+
+	DisplayPacketHeader(m, msg, end, srcaddr, srcport, dstaddr, InterfaceID);
+	if (msg->h.id.NotAnInteger != 0xFFFF)
+		{
+		if (MQ) NumPktQ++; else NumPktL++;
+		}
+
+	for (i=0; i<msg->h.numQuestions; i++)
+		{
+		DNSQuestion q;
+		mDNSu8 *p2 = (mDNSu8 *)getQuestion(msg, ptr, end, InterfaceID, &q);
+		mDNSu16 ucbit = q.qclass & kDNSQClass_UnicastResponse;
+		q.qclass &= ~kDNSQClass_UnicastResponse;
+		if (!p2) { DisplayError(srcaddr, ptr, end, "QUESTION"); return; }
+		ptr = p2;
+		p2 = (mDNSu8 *)FindUpdate(m, msg, auth, end, &q, &pkt);
+		if (p2)
+			{
+			NumProbes++;
+			DisplayResourceRecord(srcaddr, ucbit ? "(PU)" : "(PM)", &pkt.r.resrec);
+			recordstat(entry, &q.qname, OP_probe, q.qtype);
+			p2 = (mDNSu8 *)skipDomainName(msg, p2, end);
+			// Having displayed this update record, clear type and class so we don't display the same one again.
+			p2[0] = p2[1] = p2[2] = p2[3] = 0;
+			}
+		else
+			{
+			const char *ptype = ucbit ? "(QU)" : "(QM)";
+			if (srcport.NotAnInteger == MulticastDNSPort.NotAnInteger) NumQuestions++;
+			else { NumLegacy++; ptype = "(LQ)"; }
+			mprintf("%#-16a %-5s %-5s      %##s\n", srcaddr, ptype, DNSTypeName(q.qtype), q.qname.c);
+			if (msg->h.id.NotAnInteger != 0xFFFF) recordstat(entry, &q.qname, OP_query, q.qtype);
+			}
+		}
+
+	for (i=0; i<msg->h.numAnswers; i++)
+		{
+		const mDNSu8 *ep = ptr;
+		ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAns, &pkt);
+		if (!ptr) { DisplayError(srcaddr, ep, end, "KNOWN ANSWER"); return; }
+		DisplayResourceRecord(srcaddr, "(KA)", &pkt.r.resrec);
+		
+		// In the case of queries with long multi-packet KA lists, we count each subsequent KA packet
+		// the same as a single query, to more accurately reflect the burden on the network
+		// (A query with a six-packet KA list is *at least* six times the burden on the network as a single-packet query.)
+		if (msg->h.numQuestions == 0 && i == 0)
+			recordstat(entry, pkt.r.resrec.name, OP_query, pkt.r.resrec.rrtype);
+		}
+
+	for (i=0; i<msg->h.numAuthorities; i++)
+		{
+		const mDNSu8 *ep = ptr;
+		ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAuth, &pkt);
+		if (!ptr) { DisplayError(srcaddr, ep, end, "AUTHORITY"); return; }
+		// After we display an Update record with its matching question (above) we zero out its type and class
+		// If any remain that haven't been zero'd out, display them here
+		if (pkt.r.resrec.rrtype || pkt.r.resrec.rrclass) DisplayResourceRecord(srcaddr, "(AU)", &pkt.r.resrec);
+		}
+
+	for (i=0; i<msg->h.numAdditionals; i++)
+		{
+		const mDNSu8 *ep = ptr;
+		ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAdd, &pkt);
+		if (!ptr) { DisplayError(srcaddr, ep, end, "ADDITIONAL"); return; }
+		DisplayResourceRecord(srcaddr, pkt.r.resrec.rrtype == kDNSType_OPT ? "(OP)" : "(AD)", &pkt.r.resrec);
+		}
+
+	if (entry) AnalyseHost(m, entry, InterfaceID);
+	}
+
+mDNSlocal void DisplayResponse(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *end,
+	const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSAddr *dstaddr, const mDNSInterfaceID InterfaceID)
+	{
+	int i;
+	const mDNSu8 *ptr = msg->data;
+	HostEntry *entry = GotPacketFromHost(srcaddr, HostPkt_R, msg->h.id);
+	LargeCacheRecord pkt;
+
+	DisplayPacketHeader(m, msg, end, srcaddr, srcport, dstaddr, InterfaceID);
+	if (msg->h.id.NotAnInteger != 0xFFFF) NumPktR++;
+
+	for (i=0; i<msg->h.numQuestions; i++)
+		{
+		DNSQuestion q;
+		const mDNSu8 *ep = ptr;
+		ptr = getQuestion(msg, ptr, end, InterfaceID, &q);
+		if (!ptr) { DisplayError(srcaddr, ep, end, "QUESTION"); return; }
+		if (mDNSAddrIsDNSMulticast(dstaddr))
+			mprintf("%#-16a (?)   **** ERROR: SHOULD NOT HAVE Q IN mDNS RESPONSE **** %-5s %##s\n", srcaddr, DNSTypeName(q.qtype), q.qname.c);
+		else
+			mprintf("%#-16a (Q)   %-5s      %##s\n", srcaddr, DNSTypeName(q.qtype), q.qname.c);
+		}
+
+	for (i=0; i<msg->h.numAnswers; i++)
+		{
+		const mDNSu8 *ep = ptr;
+		ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAns, &pkt);
+		if (!ptr) { DisplayError(srcaddr, ep, end, "ANSWER"); return; }
+		if (pkt.r.resrec.rroriginalttl)
+			{
+			NumAnswers++;
+			DisplayResourceRecord(srcaddr, (pkt.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) ? "(AN)" : "(AN+)", &pkt.r.resrec);
+			if (msg->h.id.NotAnInteger != 0xFFFF) recordstat(entry, pkt.r.resrec.name, OP_answer, pkt.r.resrec.rrtype);
+			if (entry) RecordHostInfo(entry, &pkt.r.resrec);
+			}
+		else
+			{
+			NumGoodbyes++;
+			DisplayResourceRecord(srcaddr, "(DE)", &pkt.r.resrec);
+			recordstat(entry, pkt.r.resrec.name, OP_goodbye, pkt.r.resrec.rrtype);
+			}
+		}
+
+	for (i=0; i<msg->h.numAuthorities; i++)
+		{
+		const mDNSu8 *ep = ptr;
+		ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAuth, &pkt);
+		if (!ptr) { DisplayError(srcaddr, ep, end, "AUTHORITY"); return; }
+		mprintf("%#-16a (?)  **** ERROR: SHOULD NOT HAVE AUTHORITY IN mDNS RESPONSE **** %-5s %##s\n",
+			srcaddr, DNSTypeName(pkt.r.resrec.rrtype), pkt.r.resrec.name->c);
+		}
+
+	for (i=0; i<msg->h.numAdditionals; i++)
+		{
+		const mDNSu8 *ep = ptr;
+		ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAdd, &pkt);
+		if (!ptr) { DisplayError(srcaddr, ep, end, "ADDITIONAL"); return; }
+		NumAdditionals++;
+		DisplayResourceRecord(srcaddr,
+			pkt.r.resrec.rrtype == kDNSType_OPT ? "(OP)" : (pkt.r.resrec.RecordType & kDNSRecordTypePacketUniqueMask) ? "(AD)" : "(AD+)",
+			&pkt.r.resrec);
+		if (entry) RecordHostInfo(entry, &pkt.r.resrec);
+		}
+	
+	if (entry) AnalyseHost(m, entry, InterfaceID);
+	}
+
+mDNSlocal void ProcessUnicastResponse(mDNS *const m, const DNSMessage *const msg, const mDNSu8 *end, const mDNSAddr *srcaddr, const mDNSInterfaceID InterfaceID)
+	{
+	int i;
+	const mDNSu8 *ptr = LocateAnswers(msg, end);
+	HostEntry *entry = GotPacketFromHost(srcaddr, HostPkt_R, msg->h.id);
+	//mprintf("%#a R\n", srcaddr);
+
+	for (i=0; i<msg->h.numAnswers + msg->h.numAuthorities + msg->h.numAdditionals; i++)
+		{
+		LargeCacheRecord pkt;
+		ptr = GetLargeResourceRecord(m, msg, ptr, end, InterfaceID, kDNSRecordTypePacketAns, &pkt);
+		if (ptr && pkt.r.resrec.rroriginalttl && entry) RecordHostInfo(entry, &pkt.r.resrec);
+		}
+	}
+
+mDNSlocal mDNSBool AddressMatchesFilterList(const mDNSAddr *srcaddr)
+	{
+	FilterList *f;
+	if (!Filters) return(srcaddr->type == mDNSAddrType_IPv4);
+	for (f=Filters; f; f=f->next) if (mDNSSameAddress(srcaddr, &f->FilterAddr)) return(mDNStrue);
+	return(mDNSfalse);
+	}
+
+mDNSexport void mDNSCoreReceive(mDNS *const m, DNSMessage *const msg, const mDNSu8 *const end,
+	const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSAddr *dstaddr, mDNSIPPort dstport, const mDNSInterfaceID InterfaceID)
+	{
+	const mDNSu8 StdQ = kDNSFlag0_QR_Query    | kDNSFlag0_OP_StdQuery;
+	const mDNSu8 StdR = kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery;
+	const mDNSu8 QR_OP = (mDNSu8)(msg->h.flags.b[0] & kDNSFlag0_QROP_Mask);
+	mDNSu8 *ptr = (mDNSu8 *)&msg->h.numQuestions;
+	int goodinterface = (FilterInterface == 0);
+
+	(void)dstaddr;	// Unused
+	(void)dstport;	// Unused
+	
+	// Read the integer parts which are in IETF byte-order (MSB first, LSB second)
+	msg->h.numQuestions   = (mDNSu16)((mDNSu16)ptr[0] <<  8 | ptr[1]);
+	msg->h.numAnswers     = (mDNSu16)((mDNSu16)ptr[2] <<  8 | ptr[3]);
+	msg->h.numAuthorities = (mDNSu16)((mDNSu16)ptr[4] <<  8 | ptr[5]);
+	msg->h.numAdditionals = (mDNSu16)((mDNSu16)ptr[6] <<  8 | ptr[7]);
+
+	// For now we're only interested in monitoring IPv4 traffic.
+	// All IPv6 packets should just be duplicates of the v4 packets.
+	if (!goodinterface) goodinterface = (FilterInterface == (int)mDNSPlatformInterfaceIndexfromInterfaceID(m, InterfaceID, mDNSfalse));
+	if (goodinterface && AddressMatchesFilterList(srcaddr))
+		{
+		mDNS_Lock(m);
+		if (!mDNSAddrIsDNSMulticast(dstaddr))
+			{
+			if      (QR_OP == StdQ) mprintf("Unicast query from %#a\n", srcaddr);
+			else if (QR_OP == StdR) ProcessUnicastResponse(m, msg, end, srcaddr,                   InterfaceID);
+			}
+		else
+			{
+			if      (QR_OP == StdQ) DisplayQuery          (m, msg, end, srcaddr, srcport, dstaddr, InterfaceID);
+			else if (QR_OP == StdR) DisplayResponse       (m, msg, end, srcaddr, srcport, dstaddr, InterfaceID);
+			else
+				{
+				debugf("Unknown DNS packet type %02X%02X (ignored)", msg->h.flags.b[0], msg->h.flags.b[1]);
+				GotPacketFromHost(srcaddr, HostPkt_B, msg->h.id);
+				NumPktB++;
+				}
+			}
+		mDNS_Unlock(m);
+		}
+	}
+
+mDNSlocal mStatus mDNSNetMonitor(void)
+	{
+	struct tm tm;
+	int h, m, s, mul, div, TotPkt;
+#if !defined(WIN32)
+	sigset_t signals;
+#endif
+	
+	mStatus status = mDNS_Init(&mDNSStorage, &PlatformStorage,
+		mDNS_Init_NoCache, mDNS_Init_ZeroCacheSize,
+		mDNS_Init_DontAdvertiseLocalAddresses,
+		mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext);
+	if (status) return(status);
+
+	gettimeofday(&tv_start, NULL);
+
+#if defined( WIN32 )
+	status = SetupInterfaceList(&mDNSStorage);
+	if (status) return(status);
+	gStopEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+	if (gStopEvent == INVALID_HANDLE_VALUE) return mStatus_UnknownErr;
+	if (!SetConsoleCtrlHandler(ConsoleControlHandler, TRUE)) return mStatus_UnknownErr;
+	while (WaitForSingleObjectEx(gStopEvent, INFINITE, TRUE) == WAIT_IO_COMPLETION)
+		DispatchSocketEvents(&mDNSStorage);
+	if (!SetConsoleCtrlHandler(ConsoleControlHandler, FALSE)) return mStatus_UnknownErr;
+	CloseHandle(gStopEvent);
+#else
+	mDNSPosixListenForSignalInEventLoop(SIGINT);
+	mDNSPosixListenForSignalInEventLoop(SIGTERM);
+
+	do 
+		{
+		struct timeval	timeout = { 0x3FFFFFFF, 0 };	// wait until SIGINT or SIGTERM
+		mDNSBool		gotSomething;
+		mDNSPosixRunEventLoopOnce(&mDNSStorage, &timeout, &signals, &gotSomething);
+		}
+	while ( !( sigismember( &signals, SIGINT) || sigismember( &signals, SIGTERM)));
+#endif
+	
+	// Now display final summary
+	TotPkt = NumPktQ + NumPktL + NumPktR;
+	gettimeofday(&tv_end, NULL);
+	tv_interval = tv_end;
+	if (tv_start.tv_usec > tv_interval.tv_usec)
+		{ tv_interval.tv_usec += 1000000; tv_interval.tv_sec--; }
+	tv_interval.tv_sec  -= tv_start.tv_sec;
+	tv_interval.tv_usec -= tv_start.tv_usec;
+	h = (tv_interval.tv_sec / 3600);
+	m = (tv_interval.tv_sec % 3600) / 60;
+	s = (tv_interval.tv_sec % 60);
+	if (tv_interval.tv_sec > 10)
+		{
+		mul = 60;
+		div = tv_interval.tv_sec;
+		}
+	else
+		{
+		mul = 60000;
+		div = tv_interval.tv_sec * 1000 + tv_interval.tv_usec / 1000;
+		if (div == 0) div=1;
+		}
+
+	mprintf("\n\n");
+	localtime_r((time_t*)&tv_start.tv_sec, &tm);
+	mprintf("Started      %3d:%02d:%02d.%06d\n", tm.tm_hour, tm.tm_min, tm.tm_sec, tv_start.tv_usec);
+	localtime_r((time_t*)&tv_end.tv_sec, &tm);
+	mprintf("End          %3d:%02d:%02d.%06d\n", tm.tm_hour, tm.tm_min, tm.tm_sec, tv_end.tv_usec);
+	mprintf("Captured for %3d:%02d:%02d.%06d\n", h, m, s, tv_interval.tv_usec);
+	if (!Filters)
+		{
+		mprintf("Unique source addresses seen on network:");
+		if (IPv4HostList.num) mprintf(" %ld (IPv4)", IPv4HostList.num);
+		if (IPv6HostList.num) mprintf(" %ld (IPv6)", IPv6HostList.num);
+		if (!IPv4HostList.num && !IPv6HostList.num) mprintf(" None");
+		mprintf("\n");
+		}
+	mprintf("\n");
+	mprintf("Modern Query        Packets:      %7d   (avg%5d/min)\n", NumPktQ,        NumPktQ        * mul / div);
+	mprintf("Legacy Query        Packets:      %7d   (avg%5d/min)\n", NumPktL,        NumPktL        * mul / div);
+	mprintf("Multicast Response  Packets:      %7d   (avg%5d/min)\n", NumPktR,        NumPktR        * mul / div);
+	mprintf("Total     Multicast Packets:      %7d   (avg%5d/min)\n", TotPkt,         TotPkt         * mul / div);
+	mprintf("\n");
+	mprintf("Total New Service Probes:         %7d   (avg%5d/min)\n", NumProbes,      NumProbes      * mul / div);
+	mprintf("Total Goodbye Announcements:      %7d   (avg%5d/min)\n", NumGoodbyes,    NumGoodbyes    * mul / div);
+	mprintf("Total Query Questions:            %7d   (avg%5d/min)\n", NumQuestions,   NumQuestions   * mul / div);
+	mprintf("Total Queries from Legacy Clients:%7d   (avg%5d/min)\n", NumLegacy,      NumLegacy      * mul / div);
+	mprintf("Total Answers/Announcements:      %7d   (avg%5d/min)\n", NumAnswers,     NumAnswers     * mul / div);
+	mprintf("Total Additional Records:         %7d   (avg%5d/min)\n", NumAdditionals, NumAdditionals * mul / div);
+	mprintf("\n");
+	printstats(kReportTopServices);
+
+	if (!ExactlyOneFilter)
+		{
+		ShowSortedHostList(&IPv4HostList, kReportTopHosts);
+		ShowSortedHostList(&IPv6HostList, kReportTopHosts);
+		}
+
+	mDNS_Close(&mDNSStorage);
+	return(0);
+	}
+
+mDNSexport int main(int argc, char **argv)
+	{
+	const char *progname = strrchr(argv[0], '/') ? strrchr(argv[0], '/') + 1 : argv[0];
+	int i;
+	mStatus status;
+
+#if defined(WIN32)
+	HeapSetInformation(NULL, HeapEnableTerminationOnCorruption, NULL, 0);
+#endif
+
+	setlinebuf(stdout);				// Want to see lines as they appear, not block buffered
+
+	for (i=1; i<argc; i++)
+		{
+		if (i+1 < argc && !strcmp(argv[i], "-i") && atoi(argv[i+1]))
+			{
+			FilterInterface = atoi(argv[i+1]);
+			i += 2;
+			printf("Monitoring interface %d\n", FilterInterface);
+			}
+		else
+			{
+			struct in_addr s4;
+			struct in6_addr s6;
+			FilterList *f;
+			mDNSAddr a;
+			a.type = mDNSAddrType_IPv4;
+	
+			if (inet_pton(AF_INET, argv[i], &s4) == 1)
+				a.ip.v4.NotAnInteger = s4.s_addr;
+			else if (inet_pton(AF_INET6, argv[i], &s6) == 1)
+				{
+				a.type = mDNSAddrType_IPv6;
+				mDNSPlatformMemCopy(&a.ip.v6, &s6, sizeof(a.ip.v6));
+				}
+			else
+				{
+				struct hostent *h = gethostbyname(argv[i]);
+				if (h) a.ip.v4.NotAnInteger = *(long*)h->h_addr;
+				else goto usage;
+				}
+			
+			f = malloc(sizeof(*f));
+			f->FilterAddr = a;
+			f->next = Filters;
+			Filters = f;
+			}
+		}
+
+	status = mDNSNetMonitor();
+	if (status) { fprintf(stderr, "%s: mDNSNetMonitor failed %d\n", progname, (int)status); return(status); }
+	return(0);
+
+usage:
+	fprintf(stderr, "\nmDNS traffic monitor\n");
+	fprintf(stderr, "Usage: %s [-i index] [host]\n", progname);
+	fprintf(stderr, "Optional [-i index] parameter displays only packets from that interface index\n");
+	fprintf(stderr, "Optional [host] parameter displays only packets from that host\n");
+
+	fprintf(stderr, "\nPer-packet header output:\n");
+	fprintf(stderr, "-Q-            Multicast Query from mDNS client that accepts multicast responses\n");
+	fprintf(stderr, "-R-            Multicast Response packet containing answers/announcements\n");
+	fprintf(stderr, "-LQ-           Multicast Query from legacy client that does *not* listen for multicast responses\n");
+	fprintf(stderr, "Q/Ans/Auth/Add Number of questions, answers, authority records and additional records in packet\n");
+
+	fprintf(stderr, "\nPer-record display:\n");
+	fprintf(stderr, "(PM)           Probe Question (new service starting), requesting multicast response\n");
+	fprintf(stderr, "(PU)           Probe Question (new service starting), requesting unicast response\n");
+	fprintf(stderr, "(DE)           Deletion/Goodbye (service going away)\n");
+	fprintf(stderr, "(LQ)           Legacy Query Question\n");
+	fprintf(stderr, "(QM)           Query Question, requesting multicast response\n");
+	fprintf(stderr, "(QU)           Query Question, requesting unicast response\n");
+	fprintf(stderr, "(KA)           Known Answer (information querier already knows)\n");
+	fprintf(stderr, "(AN)           Unique Answer to question (or periodic announcment) (entire RR Set)\n");
+	fprintf(stderr, "(AN+)          Answer to question (or periodic announcment) (add to existing RR Set members)\n");
+	fprintf(stderr, "(AD)           Unique Additional Record Set (entire RR Set)\n");
+	fprintf(stderr, "(AD+)          Additional records (add to existing RR Set members)\n");
+
+	fprintf(stderr, "\nFinal summary, sorted by service type:\n");
+	fprintf(stderr, "Probe          Probes for this service type starting up\n");
+	fprintf(stderr, "Goodbye        Goodbye (deletion) packets for this service type shutting down\n");
+	fprintf(stderr, "BrowseQ        Browse questions from clients browsing to find a list of instances of this service\n");
+	fprintf(stderr, "BrowseA        Browse answers/announcments advertising instances of this service\n");
+	fprintf(stderr, "ResolveQ       Resolve questions from clients actively connecting to an instance of this service\n");
+	fprintf(stderr, "ResolveA       Resolve answers/announcments giving connection information for an instance of this service\n");
+	fprintf(stderr, "\n");
+	return(-1);
+	}
diff --git a/mdnsresponder/mDNSPosix/PosixDaemon.c b/mdnsresponder/mDNSPosix/PosixDaemon.c
new file mode 100644
index 0000000..e714509
--- /dev/null
+++ b/mdnsresponder/mDNSPosix/PosixDaemon.c
@@ -0,0 +1,282 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+
+	File:		daemon.c
+
+	Contains:	main & associated Application layer for mDNSResponder on Linux.
+
+ */
+
+#if __APPLE__
+// In Mac OS X 10.5 and later trying to use the daemon function gives a “‘daemon’ is deprecated”
+// error, which prevents compilation because we build with "-Werror".
+// Since this is supposed to be portable cross-platform code, we don't care that daemon is
+// deprecated on Mac OS X 10.5, so we use this preprocessor trick to eliminate the error message.
+#define daemon yes_we_know_that_daemon_is_deprecated_in_os_x_10_5_thankyou
+#endif
+
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <errno.h>
+#include <fcntl.h>
+#include <pwd.h>
+#include <sys/types.h>
+
+#ifdef __ANDROID__
+#include "cutils/sockets.h"
+#endif
+
+#if __APPLE__
+#undef daemon
+extern int daemon(int, int);
+#endif
+
+#include "mDNSEmbeddedAPI.h"
+#include "mDNSPosix.h"
+#include "mDNSUNP.h"		// For daemon()
+#include "uds_daemon.h"
+#include "PlatformCommon.h"
+
+#ifndef MDNS_USERNAME
+#define MDNS_USERNAME "nobody"
+#endif
+
+#define CONFIG_FILE "/etc/mdnsd.conf"
+static domainname DynDNSZone;                // Default wide-area zone for service registration
+static domainname DynDNSHostname;
+
+#define RR_CACHE_SIZE 500
+static CacheEntity gRRCache[RR_CACHE_SIZE];
+static mDNS_PlatformSupport PlatformStorage;
+
+mDNSlocal void mDNS_StatusCallback(mDNS *const m, mStatus result)
+	{
+	(void)m; // Unused
+	if (result == mStatus_NoError)
+		{
+		// On successful registration of dot-local mDNS host name, daemon may want to check if
+		// any name conflict and automatic renaming took place, and if so, record the newly negotiated
+		// name in persistent storage for next time. It should also inform the user of the name change.
+		// On Mac OS X we store the current dot-local mDNS host name in the SCPreferences store,
+		// and notify the user with a CFUserNotification.
+		}
+	else if (result == mStatus_ConfigChanged)
+		{
+		udsserver_handle_configchange(m);
+		}
+	else if (result == mStatus_GrowCache)
+		{
+		// Allocate another chunk of cache storage
+		CacheEntity *storage = malloc(sizeof(CacheEntity) * RR_CACHE_SIZE);
+		if (storage) mDNS_GrowCache(m, storage, RR_CACHE_SIZE);
+		}
+	}
+
+// %%% Reconfigure() probably belongs in the platform support layer (mDNSPosix.c), not the daemon cde
+// -- all client layers running on top of mDNSPosix.c need to handle network configuration changes,
+// not only the Unix Domain Socket Daemon
+
+static void Reconfigure(mDNS *m)
+	{
+	mDNSAddr DynDNSIP;
+	const mDNSAddr dummy = { mDNSAddrType_IPv4, { { { 1, 1, 1, 1 } } } };;
+	mDNS_SetPrimaryInterfaceInfo(m, NULL, NULL, NULL);
+	if (ParseDNSServers(m, uDNS_SERVERS_FILE) < 0)
+		LogMsg("Unable to parse DNS server list. Unicast DNS-SD unavailable");
+	ReadDDNSSettingsFromConfFile(m, CONFIG_FILE, &DynDNSHostname, &DynDNSZone, NULL);
+	mDNSPlatformSourceAddrForDest(&DynDNSIP, &dummy);
+	if (DynDNSHostname.c[0]) mDNS_AddDynDNSHostName(m, &DynDNSHostname, NULL, NULL);
+	if (DynDNSIP.type)       mDNS_SetPrimaryInterfaceInfo(m, &DynDNSIP, NULL, NULL);
+	mDNS_ConfigChanged(m);
+	}
+
+// Do appropriate things at startup with command line arguments. Calls exit() if unhappy.
+mDNSlocal void ParseCmdLinArgs(int argc, char **argv)
+	{
+	if (argc > 1)
+		{
+		if (0 == strcmp(argv[1], "-debug")) mDNS_DebugMode = mDNStrue;
+		else printf("Usage: %s [-debug]\n", argv[0]);
+		}
+#ifndef __ANDROID__
+	if (!mDNS_DebugMode)
+		{
+		int result = daemon(0, 0);
+		if (result != 0) { LogMsg("Could not run as daemon - exiting"); exit(result); }
+#if __APPLE__
+		LogMsg("The POSIX mdnsd should only be used on OS X for testing - exiting");
+		exit(-1);
+#endif
+		}
+#endif // !__ANDROID__
+	}
+
+mDNSlocal void DumpStateLog(mDNS *const m)
+// Dump a little log of what we've been up to.
+	{
+	LogMsg("---- BEGIN STATE LOG ----");
+	udsserver_info(m);
+	LogMsg("----  END STATE LOG  ----");
+	}
+
+mDNSlocal mStatus MainLoop(mDNS *m) // Loop until we quit.
+	{
+	sigset_t	signals;
+	mDNSBool	gotData = mDNSfalse;
+
+	mDNSPosixListenForSignalInEventLoop(SIGINT);
+	mDNSPosixListenForSignalInEventLoop(SIGTERM);
+	mDNSPosixListenForSignalInEventLoop(SIGUSR1);
+	mDNSPosixListenForSignalInEventLoop(SIGPIPE);
+	mDNSPosixListenForSignalInEventLoop(SIGHUP) ;
+
+	for (; ;)
+		{
+		// Work out how long we expect to sleep before the next scheduled task
+		struct timeval	timeout;
+		mDNSs32			ticks;
+
+		// Only idle if we didn't find any data the last time around
+		if (!gotData)
+			{
+			mDNSs32			nextTimerEvent = mDNS_Execute(m);
+			nextTimerEvent = udsserver_idle(nextTimerEvent);
+			ticks = nextTimerEvent - mDNS_TimeNow(m);
+			if (ticks < 1) ticks = 1;
+			}
+		else	// otherwise call EventLoop again with 0 timemout
+			ticks = 0;
+
+		timeout.tv_sec = ticks / mDNSPlatformOneSecond;
+		timeout.tv_usec = (ticks % mDNSPlatformOneSecond) * 1000000 / mDNSPlatformOneSecond;
+
+		(void) mDNSPosixRunEventLoopOnce(m, &timeout, &signals, &gotData);
+
+		if (sigismember(&signals, SIGHUP )) Reconfigure(m);
+		if (sigismember(&signals, SIGUSR1)) DumpStateLog(m);
+		// SIGPIPE happens when we try to write to a dead client; death should be detected soon in request_callback() and cleaned up.
+		if (sigismember(&signals, SIGPIPE)) LogMsg("Received SIGPIPE - ignoring");
+		if (sigismember(&signals, SIGINT) || sigismember(&signals, SIGTERM)) break;
+		}
+	return EINTR;
+	}
+
+int main(int argc, char **argv)
+	{
+	mStatus					err;
+
+	ParseCmdLinArgs(argc, argv);
+
+	LogMsg("%s starting", mDNSResponderVersionString);
+
+	err = mDNS_Init(&mDNSStorage, &PlatformStorage, gRRCache, RR_CACHE_SIZE, mDNS_Init_AdvertiseLocalAddresses, 
+					mDNS_StatusCallback, mDNS_Init_NoInitCallbackContext); 
+
+	if (mStatus_NoError == err)
+#ifdef __ANDROID__
+		{
+		dnssd_sock_t s[1];
+		char *socketname = strrchr(MDNS_UDS_SERVERPATH, '/');
+		if (socketname)
+			{
+			socketname++; // skip '/'
+			s[0] = android_get_control_socket(socketname);
+			err = udsserver_init(s, 1);
+			} else {
+			err = udsserver_init(mDNSNULL, 0);
+			}
+		}
+#else
+		err = udsserver_init(mDNSNULL, 0);
+#endif // __ANDROID__
+		
+	Reconfigure(&mDNSStorage);
+
+	// Now that we're finished with anything privileged, switch over to running as "nobody"
+	if (mStatus_NoError == err)
+		{
+		const struct passwd *pw = getpwnam(MDNS_USERNAME);
+		if (pw != NULL)
+			setuid(pw->pw_uid);
+		else
+			LogMsg("WARNING: mdnsd continuing as root because user \"%s\" does not exist", MDNS_USERNAME);
+		}
+
+	if (mStatus_NoError == err)
+		err = MainLoop(&mDNSStorage);
+ 
+	LogMsg("%s stopping", mDNSResponderVersionString);
+
+	mDNS_Close(&mDNSStorage);
+
+	if (udsserver_exit() < 0)
+		LogMsg("ExitCallback: udsserver_exit failed");
+ 
+ #if MDNS_DEBUGMSGS > 0
+	printf("mDNSResponder exiting normally with %ld\n", err);
+ #endif
+ 
+	return err;
+	}
+
+//		uds_daemon support		////////////////////////////////////////////////////////////
+
+mStatus udsSupportAddFDToEventLoop(int fd, udsEventCallback callback, void *context, void **platform_data)
+/* Support routine for uds_daemon.c */
+	{
+	// Depends on the fact that udsEventCallback == mDNSPosixEventCallback
+	(void) platform_data;
+	return mDNSPosixAddFDToEventLoop(fd, callback, context);
+	}
+
+int udsSupportReadFD(dnssd_sock_t fd, char *buf, int len, int flags, void *platform_data)
+	{
+	(void) platform_data;
+	return recv(fd, buf, len, flags);
+	}
+
+mStatus udsSupportRemoveFDFromEventLoop(int fd, void *platform_data)		// Note: This also CLOSES the file descriptor
+	{
+	mStatus err = mDNSPosixRemoveFDFromEventLoop(fd);
+	(void) platform_data;
+	close(fd);
+	return err;
+	}
+
+mDNSexport void RecordUpdatedNiceLabel(mDNS *const m, mDNSs32 delay)
+	{
+	(void)m;
+	(void)delay;
+	// No-op, for now
+	}
+
+#if _BUILDING_XCODE_PROJECT_
+// If the process crashes, then this string will be magically included in the automatically-generated crash log
+const char *__crashreporter_info__ = mDNSResponderVersionString_SCCS + 5;
+asm(".desc ___crashreporter_info__, 0x10");
+#endif
+
+// For convenience when using the "strings" command, this is the last thing in the file
+#if mDNSResponderVersion > 1
+mDNSexport const char mDNSResponderVersionString_SCCS[] = "@(#) mDNSResponder-" STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")";
+#elif MDNS_VERSIONSTR_NODTS
+mDNSexport const char mDNSResponderVersionString_SCCS[] = "@(#) mDNSResponder (Engineering Build)";
+#else
+mDNSexport const char mDNSResponderVersionString_SCCS[] = "@(#) mDNSResponder (Engineering Build) (" __DATE__ " " __TIME__ ")";
+#endif
diff --git a/mdnsresponder/mDNSPosix/ProxyResponder.c b/mdnsresponder/mDNSPosix/ProxyResponder.c
new file mode 100644
index 0000000..ebbbaa7
--- /dev/null
+++ b/mdnsresponder/mDNSPosix/ProxyResponder.c
@@ -0,0 +1,300 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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 <stdio.h>				// For printf()
+#include <stdlib.h>				// For exit() etc.
+#include <string.h>				// For strlen() etc.
+#include <unistd.h>				// For select()
+#include <signal.h>				// For SIGINT, SIGTERM
+#include <errno.h>				// For errno, EINTR
+#include <netinet/in.h>			// For INADDR_NONE
+#include <arpa/inet.h>			// For inet_addr()
+#include <netdb.h>				// For gethostbyname()
+
+#include "mDNSEmbeddedAPI.h"	// Defines the interface to the client layer above
+#include "mDNSPosix.h"			// Defines the specific types needed to run mDNS on this platform
+#include "ExampleClientApp.h"
+
+// Compatibility workaround: Solaris 2.5 has no INADDR_NONE
+#ifndef	INADDR_NONE
+#define	INADDR_NONE	(mDNSu32)0xffffffff
+#endif
+
+//*************************************************************************************************************
+// Globals
+static mDNS mDNSStorage;       // mDNS core uses this to store its globals
+static mDNS_PlatformSupport PlatformStorage;  // Stores this platform's globals
+mDNSexport const char ProgramName[] = "mDNSProxyResponderPosix";
+
+//*************************************************************************************************************
+// Proxy Host Registration
+
+typedef struct
+	{
+	mDNSv4Addr ip;
+	domainlabel hostlabel;		// Conforms to standard DNS letter-digit-hyphen host name rules
+	AuthRecord RR_A;		// 'A' (address) record for our ".local" name
+	AuthRecord RR_PTR;		// PTR (reverse lookup) record
+	} ProxyHost;
+
+mDNSlocal void HostNameCallback(mDNS *const m, AuthRecord *const rr, mStatus result)
+	{
+	ProxyHost *f = (ProxyHost*)rr->RecordContext;
+	if (result == mStatus_NoError)
+		debugf("Host name successfully registered: %##s", rr->resrec.name->c);
+	else
+		{
+		debugf("Host name conflict for %##s", rr->resrec.name->c);
+		mDNS_Deregister(m, &f->RR_A);
+		mDNS_Deregister(m, &f->RR_PTR);
+		exit(-1);
+		}
+	}
+
+mDNSlocal mStatus mDNS_RegisterProxyHost(mDNS *m, ProxyHost *p)
+	{
+	char buffer[32];
+	
+	mDNS_SetupResourceRecord(&p->RR_A,   mDNSNULL, mDNSInterface_Any, kDNSType_A,   60, kDNSRecordTypeUnique,      AuthRecordAny, HostNameCallback, p);
+	mDNS_SetupResourceRecord(&p->RR_PTR, mDNSNULL, mDNSInterface_Any, kDNSType_PTR, 60, kDNSRecordTypeKnownUnique, AuthRecordAny, HostNameCallback, p);
+
+	p->RR_A.namestorage.c[0] = 0;
+	AppendDomainLabel(&p->RR_A.namestorage, &p->hostlabel);
+	AppendLiteralLabelString(&p->RR_A.namestorage, "local");
+
+	// Note: This is reverse order compared to a normal dotted-decimal IP address, so we can't use our customary "%.4a" format code
+	mDNS_snprintf(buffer, sizeof(buffer), "%d.%d.%d.%d.in-addr.arpa.", p->ip.b[3], p->ip.b[2], p->ip.b[1], p->ip.b[0]);
+	MakeDomainNameFromDNSNameString(&p->RR_PTR.namestorage, buffer);
+	p->RR_PTR.ForceMCast = mDNStrue; // This PTR points to our dot-local name, so don't ever try to write it into a uDNS server
+
+	p->RR_A.  resrec.rdata->u.ipv4 = p->ip;
+	AssignDomainName(&p->RR_PTR.resrec.rdata->u.name, p->RR_A.resrec.name);
+
+	mDNS_Register(m, &p->RR_A);
+	mDNS_Register(m, &p->RR_PTR);
+
+	debugf("Made Proxy Host Records for %##s", p->RR_A.resrec.name->c);
+	
+	return(mStatus_NoError);
+	}
+
+//*************************************************************************************************************
+// Service Registration
+
+// This sample ServiceCallback just calls mDNS_RenameAndReregisterService to automatically pick a new
+// unique name for the service. For a device such as a printer, this may be appropriate.
+// For a device with a user interface, and a screen, and a keyboard, the appropriate
+// response may be to prompt the user and ask them to choose a new name for the service.
+mDNSlocal void ServiceCallback(mDNS *const m, ServiceRecordSet *const sr, mStatus result)
+	{
+	switch (result)
+		{
+		case mStatus_NoError:      debugf("Callback: %##s Name Registered",    sr->RR_SRV.resrec.name->c); break;
+		case mStatus_NameConflict: debugf("Callback: %##s Name Conflict",      sr->RR_SRV.resrec.name->c); break;
+		case mStatus_MemFree:      debugf("Callback: %##s Memory Free",        sr->RR_SRV.resrec.name->c); break;
+		default:                   debugf("Callback: %##s Unknown Result %ld", sr->RR_SRV.resrec.name->c, result); break;
+		}
+
+	if (result == mStatus_NoError)
+		{
+		char buffer[MAX_ESCAPED_DOMAIN_NAME];
+		ConvertDomainNameToCString(sr->RR_SRV.resrec.name, buffer);
+		printf("Service %s now registered and active\n", buffer);
+		}
+
+	if (result == mStatus_NameConflict)
+		{
+		char buffer1[MAX_ESCAPED_DOMAIN_NAME], buffer2[MAX_ESCAPED_DOMAIN_NAME];
+		ConvertDomainNameToCString(sr->RR_SRV.resrec.name, buffer1);
+		mDNS_RenameAndReregisterService(m, sr, mDNSNULL);
+		ConvertDomainNameToCString(sr->RR_SRV.resrec.name, buffer2);
+		printf("Name Conflict! %s renamed as %s\n", buffer1, buffer2);
+		}
+	}
+
+// RegisterService() is a simple wrapper function which takes C string
+// parameters, converts them to domainname parameters, and calls mDNS_RegisterService()
+mDNSlocal void RegisterService(mDNS *m, ServiceRecordSet *recordset,
+	const char name[], const char type[], const char domain[],
+	const domainname *host, mDNSu16 PortAsNumber, int argc, char **argv)
+	{
+	domainlabel n;
+	domainname t, d;
+	unsigned char txtbuffer[1024], *bptr = txtbuffer;
+	char buffer[MAX_ESCAPED_DOMAIN_NAME];
+
+	MakeDomainLabelFromLiteralString(&n, name);
+	MakeDomainNameFromDNSNameString(&t, type);
+	MakeDomainNameFromDNSNameString(&d, domain);
+	while (argc)
+		{
+		int len = strlen(argv[0]);
+		if (len > 255 || bptr + 1 + len >= txtbuffer + sizeof(txtbuffer)) break;
+		printf("STR: %s\n", argv[0]);
+		bptr[0] = len;
+		strcpy((char*)(bptr+1), argv[0]);
+		bptr += 1 + len;
+		argc--;
+		argv++;
+		}
+	
+	mDNS_RegisterService(m, recordset,
+		&n, &t, &d,					// Name, type, domain
+		host, mDNSOpaque16fromIntVal(PortAsNumber),
+		txtbuffer, bptr-txtbuffer,	// TXT data, length
+		mDNSNULL, 0,				// Subtypes
+		mDNSInterface_Any,			// Interface ID
+		ServiceCallback, mDNSNULL, 0);	// Callback, context, flags
+
+	ConvertDomainNameToCString(recordset->RR_SRV.resrec.name, buffer);
+	printf("Made Service Records for %s\n", buffer);
+	}
+
+//*************************************************************************************************************
+// Service non-existence assertion
+// (claiming a service name without actually providing a service at that name, to prevent anyone else using that name)
+// This is useful to avoid confusion between similar services
+// e.g. A printer that implements IPP printing service using the name "My Printer", but doesn't implement LPR service,
+// should also claim the LPR service name "My Printer" to stop a different printer offering LPR service under the same name,
+// since it would be confusing to users to have two equivalent services with the same name.
+
+mDNSlocal void NoSuchServiceCallback(mDNS *const m, AuthRecord *const rr, mStatus result)
+	{
+	const domainname *proxyhostname = (const domainname *)rr->RecordContext;
+	switch (result)
+		{
+		case mStatus_NoError:      debugf("Callback: %##s Name Registered",    rr->resrec.name->c); break;
+		case mStatus_NameConflict: debugf("Callback: %##s Name Conflict",      rr->resrec.name->c); break;
+		case mStatus_MemFree:      debugf("Callback: %##s Memory Free",        rr->resrec.name->c); break;
+		default:                   debugf("Callback: %##s Unknown Result %ld", rr->resrec.name->c, result); break;
+		}
+
+	if (result == mStatus_NoError)
+		{
+		char buffer[MAX_ESCAPED_DOMAIN_NAME];
+		ConvertDomainNameToCString(rr->resrec.name, buffer);
+		printf("Non-existence assertion %s now registered and active\n", buffer);
+		}
+
+	if (result == mStatus_NameConflict)
+		{
+		domainlabel n;
+		domainname t, d;
+		char buffer1[MAX_ESCAPED_DOMAIN_NAME], buffer2[MAX_ESCAPED_DOMAIN_NAME];
+		ConvertDomainNameToCString(rr->resrec.name, buffer1);
+		DeconstructServiceName(rr->resrec.name, &n, &t, &d);
+		IncrementLabelSuffix(&n, mDNStrue);
+		mDNS_RegisterNoSuchService(m, rr, &n, &t, &d, proxyhostname, mDNSInterface_Any, NoSuchServiceCallback, mDNSNULL, mDNSfalse);
+		ConvertDomainNameToCString(rr->resrec.name, buffer2);
+		printf("Name Conflict! %s renamed as %s\n", buffer1, buffer2);
+		}
+	}
+
+mDNSlocal void RegisterNoSuchService(mDNS *m, AuthRecord *const rr, domainname *proxyhostname,
+	const char name[], const char type[], const char domain[])
+	{
+	domainlabel n;
+	domainname t, d;
+	char buffer[MAX_ESCAPED_DOMAIN_NAME];
+	MakeDomainLabelFromLiteralString(&n, name);
+	MakeDomainNameFromDNSNameString(&t, type);
+	MakeDomainNameFromDNSNameString(&d, domain);
+	mDNS_RegisterNoSuchService(m, rr, &n, &t, &d, proxyhostname, mDNSInterface_Any, NoSuchServiceCallback, proxyhostname, mDNSfalse);
+	ConvertDomainNameToCString(rr->resrec.name, buffer);
+	printf("Made Non-existence Record for %s\n", buffer);
+	}
+
+//*************************************************************************************************************
+// Main
+
+mDNSexport int main(int argc, char **argv)
+	{
+	mStatus			status;
+	sigset_t		signals;
+	
+	if (argc < 3) goto usage;
+	
+	status = mDNS_Init(&mDNSStorage, &PlatformStorage,
+		mDNS_Init_NoCache, mDNS_Init_ZeroCacheSize,
+		mDNS_Init_DontAdvertiseLocalAddresses,
+		mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext);
+	if (status) { fprintf(stderr, "Daemon start: mDNS_Init failed %d\n", (int)status); return(status); }
+
+	mDNSPosixListenForSignalInEventLoop(SIGINT);
+	mDNSPosixListenForSignalInEventLoop(SIGTERM);
+
+	if (!strcmp(argv[1], "-"))
+		{
+		domainname proxyhostname;
+		AuthRecord proxyrecord;
+		if (argc < 5) goto usage;
+		proxyhostname.c[0] = 0;
+		AppendLiteralLabelString(&proxyhostname, argv[2]);
+		AppendLiteralLabelString(&proxyhostname, "local");
+		RegisterNoSuchService(&mDNSStorage, &proxyrecord, &proxyhostname, argv[3], argv[4], "local.");
+		}
+	else
+		{
+		ProxyHost proxyhost;
+		ServiceRecordSet proxyservice;
+
+		proxyhost.ip.NotAnInteger = inet_addr(argv[1]);
+		if (proxyhost.ip.NotAnInteger == INADDR_NONE)	// INADDR_NONE is 0xFFFFFFFF
+			{
+			struct hostent *h = gethostbyname(argv[1]);
+			if (h) proxyhost.ip.NotAnInteger = *(long*)h->h_addr;
+			}
+		if (proxyhost.ip.NotAnInteger == INADDR_NONE)	// INADDR_NONE is 0xFFFFFFFF
+			{
+			fprintf(stderr, "%s is not valid host address\n", argv[1]);
+			return(-1);
+			}
+	
+		MakeDomainLabelFromLiteralString(&proxyhost.hostlabel, argv[2]);
+
+		mDNS_RegisterProxyHost(&mDNSStorage, &proxyhost);
+
+		if (argc >=6)
+			RegisterService(&mDNSStorage, &proxyservice, argv[3], argv[4], "local.",
+							proxyhost.RR_A.resrec.name, atoi(argv[5]), argc-6, &argv[6]);
+		}
+
+	do 
+		{
+		struct timeval	timeout = { 0x3FFFFFFF, 0 };	// wait until SIGINT or SIGTERM
+		mDNSBool		gotSomething;
+		mDNSPosixRunEventLoopOnce(&mDNSStorage, &timeout, &signals, &gotSomething);
+		}
+	while ( !( sigismember( &signals, SIGINT) || sigismember( &signals, SIGTERM)));
+
+	mDNS_Close(&mDNSStorage);
+
+	return(0);
+
+usage:
+	fprintf(stderr, "%s ip hostlabel [srvname srvtype port txt [txt ...]]\n", argv[0]);
+	fprintf(stderr, "ip        Real IP address (or valid host name) of the host where the service actually resides\n");
+	fprintf(stderr, "hostlabel First label of the dot-local host name to create for this host, e.g. \"foo\" for \"foo.local.\"\n");
+	fprintf(stderr, "srvname   Descriptive name of service, e.g. \"Stuart's Ink Jet Printer\"\n");
+	fprintf(stderr, "srvtype   IANA service type, e.g. \"_ipp._tcp\" or \"_ssh._tcp\", etc.\n");
+	fprintf(stderr, "port      Port number where the service resides (1-65535)\n");
+	fprintf(stderr, "txt       Additional name/value pairs specified in service definition, e.g. \"pdl=application/postscript\"\n");
+	fprintf(stderr, "e.g. %s 169.254.12.34 thehost                                (just create a dot-local host name)\n", argv[0]);
+	fprintf(stderr, "or   %s 169.254.12.34 thehost \"My Printer\" _printer._tcp. 515 rp=lpt1 pdl=application/postscript\n", argv[0]);
+	fprintf(stderr, "or   %s -             thehost \"My Printer\" _printer._tcp.           (assertion of non-existence)\n", argv[0]);
+	return(-1);
+	}
diff --git a/mdnsresponder/mDNSPosix/ReadMe.txt b/mdnsresponder/mDNSPosix/ReadMe.txt
new file mode 100755
index 0000000..c2f5641
--- /dev/null
+++ b/mdnsresponder/mDNSPosix/ReadMe.txt
@@ -0,0 +1,314 @@
+ReadMe About mDNSPosix
+----------------------
+
+mDNSPosix is a port of Apple's Multicast DNS and DNS Service Discovery
+code to Posix platforms.
+
+Multicast DNS and DNS Service Discovery are technologies that allow you
+to register IP-based services and browse the network for those services.
+For more information about mDNS, see the mDNS web site.
+
+  <http://www.multicastdns.org/>
+
+Multicast DNS is part of a family of technologies resulting from the
+efforts of the IETF Zeroconf working group.  For information about
+other Zeroconf technologies, see the Zeroconf web site.
+
+  <http://www.zeroconf.org/>
+
+Apple uses the trade mark "Bonjour" to describe our implementation of
+Zeroconf technologies.  This sample is designed to show how easy it is
+to make a device "Bonjour compatible".
+
+The "Bonjour" trade mark can also be licensed at no charge for
+inclusion on your own products, packaging, manuals, promotional
+materials, or web site. For details and licensing terms, see
+
+  <http://developer.apple.com/bonjour/>
+
+The code in this sample was compiled and tested on Mac OS X (10.1.x,
+10.2, 10.3), Solaris (SunOS 5.8), Linux (Redhat 2.4.9-21, Fedora Core 1), 
+and OpenBSD (2.9). YMMV.
+
+
+Packing List
+------------
+
+The sample uses the following directories:
+
+o mDNSCore -- A directory containing the core mDNS code.  This code
+  is written in pure ANSI C and has proved to be very portable.
+  Every platform needs this core protocol engine code.
+
+o mDNSShared -- A directory containing useful code that's not core to
+  the main protocol engine itself, but nonetheless useful, and used by
+  more than one (but not necessarily all) platforms.
+
+o mDNSPosix -- The files that are specific to Posix platforms: Linux,
+  Solaris, FreeBSD, NetBSD, OpenBSD, etc. This code will also work on
+  OS X, though that's not its primary purpose.
+
+o Clients -- Example client code showing how to use the API to the
+  services provided by the daemon.
+
+
+Building the Code
+-----------------
+
+The sample does not use autoconf technology, primarily because I didn't
+want to delay shipping while I learnt how to use it.  Thus the code
+builds using a very simple make file.  To build the sample you should
+cd to the mDNSPosix directory and type "make os=myos", e.g.
+
+    make os=panther
+
+For Linux you would change that to:
+
+    make os=linux
+
+There are definitions for each of the platforms I ported to.  If you're
+porting to any other platform please add appropriate definitions for it
+and send us the diffs so they can be incorporated into the main
+distribution.
+
+
+Using the Sample
+----------------
+When you compile, you will get:
+
+o Main products for general-purpose use (e.g. on a desktop computer):
+  - mdnsd
+  - libmdns
+  - nss_mdns (See nss_ReadMe.txt for important information about nss_mdns)
+
+o Standalone products for dedicated devices (printer, network camera, etc.)
+  - mDNSClientPosix
+  - mDNSResponderPosix
+  - mDNSProxyResponderPosix
+
+o Testing and Debugging tools
+  - dns-sd command-line tool (from the "Clients" folder)
+  - mDNSNetMonitor
+  - mDNSIdentify
+
+As root type "make install" to install eight things:
+o mdnsd                   (usually in /usr/sbin)
+o libmdns                 (usually in /usr/lib)
+o dns_sd.h                (usually in /usr/include)
+o startup scripts         (e.g. in /etc/rc.d)
+o manual pages            (usually in /usr/share/man)
+o dns-sd tool             (usually in /usr/bin)
+o nss_mdns                (usually in /lib)
+o nss configuration files (usually in /etc)
+
+The "make install" concludes by executing the startup script
+(usually "/etc/init.d/mdns start") to start the daemon running.
+You shouldn't need to reboot unless you really want to.
+
+Once the daemon is running, you can use the dns-sd test tool
+to exercise all the major functionality of the daemon. Running
+"dns-sd" with no arguments gives a summary of the available options.
+This test tool is also described in detail, with several examples,
+in Chapter 6 of the O'Reilly "Zero Configuration Networking" book.
+
+
+How It Works
+------------
+                                                   +--------------------+
+                                                   | Client Application |
+   +----------------+                              +--------------------+
+   |  uds_daemon.c  | <--- Unix Domain Socket ---> |      libmdns       |
+   +----------------+                              +--------------------+
+   |    mDNSCore    |
+   +----------------+
+   |  mDNSPosix.c   |
+   +----------------+
+
+mdnsd is divided into three sections.
+
+o mDNSCore is the main protocol engine
+o mDNSPosix.c provides the glue it needs to run on a Posix OS
+o uds_daemon.c exports a Unix Domain Socket interface to
+  the services provided by mDNSCore
+
+Client applications link with the libmdns, which implements the functions
+defined in the dns_sd.h header file, and implements the IPC protocol
+used to communicate over the Unix Domain Socket interface to the daemon.
+
+Note that, strictly speaking, nss_mdns could be just another client of
+mdnsd, linking with libmdns just like any other client. However, because
+of its central role in the normal operation of multicast DNS, it is built
+and installed along with the other essential system support components.
+
+
+Clients for Embedded Systems
+----------------------------
+
+For small devices with very constrained resources, with a single address
+space and (typically) no virtual memory, the uds_daemon.c/UDS/libmdns
+layer may be eliminated, and the Client Application may live directly
+on top of mDNSCore:
+
+    +--------------------+
+    | Client Application |
+    +--------------------+
+    |      mDNSCore      |
+    +--------------------+
+    |    mDNSPosix.c     |
+    +--------------------+
+
+Programming to this model is more work, so using the daemon and its
+library is recommended if your platform is capable of that.
+
+The runtime behaviour when using the embedded model is as follows:
+
+1. The application calls mDNS_Init, which in turns calls the platform
+   (mDNSPlatformInit).
+
+2. mDNSPlatformInit gets a list of interfaces (get_ifi_info) and registers
+   each one with the core (mDNS_RegisterInterface).  For each interface
+   it also creates a multicast socket (SetupSocket).
+
+3. The application then calls select() repeatedly to handle file descriptor
+   events. Before calling select() each time, the application calls
+   mDNSPosixGetFDSet() to give mDNSPosix.c a chance to add its own file
+   descriptors to the set, and then after select() returns, it calls
+   mDNSPosixProcessFDSet() to give mDNSPosix.c a chance to receive and
+   process any packets that may have arrived.
+
+4. When the core needs to send a UDP packet it calls
+   mDNSPlatformSendUDP.  That routines finds the interface that
+   corresponds to the source address requested by the core, and
+   sends the datagram using the UDP socket created for the
+   interface.  If the socket is flow send-side controlled it just
+   drops the packet.
+
+5. When SocketDataReady runs it uses a complex routine,
+   "recvfrom_flags", to actually receive the packet.  This is required
+   because the core needs information about the packet that is
+   only available via the "recvmsg" call, and that call is complex
+   to implement in a portable way.  I got my implementation of
+   "recvfrom_flags" from Stevens' "UNIX Network Programming", but
+   I had to modify it further to work with Linux.
+
+One thing to note is that the Posix platform code is very deliberately
+not multi-threaded.  I do everything from a main loop that calls
+"select()".  This is good because it avoids all the problems that often
+accompany multi-threaded code. If you decide to use threads in your
+platform, you will have to implement the mDNSPlatformLock() and
+mDNSPlatformUnlock() calls which are currently no-ops in mDNSPosix.c.
+
+
+Once you've built the embedded samples you can test them by first
+running the client, as shown below.
+
+  quinn% build/mDNSClientPosix
+  Hit ^C when you're bored waiting for responses.
+
+By default the client starts a search for AppleShare servers and then
+sits and waits, printing a message when services appear and disappear.
+
+To continue with the test you should start the responder in another
+shell window.
+
+  quinn% build/mDNSResponderPosix -n Foo
+
+This will start the responder and tell it to advertise a AppleShare
+service "Foo".  In the client window you will see the client print out
+the following as the service shows up on the network.
+
+  quinn% build/mDNSClientPosix
+  Hit ^C when you're bored waiting for responses.
+  *** Found name = 'Foo', type = '_afpovertcp._tcp.', domain = 'local.'
+
+Back in the responder window you can quit the responder cleanly using
+SIGINT (typically ^C).
+
+  quinn% build/mDNSResponderPosix -n Foo
+  ^C
+  quinn%
+
+As the responder quits it will multicast that the "Foo" service is
+disappearing and the client will see that notification and print a
+message to that effect (shown below).  Finally, when you're done with
+the client you can use SIGINT to quit it.
+
+  quinn% build/mDNSClientPosix
+  Hit ^C when you're bored waiting for responses.
+  *** Found name = 'Foo', type = '_afpovertcp._tcp.', domain = 'local.'
+  *** Lost  name = 'Foo', type = '_afpovertcp._tcp.', domain = 'local.'
+  ^C
+  quinn%
+
+If things don't work, try starting each program in verbose mode (using
+the "-v 1" option, or very verbose mode with "-v 2") to see if there's
+an obvious cause.
+
+That's it for the core functionality.  Each program supports a variety
+of other options.  For example, you can advertise and browse for a
+different service type using the "-t type" option.  Use the "-?" option
+on each program for more user-level information.
+
+
+Caveats
+-------
+Currently the program uses a simple make file.
+
+The Multicast DNS protocol can also operate locally over the loopback
+interface, but this exposed some problems with the underlying network
+stack in early versions of Mac OS X and may expose problems with other
+network stacks too.
+
+o On Mac OS X 10.1.x the code failed to start on the loopback interface
+  because the IP_ADD_MEMBERSHIP option returns ENOBUFS.
+
+o On Mac OS X 10.2 the loopback-only case failed because
+  "sendto" calls fails with error EHOSTUNREACH. (3016042)
+
+Consequently, the code will attempt service discovery on the loopback
+interface only if no other interfaces are available.
+
+I haven't been able to test the loopback-only case on other platforms
+because I don't have access to the physical machine.
+
+
+Licencing
+---------
+This source code is Open Source; for details see the "LICENSE" file.
+
+Credits and Version History
+---------------------------
+If you find any problems with this sample, mail <dts@apple.com> and I
+will try to fix them up.
+
+1.0a1 (Jul 2002) was a prerelease version that was distributed
+internally at Apple.
+
+1.0a2 (Jul 2002) was a prerelease version that was distributed
+internally at Apple.
+
+1.0a3 (Aug 2002) was the first shipping version.  The core mDNS code is
+the code from Mac OS 10.2 (Jaguar) GM.
+
+Share and Enjoy
+
+Apple Developer Technical Support
+Networking, Communications, Hardware
+
+6 Aug 2002
+
+Impact: A local network user may cause a denial of the Bonjour service
+Description: An error handling issue exists in the Bonjour Namespace
+Provider. A local network user may send a maliciously crafted multicast
+DNS packet leading to an unexpected termination of the Bonjour service.
+This update addresses the issue by performing additional validation of
+multicast DNS packets. This issue does not affect systems running Mac OS
+X or Windows.
+CVE-ID
+CVE-2011-0220 : JaeSeung Song of the Department of Computing at Imperial
+College London
+
+To Do List
+----------
+• port to a System V that's not Solaris
+• use sig_atomic_t for signal to main thread flags
diff --git a/mdnsresponder/mDNSPosix/Responder.c b/mdnsresponder/mDNSPosix/Responder.c
new file mode 100755
index 0000000..d4dc385
--- /dev/null
+++ b/mdnsresponder/mDNSPosix/Responder.c
@@ -0,0 +1,792 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+#if __APPLE__
+// In Mac OS X 10.5 and later trying to use the daemon function gives a “‘daemon’ is deprecated”
+// error, which prevents compilation because we build with "-Werror".
+// Since this is supposed to be portable cross-platform code, we don't care that daemon is
+// deprecated on Mac OS X 10.5, so we use this preprocessor trick to eliminate the error message.
+#define daemon yes_we_know_that_daemon_is_deprecated_in_os_x_10_5_thankyou
+#endif
+
+#include <assert.h>
+#include <stdio.h>			// For printf()
+#include <stdlib.h>			// For exit() etc.
+#include <string.h>			// For strlen() etc.
+#include <unistd.h>			// For select()
+#include <errno.h>			// For errno, EINTR
+#include <signal.h>
+#include <fcntl.h>
+
+#if __APPLE__
+#undef daemon
+extern int daemon(int, int);
+#endif
+
+#include "mDNSEmbeddedAPI.h"// Defines the interface to the client layer above
+#include "mDNSPosix.h"		// Defines the specific types needed to run mDNS on this platform
+#include "mDNSUNP.h"		// For daemon()
+
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark ***** Globals
+#endif
+
+static mDNS mDNSStorage;       // mDNS core uses this to store its globals
+static mDNS_PlatformSupport PlatformStorage;  // Stores this platform's globals
+
+mDNSexport const char ProgramName[] = "mDNSResponderPosix";
+
+static const char *gProgramName = ProgramName;
+
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark ***** Signals
+#endif
+
+static volatile mDNSBool gReceivedSigUsr1;
+static volatile mDNSBool gReceivedSigHup;
+static volatile mDNSBool gStopNow;
+
+// We support 4 signals.
+//
+// o SIGUSR1 toggles verbose mode on and off in debug builds
+// o SIGHUP  triggers the program to re-read its preferences.
+// o SIGINT  causes an orderly shutdown of the program.
+// o SIGQUIT causes a somewhat orderly shutdown (direct but dangerous)
+// o SIGKILL kills us dead (easy to implement :-)
+//
+// There are fatal race conditions in our signal handling, but there's not much 
+// we can do about them while remaining within the Posix space.  Specifically, 
+// if a signal arrives after we test the globals its sets but before we call 
+// select, the signal will be dropped.  The user will have to send the signal 
+// again.  Unfortunately, Posix does not have a "sigselect" to atomically 
+// modify the signal mask and start a select.
+
+static void HandleSigUsr1(int sigraised)
+    // If we get a SIGUSR1 we toggle the state of the 
+    // verbose mode.
+{
+    assert(sigraised == SIGUSR1);
+    gReceivedSigUsr1 = mDNStrue;
+}
+
+static void HandleSigHup(int sigraised)
+    // A handler for SIGHUP that causes us to break out of the 
+    // main event loop when the user kill 1's us.  This has the 
+    // effect of triggered the main loop to deregister the 
+    // current services and re-read the preferences.
+{
+    assert(sigraised == SIGHUP);
+	gReceivedSigHup = mDNStrue;
+}
+
+static void HandleSigInt(int sigraised)
+    // A handler for SIGINT that causes us to break out of the 
+    // main event loop when the user types ^C.  This has the 
+    // effect of quitting the program.
+{
+    assert(sigraised == SIGINT);
+    
+    if (gMDNSPlatformPosixVerboseLevel > 0) {
+        fprintf(stderr, "\nSIGINT\n");
+    }
+    gStopNow = mDNStrue;
+}
+
+static void HandleSigQuit(int sigraised)
+    // If we get a SIGQUIT the user is desperate and we 
+    // just call mDNS_Close directly.  This is definitely 
+    // not safe (because it could reenter mDNS), but 
+    // we presume that the user has already tried the safe 
+    // alternatives.
+{
+    assert(sigraised == SIGQUIT);
+
+    if (gMDNSPlatformPosixVerboseLevel > 0) {
+        fprintf(stderr, "\nSIGQUIT\n");
+    }
+    mDNS_Close(&mDNSStorage);
+    exit(0);
+}
+
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark ***** Parameter Checking
+#endif
+
+static mDNSBool CheckThatRichTextNameIsUsable(const char *richTextName, mDNSBool printExplanation)
+    // Checks that richTextName is reasonable 
+    // label and, if it isn't and printExplanation is true, prints 
+    // an explanation of why not.
+{
+    mDNSBool result = mDNStrue;
+    if (result && strlen(richTextName) > 63) {
+        if (printExplanation) {
+            fprintf(stderr, 
+                    "%s: Service name is too long (must be 63 characters or less)\n", 
+                    gProgramName);
+        }
+        result = mDNSfalse;
+    }
+    if (result && richTextName[0] == 0) {
+        if (printExplanation) {
+            fprintf(stderr, "%s: Service name can't be empty\n", gProgramName);
+        }
+        result = mDNSfalse;
+    }
+    return result;
+}
+
+static mDNSBool CheckThatServiceTypeIsUsable(const char *serviceType, mDNSBool printExplanation)
+    // Checks that serviceType is a reasonable service type 
+    // label and, if it isn't and printExplanation is true, prints 
+    // an explanation of why not.
+{
+    mDNSBool result;
+    
+    result = mDNStrue;
+    if (result && strlen(serviceType) > 63) {
+        if (printExplanation) {
+            fprintf(stderr, 
+                    "%s: Service type is too long (must be 63 characters or less)\n", 
+                    gProgramName);
+        }
+        result = mDNSfalse;
+    }
+    if (result && serviceType[0] == 0) {
+        if (printExplanation) {
+            fprintf(stderr, 
+                    "%s: Service type can't be empty\n", 
+                    gProgramName);
+        }
+        result = mDNSfalse;
+    }
+    return result;
+}
+
+static mDNSBool CheckThatPortNumberIsUsable(long portNumber, mDNSBool printExplanation)
+    // Checks that portNumber is a reasonable port number
+    // and, if it isn't and printExplanation is true, prints 
+    // an explanation of why not.
+{
+    mDNSBool result;
+    
+    result = mDNStrue;
+    if (result && (portNumber <= 0 || portNumber > 65535)) {
+        if (printExplanation) {
+            fprintf(stderr, 
+                    "%s: Port number specified by -p must be in range 1..65535\n", 
+                    gProgramName);
+        }
+        result = mDNSfalse;
+    }
+    return result;
+}
+
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark ***** Command Line Arguments
+#endif
+
+static const char kDefaultPIDFile[]     = "/var/run/mDNSResponder.pid";
+static const char kDefaultServiceType[] = "_afpovertcp._tcp.";
+static const char kDefaultServiceDomain[] = "local.";
+enum {
+    kDefaultPortNumber = 548
+};
+
+static void PrintUsage()
+{
+    fprintf(stderr, 
+            "Usage: %s [-v level ] [-r] [-n name] [-t type] [-d domain] [-p port] [-f file] [-b] [-P pidfile] [-x name=val ...]\n", 
+            gProgramName);
+    fprintf(stderr, "          -v verbose mode, level is a number from 0 to 2\n");
+    fprintf(stderr, "             0 = no debugging info (default)\n");
+    fprintf(stderr, "             1 = standard debugging info\n");
+    fprintf(stderr, "             2 = intense debugging info\n");
+    fprintf(stderr, "             can be cycled kill -USR1\n");
+    fprintf(stderr, "          -r also bind to port 53 (port 5353 is always bound)\n");
+    fprintf(stderr, "          -n uses 'name' as the service name (required)\n");
+    fprintf(stderr, "          -t uses 'type' as the service type (default is '%s')\n", kDefaultServiceType);
+    fprintf(stderr, "          -d uses 'domain' as the service domain (default is '%s')\n", kDefaultServiceDomain);
+    fprintf(stderr, "          -p uses 'port' as the port number (default is '%d')\n",  kDefaultPortNumber);
+    fprintf(stderr, "          -f reads a service list from 'file'\n");
+    fprintf(stderr, "          -b forces daemon (background) mode\n");
+    fprintf(stderr, "          -P uses 'pidfile' as the PID file\n");
+    fprintf(stderr, "             (default is '%s')\n",  kDefaultPIDFile);
+    fprintf(stderr, "             only meaningful if -b also specified\n");
+    fprintf(stderr, "          -x stores name=val in TXT record (default is empty).\n");
+    fprintf(stderr, "             MUST be the last command-line argument;\n");
+    fprintf(stderr, "             all subsequent arguments after -x are treated as name=val pairs.\n");
+}
+
+static   mDNSBool  gAvoidPort53      = mDNStrue;
+static const char *gServiceName      = "";
+static const char *gServiceType      = kDefaultServiceType;
+static const char *gServiceDomain    = kDefaultServiceDomain;
+static mDNSu8      gServiceText[sizeof(RDataBody)];
+static mDNSu16     gServiceTextLen   = 0;
+static        int  gPortNumber       = kDefaultPortNumber;
+static const char *gServiceFile      = "";
+static   mDNSBool  gDaemon           = mDNSfalse;
+static const char *gPIDFile          = kDefaultPIDFile;
+
+static void ParseArguments(int argc, char **argv)
+    // Parses our command line arguments into the global variables 
+    // listed above.
+{
+    int ch;
+    
+    // Set gProgramName to the last path component of argv[0]
+    
+    gProgramName = strrchr(argv[0], '/');
+    if (gProgramName == NULL) {
+        gProgramName = argv[0];
+    } else {
+        gProgramName += 1;
+    }
+    
+    // Parse command line options using getopt.
+    
+    do {
+        ch = getopt(argc, argv, "v:rn:t:d:p:f:dP:bx");
+        if (ch != -1) {
+            switch (ch) {
+                case 'v':
+                    gMDNSPlatformPosixVerboseLevel = atoi(optarg);
+                    if (gMDNSPlatformPosixVerboseLevel < 0 || gMDNSPlatformPosixVerboseLevel > 2) {
+                        fprintf(stderr, 
+                                "%s: Verbose mode must be in the range 0..2\n", 
+                                gProgramName);
+                        exit(1);
+                    }
+                    break;
+                case 'r':
+                    gAvoidPort53 = mDNSfalse;
+                    break;
+                case 'n':
+                    gServiceName = optarg;
+                    if ( ! CheckThatRichTextNameIsUsable(gServiceName, mDNStrue) ) {
+                        exit(1);
+                    }
+                    break;
+                case 't':
+                    gServiceType = optarg;
+                    if ( ! CheckThatServiceTypeIsUsable(gServiceType, mDNStrue) ) {
+                        exit(1);
+                    }
+                    break;
+                case 'd':
+                    gServiceDomain = optarg;
+                    break;
+                case 'p':
+                    gPortNumber = atol(optarg);
+                    if ( ! CheckThatPortNumberIsUsable(gPortNumber, mDNStrue) ) {
+                        exit(1);
+                    }
+                    break;
+                case 'f':
+                    gServiceFile = optarg;
+                    break;
+                case 'b':
+                    gDaemon = mDNStrue;
+                    break;
+                case 'P':
+                    gPIDFile = optarg;
+                    break;
+                case 'x':
+                	while (optind < argc)
+                		{
+                		gServiceText[gServiceTextLen] = strlen(argv[optind]);
+                		mDNSPlatformMemCopy(gServiceText+gServiceTextLen+1, argv[optind], gServiceText[gServiceTextLen]);
+                		gServiceTextLen += 1 + gServiceText[gServiceTextLen];
+                		optind++;
+                		}
+                	ch = -1;
+                	break;
+                case '?':
+                default:
+                    PrintUsage();
+                    exit(1);
+                    break;
+            }
+        }
+    } while (ch != -1);
+
+    // Check for any left over command line arguments.
+    
+    if (optind != argc) {
+	    PrintUsage();
+        fprintf(stderr, "%s: Unexpected argument '%s'\n", gProgramName, argv[optind]);
+        exit(1);
+    }
+    
+    // Check for inconsistency between the arguments.
+    
+    if ( (gServiceName[0] == 0) && (gServiceFile[0] == 0) ) {
+    	PrintUsage();
+        fprintf(stderr, "%s: You must specify a service name to register (-n) or a service file (-f).\n", gProgramName);
+        exit(1);
+    }
+}
+
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark ***** Registration
+#endif
+
+typedef struct PosixService PosixService;
+
+struct PosixService {
+    ServiceRecordSet coreServ;
+    PosixService *next;
+    int serviceID;
+};
+
+static PosixService *gServiceList = NULL;
+
+static void RegistrationCallback(mDNS *const m, ServiceRecordSet *const thisRegistration, mStatus status)
+    // mDNS core calls this routine to tell us about the status of 
+    // our registration.  The appropriate action to take depends 
+    // entirely on the value of status.
+{
+    switch (status) {
+
+        case mStatus_NoError:      
+            debugf("Callback: %##s Name Registered",   thisRegistration->RR_SRV.resrec.name->c); 
+            // Do nothing; our name was successfully registered.  We may 
+            // get more call backs in the future.
+            break;
+
+        case mStatus_NameConflict: 
+            debugf("Callback: %##s Name Conflict",     thisRegistration->RR_SRV.resrec.name->c); 
+
+            // In the event of a conflict, this sample RegistrationCallback 
+            // just calls mDNS_RenameAndReregisterService to automatically 
+            // pick a new unique name for the service. For a device such as a 
+            // printer, this may be appropriate.  For a device with a user 
+            // interface, and a screen, and a keyboard, the appropriate response 
+            // may be to prompt the user and ask them to choose a new name for 
+            // the service.
+            //
+            // Also, what do we do if mDNS_RenameAndReregisterService returns an 
+            // error.  Right now I have no place to send that error to.
+            
+            status = mDNS_RenameAndReregisterService(m, thisRegistration, mDNSNULL);
+            assert(status == mStatus_NoError);
+            break;
+
+        case mStatus_MemFree:      
+            debugf("Callback: %##s Memory Free",       thisRegistration->RR_SRV.resrec.name->c); 
+            
+            // When debugging is enabled, make sure that thisRegistration 
+            // is not on our gServiceList.
+            
+            #if !defined(NDEBUG)
+                {
+                    PosixService *cursor;
+                    
+                    cursor = gServiceList;
+                    while (cursor != NULL) {
+                        assert(&cursor->coreServ != thisRegistration);
+                        cursor = cursor->next;
+                    }
+                }
+            #endif
+            free(thisRegistration);
+            break;
+
+        default:                   
+            debugf("Callback: %##s Unknown Status %ld", thisRegistration->RR_SRV.resrec.name->c, status); 
+            break;
+    }
+}
+
+static int gServiceID = 0;
+
+static mStatus RegisterOneService(const char *  richTextName, 
+                                  const char *  serviceType, 
+                                  const char *  serviceDomain, 
+                                  const mDNSu8  text[],
+                                  mDNSu16       textLen,
+                                  long          portNumber)
+{
+    mStatus             status;
+    PosixService *      thisServ;
+    domainlabel         name;
+    domainname          type;
+    domainname          domain;
+    
+    status = mStatus_NoError;
+    thisServ = (PosixService *) malloc(sizeof(*thisServ));
+    if (thisServ == NULL) {
+        status = mStatus_NoMemoryErr;
+    }
+    if (status == mStatus_NoError) {
+        MakeDomainLabelFromLiteralString(&name,  richTextName);
+        MakeDomainNameFromDNSNameString(&type, serviceType);
+        MakeDomainNameFromDNSNameString(&domain, serviceDomain);
+        status = mDNS_RegisterService(&mDNSStorage, &thisServ->coreServ,
+                &name, &type, &domain,				// Name, type, domain
+                NULL, mDNSOpaque16fromIntVal(portNumber),
+                text, textLen,						// TXT data, length
+                NULL, 0,							// Subtypes
+                mDNSInterface_Any,					// Interface ID
+                RegistrationCallback, thisServ, 0);	// Callback, context, flags
+    }
+    if (status == mStatus_NoError) {
+        thisServ->serviceID = gServiceID;
+        gServiceID += 1;
+
+        thisServ->next = gServiceList;
+        gServiceList = thisServ;
+
+        if (gMDNSPlatformPosixVerboseLevel > 0) {
+            fprintf(stderr, 
+                    "%s: Registered service %d, name \"%s\", type \"%s\", domain \"%s\",  port %ld\n", 
+                    gProgramName, 
+                    thisServ->serviceID, 
+                    richTextName,
+                    serviceType,
+                    serviceDomain,
+                    portNumber);
+        }
+    } else {
+        if (thisServ != NULL) {
+            free(thisServ);
+        }
+    }
+    return status;
+}
+
+static mDNSBool ReadALine(char *buf, size_t bufSize, FILE *fp, mDNSBool skipBlankLines)
+{
+	size_t	len;
+	mDNSBool readNextLine;
+
+	do {
+		readNextLine = mDNSfalse;
+
+		if (fgets(buf, bufSize, fp) == NULL)
+			return mDNSfalse;	// encountered EOF or an error condition
+
+		// These first characters indicate a blank line.
+		if (buf[0] == ' ' || buf[0] == '\t' || buf[0] == '\r' || buf[0] == '\n') {
+			if (!skipBlankLines)
+				return mDNSfalse;
+			readNextLine = mDNStrue;
+		}
+		// always skip comment lines
+		if (buf[0] == '#')
+			readNextLine = mDNStrue;
+
+	} while (readNextLine);
+
+	len = strlen( buf);
+	if ( buf[len - 1] == '\r' || buf[len - 1] == '\n')
+		buf[len - 1] = '\0';
+
+    return mDNStrue;
+}
+
+static mStatus RegisterServicesInFile(const char *filePath)
+{
+    mStatus     status = mStatus_NoError;
+    FILE *      fp = fopen(filePath, "r");
+    
+    if (fp == NULL) {
+        return mStatus_UnknownErr;
+    }
+
+	if (gMDNSPlatformPosixVerboseLevel > 1)
+		fprintf(stderr, "Parsing %s for services\n", filePath);
+
+	do {
+		char nameBuf[256];
+		char * name = nameBuf; 
+		char type[256];
+		const char *dom = kDefaultServiceDomain;
+		char rawText[1024];
+		mDNSu8  text[sizeof(RDataBody)];
+		unsigned int textLen = 0;
+		char port[256];
+		char *p;
+
+		// Read the service name, type, port, and optional text record fields.
+		// Skip blank lines while looking for the next service name.
+		if (! ReadALine(name, sizeof(nameBuf), fp, mDNStrue))
+			break;
+
+		// Special case that allows service name to begin with a '#'
+		// character by escaping it with a '\' to distiguish it from
+		// a comment line.  Remove the leading '\' here before
+		// registering the service.
+		if (name[0] == '\\' && name[1] == '#')
+			name++;
+
+		if (gMDNSPlatformPosixVerboseLevel > 1)
+			fprintf(stderr, "Service name: \"%s\"\n", name);
+
+		// Don't skip blank lines in calls to ReadAline() after finding the
+		// service name since the next blank line indicates the end
+		// of this service record.
+		if (! ReadALine(type, sizeof(type), fp, mDNSfalse))
+			break;
+
+		// see if a domain name is specified
+		p = type;
+		while (*p && *p != ' ' && *p != '\t') p++;
+		if (*p) {
+			*p = 0;	// NULL terminate the <type>.<protocol> string
+			// skip any leading whitespace before domain name
+			p++;
+			while (*p && (*p == ' ' || *p == '\t')) p++;
+			if (*p)
+				dom = p;
+		}
+		if (gMDNSPlatformPosixVerboseLevel > 1) {
+			fprintf(stderr, "Service type: \"%s\"\n", type);
+			fprintf(stderr, "Service domain: \"%s\"\n", dom);
+		}
+
+		if (! ReadALine(port, sizeof(port), fp, mDNSfalse))
+			break;
+		if (gMDNSPlatformPosixVerboseLevel > 1)
+			fprintf(stderr, "Service port: %s\n", port);
+
+		if (   ! CheckThatRichTextNameIsUsable(name, mDNStrue)
+			|| ! CheckThatServiceTypeIsUsable(type, mDNStrue)
+			|| ! CheckThatPortNumberIsUsable(atol(port), mDNStrue))
+			break;
+
+		// read the TXT record fields
+		while (1) {
+			int len;
+			if (!ReadALine(rawText, sizeof(rawText), fp, mDNSfalse)) break;
+			if (gMDNSPlatformPosixVerboseLevel > 1)
+				fprintf(stderr, "Text string: \"%s\"\n", rawText);
+			len = strlen(rawText);
+			if (len <= 255)
+				{
+				unsigned int newlen = textLen + 1 + len;
+				if (len == 0 || newlen >= sizeof(text)) break;
+				text[textLen] = len;
+				mDNSPlatformMemCopy(text + textLen + 1, rawText, len);
+				textLen = newlen;
+				}
+			else
+				fprintf(stderr, "%s: TXT attribute too long for name = %s, type = %s, port = %s\n", 
+					gProgramName, name, type, port);
+		}
+
+		status = RegisterOneService(name, type, dom, text, textLen, atol(port));
+		if (status != mStatus_NoError) {
+			// print error, but try to read and register other services in the file
+			fprintf(stderr, "%s: Failed to register service, name \"%s\", type \"%s\", domain \"%s\", port %s\n", 
+					gProgramName, name, type, dom, port);
+		}
+
+	} while (!feof(fp));
+
+	if (!feof(fp)) {
+		fprintf(stderr, "%s: Error reading service file %s\n", gProgramName, filePath);
+		status = mStatus_UnknownErr;
+	}
+    
+	{
+// __ANDROID__ : replaced assert(fclose(..))
+		int fp_closed = fclose(fp);
+		assert(0 == fp_closed);
+	}
+    
+	return status;
+}
+
+static mStatus RegisterOurServices(void)
+{
+    mStatus status;
+    
+    status = mStatus_NoError;
+    if (gServiceName[0] != 0) {
+        status = RegisterOneService(gServiceName, 
+                                    gServiceType, 
+                                    gServiceDomain, 
+                                    gServiceText, gServiceTextLen, 
+                                    gPortNumber);
+    }
+    if (status == mStatus_NoError && gServiceFile[0] != 0) {
+        status = RegisterServicesInFile(gServiceFile);
+    }
+    return status;
+}
+
+static void DeregisterOurServices(void)
+{
+    PosixService *thisServ;
+    int thisServID;
+    
+    while (gServiceList != NULL) {
+        thisServ = gServiceList;
+        gServiceList = thisServ->next;
+
+        thisServID = thisServ->serviceID;
+        
+        mDNS_DeregisterService(&mDNSStorage, &thisServ->coreServ);
+
+        if (gMDNSPlatformPosixVerboseLevel > 0) {
+            fprintf(stderr, 
+                    "%s: Deregistered service %d\n",
+                    gProgramName, 
+                    thisServ->serviceID);
+        }
+    }
+}
+
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark **** Main
+#endif
+
+int main(int argc, char **argv)
+{
+    mStatus status;
+    int     result;
+
+    // Parse our command line arguments.  This won't come back if there's an error.
+    
+    ParseArguments(argc, argv);
+
+    // If we're told to run as a daemon, then do that straight away.
+    // Note that we don't treat the inability to create our PID 
+    // file as an error.  Also note that we assign getpid to a long 
+    // because printf has no format specified for pid_t.
+    
+    if (gDaemon) {
+    	int result;
+        if (gMDNSPlatformPosixVerboseLevel > 0) {
+            fprintf(stderr, "%s: Starting in daemon mode\n", gProgramName);
+        }
+        result = daemon(0,0);
+        if (result == 0) {
+            FILE *fp;
+            int  junk;
+            
+            fp = fopen(gPIDFile, "w");
+            if (fp != NULL) {
+                fprintf(fp, "%ld\n", (long) getpid());
+                junk = fclose(fp);
+                assert(junk == 0);
+            }
+        } else {
+            fprintf(stderr, "%s: Could not run as daemon - exiting\n", gProgramName);
+            exit(result);
+        }
+    } else {
+        if (gMDNSPlatformPosixVerboseLevel > 0) {
+            fprintf(stderr, "%s: Starting in foreground mode, PID %ld\n", gProgramName, (long) getpid());
+        }
+    }
+
+    status = mDNS_Init(&mDNSStorage, &PlatformStorage,
+    	mDNS_Init_NoCache, mDNS_Init_ZeroCacheSize,
+    	mDNS_Init_AdvertiseLocalAddresses,
+    	mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext);
+    if (status != mStatus_NoError) return(2);
+
+	status = RegisterOurServices();
+    if (status != mStatus_NoError) return(2);
+    
+    signal(SIGHUP,  HandleSigHup);      // SIGHUP has to be sent by kill -HUP <pid>
+    signal(SIGINT,  HandleSigInt);      // SIGINT is what you get for a Ctrl-C
+    signal(SIGQUIT, HandleSigQuit);     // SIGQUIT is what you get for a Ctrl-\ (indeed)
+    signal(SIGUSR1, HandleSigUsr1);     // SIGUSR1 has to be sent by kill -USR1 <pid>
+
+	while (!gStopNow)
+		{
+		int nfds = 0;
+		fd_set readfds;
+		struct timeval timeout;
+		int result;
+		
+		// 1. Set up the fd_set as usual here.
+		// This example client has no file descriptors of its own,
+		// but a real application would call FD_SET to add them to the set here
+		FD_ZERO(&readfds);
+		
+		// 2. Set up the timeout.
+		// This example client has no other work it needs to be doing,
+		// so we set an effectively infinite timeout
+		timeout.tv_sec = 0x3FFFFFFF;
+		timeout.tv_usec = 0;
+		
+		// 3. Give the mDNSPosix layer a chance to add its information to the fd_set and timeout
+		mDNSPosixGetFDSet(&mDNSStorage, &nfds, &readfds, &timeout);
+		
+		// 4. Call select as normal
+		verbosedebugf("select(%d, %d.%06d)", nfds, timeout.tv_sec, timeout.tv_usec);
+		result = select(nfds, &readfds, NULL, NULL, &timeout);
+		
+		if (result < 0)
+			{
+			verbosedebugf("select() returned %d errno %d", result, errno);
+			if (errno != EINTR) gStopNow = mDNStrue;
+			else
+				{
+				if (gReceivedSigUsr1)
+					{
+					gReceivedSigUsr1 = mDNSfalse;
+					gMDNSPlatformPosixVerboseLevel += 1;
+					if (gMDNSPlatformPosixVerboseLevel > 2)
+						gMDNSPlatformPosixVerboseLevel = 0;
+					if ( gMDNSPlatformPosixVerboseLevel > 0 )
+						fprintf(stderr, "\nVerbose level %d\n", gMDNSPlatformPosixVerboseLevel);
+					}
+				if (gReceivedSigHup)
+					{
+					if (gMDNSPlatformPosixVerboseLevel > 0)
+						fprintf(stderr, "\nSIGHUP\n");
+					gReceivedSigHup = mDNSfalse;
+					DeregisterOurServices();
+					status = mDNSPlatformPosixRefreshInterfaceList(&mDNSStorage);
+					if (status != mStatus_NoError) break;
+					status = RegisterOurServices();
+					if (status != mStatus_NoError) break;
+					}
+				}
+			}
+		else
+			{
+			// 5. Call mDNSPosixProcessFDSet to let the mDNSPosix layer do its work
+			mDNSPosixProcessFDSet(&mDNSStorage, &readfds);
+			
+			// 6. This example client has no other work it needs to be doing,
+			// but a real client would do its work here
+			// ... (do work) ...
+			}
+		}
+
+	debugf("Exiting");
+    
+	DeregisterOurServices();
+	mDNS_Close(&mDNSStorage);
+
+    if (status == mStatus_NoError) {
+        result = 0;
+    } else {
+        result = 2;
+    }
+    if ( (result != 0) || (gMDNSPlatformPosixVerboseLevel > 0) ) {
+        fprintf(stderr, "%s: Finished with status %d, result %d\n", gProgramName, (int)status, result);
+    }
+    
+    return result;
+}
diff --git a/mdnsresponder/mDNSPosix/Services.txt b/mdnsresponder/mDNSPosix/Services.txt
new file mode 100755
index 0000000..f8d6978
--- /dev/null
+++ b/mdnsresponder/mDNSPosix/Services.txt
@@ -0,0 +1,36 @@
+#
+# Example services file parsed by mDNSResponderPosix.
+# 
+# Lines beginning with '#' are comments/ignored.
+# Blank lines indicate the end of a service record specification.
+# The first character of the service name can be a '#' if you escape it with 
+# backslash to distinguish if from a comment line.
+# ie, "\#serviceName" will be registered as "#serviceName".
+# Note that any line beginning with white space is considered a blank line.
+#
+# The record format is:
+# 
+# <service name>
+# <type>.<protocol> <optional domain>
+# <port number>
+# <zero or more strings for the text record, one string per line>
+#
+# <One or more blank lines between records>
+# 
+# Examples shown below.
+
+serviceName1
+_afpovertcp._tcp.
+548
+name=val1
+
+serviceName2
+_afpovertcp._tcp. local.
+548
+name=val2
+name2=anotherattribute
+
+serviceName3
+_afpovertcp._tcp.
+548
+name=val3
diff --git a/mdnsresponder/mDNSPosix/build/prod/libdns_sd.so b/mdnsresponder/mDNSPosix/build/prod/libdns_sd.so
new file mode 100755
index 0000000..a59cf9a
--- /dev/null
+++ b/mdnsresponder/mDNSPosix/build/prod/libdns_sd.so
Binary files differ
diff --git a/mdnsresponder/mDNSPosix/build/prod/mDNSClientPosix b/mdnsresponder/mDNSPosix/build/prod/mDNSClientPosix
new file mode 100755
index 0000000..9a407f3
--- /dev/null
+++ b/mdnsresponder/mDNSPosix/build/prod/mDNSClientPosix
Binary files differ
diff --git a/mdnsresponder/mDNSPosix/build/prod/mDNSIdentify b/mdnsresponder/mDNSPosix/build/prod/mDNSIdentify
new file mode 100755
index 0000000..3025874
--- /dev/null
+++ b/mdnsresponder/mDNSPosix/build/prod/mDNSIdentify
Binary files differ
diff --git a/mdnsresponder/mDNSPosix/build/prod/mDNSNetMonitor b/mdnsresponder/mDNSPosix/build/prod/mDNSNetMonitor
new file mode 100755
index 0000000..d430f98
--- /dev/null
+++ b/mdnsresponder/mDNSPosix/build/prod/mDNSNetMonitor
Binary files differ
diff --git a/mdnsresponder/mDNSPosix/build/prod/mDNSProxyResponderPosix b/mdnsresponder/mDNSPosix/build/prod/mDNSProxyResponderPosix
new file mode 100755
index 0000000..bf3fbbf
--- /dev/null
+++ b/mdnsresponder/mDNSPosix/build/prod/mDNSProxyResponderPosix
Binary files differ
diff --git a/mdnsresponder/mDNSPosix/build/prod/mDNSResponderPosix b/mdnsresponder/mDNSPosix/build/prod/mDNSResponderPosix
new file mode 100755
index 0000000..c4107a0
--- /dev/null
+++ b/mdnsresponder/mDNSPosix/build/prod/mDNSResponderPosix
Binary files differ
diff --git a/mdnsresponder/mDNSPosix/build/prod/mdnsd b/mdnsresponder/mDNSPosix/build/prod/mdnsd
new file mode 100755
index 0000000..e80fd97
--- /dev/null
+++ b/mdnsresponder/mDNSPosix/build/prod/mdnsd
Binary files differ
diff --git a/mdnsresponder/mDNSPosix/libnss_mdns.8 b/mdnsresponder/mDNSPosix/libnss_mdns.8
new file mode 100755
index 0000000..9c00d4f
--- /dev/null
+++ b/mdnsresponder/mDNSPosix/libnss_mdns.8
@@ -0,0 +1,148 @@
+.\"
+.\" See section LICENSE for license information.
+.\"
+.Dd June 15, 2004
+.Dt LIBNSS_MDNS 8
+.Os
+.Sh NAME
+.Nm libnss_mdns
+.Nd name service switch module to support Apple mdnsd
+.Sh DESCRIPTION
+The
+.Nm
+shared library implements a name service switch module to interface with Apple
+mdnsd.  It is enabled by adding
+.Ql mdns
+to the
+.Ql hosts:
+line of
+.Xr nsswitch.conf 5 .
+This will cause calls to
+.Xr gethostbyname 3 ,
+.Xr gethostbyname2 3
+and
+.Xr gethostbyaddr 3
+to include mdnsd in their lookup path.
+.Pp
+The
+.Nm
+shared library should be installed in the system library paths, typically in
+.Pa /lib .
+.Sh FILES
+.Bl -tag -width Pa -compact
+.It Pa /etc/nss_mdns.conf
+configuration options
+.El
+.Sh EXAMPLES
+In
+.Pa /etc/nsswitch.conf :
+.Dl hosts: files mdns dns
+.Pp
+This will cause the name service switch to lookup hostnames first using
+.Pa /etc/hosts ,
+then mdns and finally via DNS.
+.Sh DIAGNOSTICS
+.Nm
+dumps status information via
+.Xr syslog 3 .
+.Sh SEE ALSO
+.\" Cross-references should be ordered by section (low to high), then in
+.\"     alphabetical order.
+.Xr nsswitch.conf 5 ,
+.Xr nss_mdns.conf 5 ,
+.Xr ldconfig 8
+.Pp
+.Li info libc Qq Name Service Switch
+.\" .Sh STANDARDS
+.Sh HISTORY
+.Nm
+was originally written for
+.An NICTA Bq http://www.nicta.com.au/ .
+.Sh AUTHORS
+.An "Andrew White" Bq Andrew.White@nicta.com.au
+.Sh BUGS
+Return values for obscure error conditions may not match those expected by nsswitch code.
+.Pp
+Version 0.62 of mdnsd does not support an option to force link-local multicast lookups.
+.Ql PTR
+reverse lookups for non-link-local domains will not function correctly.
+.Sh LICENSE
+This software is licensed under the NICTA Public Source License version 1.0
+.Ss NICTA Public Software Licence
+Version 1.0
+.Pp
+Copyright 2004 National ICT Australia Ltd
+.Pp
+All rights reserved.
+.Pp
+By this licence, National ICT Australia Ltd (NICTA) grants permission,
+free of charge, to any person who obtains a copy of this software
+and any associated documentation files ("the Software") to use and
+deal with the Software in source code and binary forms without
+restriction, with or without modification, and to permit persons
+to whom the Software is furnished to do so, provided that the
+following conditions are met:
+.Bl -bullet
+.It
+Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimers.
+.It
+Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimers in
+the documentation and/or other materials provided with the
+distribution.
+.It
+The name of NICTA may not be used to endorse or promote products
+derived from this Software without specific prior written permission.
+.El
+.Pp
+EXCEPT AS EXPRESSLY STATED IN THIS LICENCE AND TO THE FULL EXTENT
+PERMITTED BY APPLICABLE LAW, THE SOFTWARE IS PROVIDED "AS-IS" AND
+NICTA MAKES NO REPRESENTATIONS, WARRANTIES OR CONDITIONS OF ANY
+KIND, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, ANY
+REPRESENTATIONS, WARRANTIES OR CONDITIONS REGARDING THE CONTENTS
+OR ACCURACY OF THE SOFTWARE, OR OF TITLE, MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, THE ABSENCE OF LATENT
+OR OTHER DEFECTS, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR
+NOT DISCOVERABLE.
+.Pp
+TO THE FULL EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT WILL
+NICTA BE LIABLE ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
+NEGLIGENCE) FOR ANY LOSS OR DAMAGE WHATSOEVER, INCLUDING (WITHOUT
+LIMITATION) LOSS OF PRODUCTION OR OPERATION TIME, LOSS, DAMAGE OR
+CORRUPTION OF DATA OR RECORDS; OR LOSS OF ANTICIPATED SAVINGS,
+OPPORTUNITY, REVENUE, PROFIT OR GOODWILL, OR OTHER ECONOMIC LOSS;
+OR ANY SPECIAL, INCIDENTAL, INDIRECT, CONSEQUENTIAL, PUNITIVE OR
+EXEMPLARY DAMAGES ARISING OUT OF OR IN CONNECTION WITH THIS LICENCE,
+THE SOFTWARE OR THE USE OF THE SOFTWARE, EVEN IF NICTA HAS BEEN
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+.Pp
+If applicable legislation implies warranties or conditions, or
+imposes obligations or liability on NICTA in respect of the Software
+that cannot be wholly or partly excluded, restricted or modified,
+NICTA's liability is limited, to the full extent permitted by the
+applicable legislation, at its option, to:
+.Pp
+.Bl -tag -width "a." -compact
+.It a.
+in the case of goods, any one or more of the following:
+.Bl -tag -width "iii." -compact
+.It i.
+the replacement of the goods or the supply of equivalent goods;
+.It ii.
+the repair of the goods;
+.It iii.
+the payment of the cost of replacing the goods or of acquiring
+equivalent goods;
+.It iv.
+the payment of the cost of having the goods repaired; or
+.El
+.It b.
+in the case of services:
+.Bl -tag -width "iii." -compact
+.It i.
+the supplying of the services again; or 
+.It ii.
+the payment of the cost of having the services supplied again.
+.El
+.El
diff --git a/mdnsresponder/mDNSPosix/mDNSPosix.c b/mdnsresponder/mDNSPosix/mDNSPosix.c
new file mode 100644
index 0000000..5e47657
--- /dev/null
+++ b/mdnsresponder/mDNSPosix/mDNSPosix.c
@@ -0,0 +1,1681 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ *
+ * Formatting notes:
+ * This code follows the "Whitesmiths style" C indentation rules. Plenty of discussion
+ * on C indentation can be found on the web, such as <http://www.kafejo.com/komp/1tbs.htm>,
+ * but for the sake of brevity here I will say just this: Curly braces are not syntactially
+ * part of an "if" statement; they are the beginning and ending markers of a compound statement;
+ * therefore common sense dictates that if they are part of a compound statement then they
+ * should be indented to the same level as everything else in that compound statement.
+ * Indenting curly braces at the same level as the "if" implies that curly braces are
+ * part of the "if", which is false. (This is as misleading as people who write "char* x,y;"
+ * thinking that variables x and y are both of type "char*" -- and anyone who doesn't
+ * understand why variable y is not of type "char*" just proves the point that poor code
+ * layout leads people to unfortunate misunderstandings about how the C language really works.)
+ */
+
+#include "mDNSEmbeddedAPI.h"           // Defines the interface provided to the client layer above
+#include "DNSCommon.h"
+#include "mDNSPosix.h"				 // Defines the specific types needed to run mDNS on this platform
+#include "dns_sd.h"
+
+#include <assert.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <errno.h>
+#include <string.h>
+#include <unistd.h>
+#ifndef __ANDROID__
+  #include <syslog.h>
+#endif
+#include <stdarg.h>
+#include <fcntl.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+#include <sys/uio.h>
+#include <sys/select.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <time.h>                   // platform support for UTC time
+
+#if USES_NETLINK
+#include <asm/types.h>
+#include <linux/netlink.h>
+#include <linux/rtnetlink.h>
+#else // USES_NETLINK
+#include <net/route.h>
+#include <net/if.h>
+#endif // USES_NETLINK
+
+#include "mDNSUNP.h"
+#include "GenLinkedList.h"
+
+// Disallow SO_REUSEPORT on Android because we use >3.9 kernel headers to build binaries targeted to 3.4.x.
+#ifdef __ANDROID__
+#undef SO_REUSEPORT
+#endif
+
+// __ANDROID__ : replaced assert(close(..)) at several points in this file.
+
+// ***************************************************************************
+// Structures
+
+// We keep a list of client-supplied event sources in PosixEventSource records 
+struct PosixEventSource
+	{
+	mDNSPosixEventCallback		Callback;
+	void						*Context;
+	int							fd;
+	struct  PosixEventSource	*Next;
+	};
+typedef struct PosixEventSource	PosixEventSource;
+
+// Context record for interface change callback
+struct IfChangeRec
+	{
+	int	NotifySD;
+	mDNS *mDNS;
+	};
+typedef struct IfChangeRec	IfChangeRec;
+
+// Note that static data is initialized to zero in (modern) C.
+static fd_set			gEventFDs;
+static int				gMaxFD;					// largest fd in gEventFDs
+static GenLinkedList	gEventSources;			// linked list of PosixEventSource's
+static sigset_t			gEventSignalSet;		// Signals which event loop listens for
+static sigset_t			gEventSignals;			// Signals which were received while inside loop
+
+// ***************************************************************************
+// Globals (for debugging)
+
+static int num_registered_interfaces = 0;
+static int num_pkts_accepted = 0;
+static int num_pkts_rejected = 0;
+
+// ***************************************************************************
+// Functions
+
+int gMDNSPlatformPosixVerboseLevel = 0;
+
+#define PosixErrorToStatus(errNum) ((errNum) == 0 ? mStatus_NoError : mStatus_UnknownErr)
+
+mDNSlocal void SockAddrTomDNSAddr(const struct sockaddr *const sa, mDNSAddr *ipAddr, mDNSIPPort *ipPort)
+	{
+	switch (sa->sa_family)
+		{
+		case AF_INET:
+			{
+			struct sockaddr_in *sin          = (struct sockaddr_in*)sa;
+			ipAddr->type                     = mDNSAddrType_IPv4;
+			ipAddr->ip.v4.NotAnInteger       = sin->sin_addr.s_addr;
+			if (ipPort) ipPort->NotAnInteger = sin->sin_port;
+			break;
+			}
+
+#if HAVE_IPV6
+		case AF_INET6:
+			{
+			struct sockaddr_in6 *sin6        = (struct sockaddr_in6*)sa;
+#ifndef NOT_HAVE_SA_LEN
+			assert(sin6->sin6_len == sizeof(*sin6));
+#endif
+			ipAddr->type                     = mDNSAddrType_IPv6;
+			ipAddr->ip.v6                    = *(mDNSv6Addr*)&sin6->sin6_addr;
+			if (ipPort) ipPort->NotAnInteger = sin6->sin6_port;
+			break;
+			}
+#endif
+
+		default:
+			verbosedebugf("SockAddrTomDNSAddr: Uknown address family %d\n", sa->sa_family);
+			ipAddr->type = mDNSAddrType_None;
+			if (ipPort) ipPort->NotAnInteger = 0;
+			break;
+		}
+	}
+
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark ***** Send and Receive
+#endif
+
+// mDNS core calls this routine when it needs to send a packet.
+mDNSexport mStatus mDNSPlatformSendUDP(const mDNS *const m, const void *const msg, const mDNSu8 *const end,
+	mDNSInterfaceID InterfaceID, UDPSocket *src, const mDNSAddr *dst, mDNSIPPort dstPort)
+	{
+	int                     err = 0;
+	struct sockaddr_storage to;
+	PosixNetworkInterface * thisIntf = (PosixNetworkInterface *)(InterfaceID);
+	int sendingsocket = -1;
+
+	(void)src;	// Will need to use this parameter once we implement mDNSPlatformUDPSocket/mDNSPlatformUDPClose
+
+	assert(m != NULL);
+	assert(msg != NULL);
+	assert(end != NULL);
+	assert((((char *) end) - ((char *) msg)) > 0);
+
+	if (dstPort.NotAnInteger == 0) 
+		{
+		LogMsg("mDNSPlatformSendUDP: Invalid argument -dstPort is set to 0");
+		return PosixErrorToStatus(EINVAL);
+		}
+	if (dst->type == mDNSAddrType_IPv4)
+		{
+		struct sockaddr_in *sin = (struct sockaddr_in*)&to;
+#ifndef NOT_HAVE_SA_LEN
+		sin->sin_len            = sizeof(*sin);
+#endif
+		sin->sin_family         = AF_INET;
+		sin->sin_port           = dstPort.NotAnInteger;
+		sin->sin_addr.s_addr    = dst->ip.v4.NotAnInteger;
+		sendingsocket           = thisIntf ? thisIntf->multicastSocket4 : m->p->unicastSocket4;
+		}
+
+#if HAVE_IPV6
+	else if (dst->type == mDNSAddrType_IPv6)
+		{
+		struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&to;
+		mDNSPlatformMemZero(sin6, sizeof(*sin6));
+#ifndef NOT_HAVE_SA_LEN
+		sin6->sin6_len            = sizeof(*sin6);
+#endif
+		sin6->sin6_family         = AF_INET6;
+		sin6->sin6_port           = dstPort.NotAnInteger;
+		sin6->sin6_addr           = *(struct in6_addr*)&dst->ip.v6;
+		sendingsocket             = thisIntf ? thisIntf->multicastSocket6 : m->p->unicastSocket6;
+		}
+#endif
+
+	if (sendingsocket >= 0)
+		err = sendto(sendingsocket, msg, (char*)end - (char*)msg, 0, (struct sockaddr *)&to, GET_SA_LEN(to));
+
+	if      (err > 0) err = 0;
+	else if (err < 0)
+		{
+		static int MessageCount = 0;
+        // Don't report EHOSTDOWN (i.e. ARP failure), ENETDOWN, or no route to host for unicast destinations
+		if (!mDNSAddressIsAllDNSLinkGroup(dst))
+			if (errno == EHOSTDOWN || errno == ENETDOWN || errno == EHOSTUNREACH || errno == ENETUNREACH) return(mStatus_TransientErr);
+
+		if (MessageCount < 1000)
+			{
+			MessageCount++;
+			if (thisIntf)
+				LogMsg("mDNSPlatformSendUDP got error %d (%s) sending packet to %#a on interface %#a/%s/%d",
+							  errno, strerror(errno), dst, &thisIntf->coreIntf.ip, thisIntf->intfName, thisIntf->index);
+			else
+				LogMsg("mDNSPlatformSendUDP got error %d (%s) sending packet to %#a", errno, strerror(errno), dst);
+			}
+		}
+
+	return PosixErrorToStatus(err);
+	}
+
+// This routine is called when the main loop detects that data is available on a socket.
+mDNSlocal void SocketDataReady(mDNS *const m, PosixNetworkInterface *intf, int skt)
+	{
+	mDNSAddr   senderAddr, destAddr;
+	mDNSIPPort senderPort;
+	ssize_t                 packetLen;
+	DNSMessage              packet;
+	struct my_in_pktinfo    packetInfo;
+	struct sockaddr_storage from;
+	socklen_t               fromLen;
+	int                     flags;
+	mDNSu8					ttl;
+	mDNSBool                reject;
+	const mDNSInterfaceID InterfaceID = intf ? intf->coreIntf.InterfaceID : NULL;
+
+	assert(m    != NULL);
+	assert(skt  >= 0);
+
+	fromLen = sizeof(from);
+	flags   = 0;
+	packetLen = recvfrom_flags(skt, &packet, sizeof(packet), &flags, (struct sockaddr *) &from, &fromLen, &packetInfo, &ttl);
+
+	if (packetLen >= 0)
+		{
+		SockAddrTomDNSAddr((struct sockaddr*)&from, &senderAddr, &senderPort);
+		SockAddrTomDNSAddr((struct sockaddr*)&packetInfo.ipi_addr, &destAddr, NULL);
+
+		// If we have broken IP_RECVDSTADDR functionality (so far
+		// I've only seen this on OpenBSD) then apply a hack to
+		// convince mDNS Core that this isn't a spoof packet.
+		// Basically what we do is check to see whether the
+		// packet arrived as a multicast and, if so, set its
+		// destAddr to the mDNS address.
+		//
+		// I must admit that I could just be doing something
+		// wrong on OpenBSD and hence triggering this problem
+		// but I'm at a loss as to how.
+		//
+		// If this platform doesn't have IP_PKTINFO or IP_RECVDSTADDR, then we have
+		// no way to tell the destination address or interface this packet arrived on,
+		// so all we can do is just assume it's a multicast
+
+		#if HAVE_BROKEN_RECVDSTADDR || (!defined(IP_PKTINFO) && !defined(IP_RECVDSTADDR))
+			if ((destAddr.NotAnInteger == 0) && (flags & MSG_MCAST))
+				{
+				destAddr.type = senderAddr.type;
+				if      (senderAddr.type == mDNSAddrType_IPv4) destAddr.ip.v4 = AllDNSLinkGroup_v4.ip.v4;
+				else if (senderAddr.type == mDNSAddrType_IPv6) destAddr.ip.v6 = AllDNSLinkGroup_v6.ip.v6;
+				}
+		#endif
+
+		// We only accept the packet if the interface on which it came
+		// in matches the interface associated with this socket.
+		// We do this match by name or by index, depending on which
+		// information is available.  recvfrom_flags sets the name
+		// to "" if the name isn't available, or the index to -1
+		// if the index is available.  This accomodates the various
+		// different capabilities of our target platforms.
+
+		reject = mDNSfalse;
+		if (!intf)
+			{
+			// Ignore multicasts accidentally delivered to our unicast receiving socket
+			if (mDNSAddrIsDNSMulticast(&destAddr)) packetLen = -1;
+			}
+		else
+			{
+			if      (packetInfo.ipi_ifname[0] != 0) reject = (strcmp(packetInfo.ipi_ifname, intf->intfName) != 0);
+			else if (packetInfo.ipi_ifindex != -1)  reject = (packetInfo.ipi_ifindex != intf->index);
+	
+			if (reject)
+				{
+				verbosedebugf("SocketDataReady ignored a packet from %#a to %#a on interface %s/%d expecting %#a/%s/%d/%d",
+					&senderAddr, &destAddr, packetInfo.ipi_ifname, packetInfo.ipi_ifindex,
+					&intf->coreIntf.ip, intf->intfName, intf->index, skt);
+				packetLen = -1;
+				num_pkts_rejected++;
+				if (num_pkts_rejected > (num_pkts_accepted + 1) * (num_registered_interfaces + 1) * 2)
+					{
+					fprintf(stderr,
+						"*** WARNING: Received %d packets; Accepted %d packets; Rejected %d packets because of interface mismatch\n",
+						num_pkts_accepted + num_pkts_rejected, num_pkts_accepted, num_pkts_rejected);
+					num_pkts_accepted = 0;
+					num_pkts_rejected = 0;
+					}
+				}
+			else
+				{
+				verbosedebugf("SocketDataReady got a packet from %#a to %#a on interface %#a/%s/%d/%d",
+					&senderAddr, &destAddr, &intf->coreIntf.ip, intf->intfName, intf->index, skt);
+				num_pkts_accepted++;
+				}
+			}
+		}
+
+	if (packetLen >= 0)
+		mDNSCoreReceive(m, &packet, (mDNSu8 *)&packet + packetLen,
+			&senderAddr, senderPort, &destAddr, MulticastDNSPort, InterfaceID);
+	}
+
+mDNSexport TCPSocket *mDNSPlatformTCPSocket(mDNS * const m, TCPSocketFlags flags, mDNSIPPort * port)
+	{
+	(void)m;			// Unused
+	(void)flags;		// Unused
+	(void)port;			// Unused
+	return NULL;
+	}
+
+mDNSexport TCPSocket *mDNSPlatformTCPAccept(TCPSocketFlags flags, int sd)
+	{
+	(void)flags;		// Unused
+	(void)sd;			// Unused
+	return NULL;
+	}
+
+mDNSexport int mDNSPlatformTCPGetFD(TCPSocket *sock)
+	{
+	(void)sock;			// Unused
+	return -1;
+	}
+
+mDNSexport mStatus mDNSPlatformTCPConnect(TCPSocket *sock, const mDNSAddr *dst, mDNSOpaque16 dstport, domainname *hostname, mDNSInterfaceID InterfaceID,
+										  TCPConnectionCallback callback, void *context)
+	{
+	(void)sock;			// Unused
+	(void)dst;			// Unused
+	(void)dstport;		// Unused
+	(void)hostname;     // Unused
+	(void)InterfaceID;	// Unused
+	(void)callback;		// Unused
+	(void)context;		// Unused
+	return(mStatus_UnsupportedErr);
+	}
+
+mDNSexport void mDNSPlatformTCPCloseConnection(TCPSocket *sock)
+	{
+	(void)sock;			// Unused
+	}
+
+mDNSexport long mDNSPlatformReadTCP(TCPSocket *sock, void *buf, unsigned long buflen, mDNSBool * closed)
+	{
+	(void)sock;			// Unused
+	(void)buf;			// Unused
+	(void)buflen;		// Unused
+	(void)closed;		// Unused
+	return 0;			
+	}
+
+mDNSexport long mDNSPlatformWriteTCP(TCPSocket *sock, const char *msg, unsigned long len)
+	{
+	(void)sock;			// Unused
+	(void)msg;			// Unused
+	(void)len;			// Unused
+	return 0;
+	}
+
+mDNSexport UDPSocket *mDNSPlatformUDPSocket(mDNS * const m, mDNSIPPort port)
+	{
+	(void)m;			// Unused
+	(void)port;			// Unused
+	return NULL;
+	}
+
+mDNSexport void           mDNSPlatformUDPClose(UDPSocket *sock)
+	{
+	(void)sock;			// Unused
+	}
+	
+mDNSexport void mDNSPlatformUpdateProxyList(mDNS *const m, const mDNSInterfaceID InterfaceID)
+	{
+	(void)m;			// Unused
+	(void)InterfaceID;			// Unused
+	}
+
+mDNSexport void mDNSPlatformSendRawPacket(const void *const msg, const mDNSu8 *const end, mDNSInterfaceID InterfaceID)
+	{
+	(void)msg;			// Unused
+	(void)end;			// Unused
+	(void)InterfaceID;			// Unused
+	}
+	
+mDNSexport void mDNSPlatformSetLocalAddressCacheEntry(mDNS *const m, const mDNSAddr *const tpa, const mDNSEthAddr *const tha, mDNSInterfaceID InterfaceID)
+	{
+	(void)m;			// Unused
+	(void)tpa;			// Unused
+	(void)tha;			// Unused
+	(void)InterfaceID;			// Unused
+	}	
+
+mDNSexport mStatus mDNSPlatformTLSSetupCerts(void)
+	{
+	return(mStatus_UnsupportedErr);
+	}
+	
+mDNSexport void mDNSPlatformTLSTearDownCerts(void)
+	{
+	}
+
+mDNSexport void mDNSPlatformSetAllowSleep(mDNS *const m, mDNSBool allowSleep, const char *reason)
+	{
+	(void) m;
+	(void) allowSleep;
+	(void) reason;
+	}
+
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - /etc/hosts support
+#endif
+
+mDNSexport void FreeEtcHosts(mDNS *const m, AuthRecord *const rr, mStatus result)
+    {
+    (void)m;  // unused
+	(void)rr;
+	(void)result;
+	}
+
+
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark ***** DDNS Config Platform Functions
+#endif
+
+mDNSexport void mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, mDNSBool setsearch, domainname *const fqdn, DNameListElem **RegDomains, DNameListElem **BrowseDomains)
+	{
+	(void) m;
+	(void) setservers;
+	(void) fqdn;
+	(void) setsearch;
+	(void) RegDomains;
+	(void) BrowseDomains;
+	}
+
+mDNSexport mStatus mDNSPlatformGetPrimaryInterface(mDNS * const m, mDNSAddr * v4, mDNSAddr * v6, mDNSAddr * router)
+	{
+	(void) m;
+	(void) v4;
+	(void) v6;
+	(void) router;
+
+	return mStatus_UnsupportedErr;
+	}
+
+mDNSexport void mDNSPlatformDynDNSHostNameStatusChanged(const domainname *const dname, const mStatus status)
+	{
+	(void) dname;
+	(void) status;
+	}
+
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark ***** Init and Term
+#endif
+
+// This gets the current hostname, truncating it at the first dot if necessary
+mDNSlocal void GetUserSpecifiedRFC1034ComputerName(domainlabel *const namelabel)
+	{
+	int len = 0;
+#ifndef __ANDROID__
+	gethostname((char *)(&namelabel->c[1]), MAX_DOMAIN_LABEL);
+#else
+	// use an appropriate default label rather than the linux default of 'localhost'
+	strncpy(&namelabel->c[1], "Android", MAX_DOMAIN_LABEL);
+#endif
+	while (len < MAX_DOMAIN_LABEL && namelabel->c[len+1] && namelabel->c[len+1] != '.') len++;
+	namelabel->c[0] = len;
+	}
+
+// On OS X this gets the text of the field labelled "Computer Name" in the Sharing Prefs Control Panel
+// Other platforms can either get the information from the appropriate place,
+// or they can alternatively just require all registering services to provide an explicit name
+mDNSlocal void GetUserSpecifiedFriendlyComputerName(domainlabel *const namelabel)
+	{
+	// On Unix we have no better name than the host name, so we just use that.
+	GetUserSpecifiedRFC1034ComputerName(namelabel);
+	}
+
+mDNSexport int ParseDNSServers(mDNS *m, const char *filePath)
+	{
+	char line[256];
+	char nameserver[16];
+	char keyword[10];
+	int  numOfServers = 0;
+	FILE *fp = fopen(filePath, "r");
+	if (fp == NULL) return -1;
+	while (fgets(line,sizeof(line),fp))
+		{
+		struct in_addr ina;
+		line[255]='\0';		// just to be safe
+		if (sscanf(line,"%10s %15s", keyword, nameserver) != 2) continue;	// it will skip whitespaces
+		if (strncasecmp(keyword,"nameserver",10)) continue;
+		if (inet_aton(nameserver, (struct in_addr *)&ina) != 0)
+			{
+			mDNSAddr DNSAddr;
+			DNSAddr.type = mDNSAddrType_IPv4;
+			DNSAddr.ip.v4.NotAnInteger = ina.s_addr;
+			mDNS_AddDNSServer(m, NULL, mDNSInterface_Any, &DNSAddr, UnicastDNSPort, mDNSfalse, 0);
+			numOfServers++;
+			}
+		}
+    //  __ANDROID__ : if fp was opened, it needs to be closed
+	int fp_closed = fclose(fp);
+	assert(fp_closed == 0);
+	return (numOfServers > 0) ? 0 : -1;
+	}
+
+// Searches the interface list looking for the named interface.
+// Returns a pointer to if it found, or NULL otherwise.
+mDNSlocal PosixNetworkInterface *SearchForInterfaceByName(mDNS *const m, const char *intfName)
+	{
+	PosixNetworkInterface *intf;
+
+	assert(m != NULL);
+	assert(intfName != NULL);
+
+	intf = (PosixNetworkInterface*)(m->HostInterfaces);
+	while ((intf != NULL) && (strcmp(intf->intfName, intfName) != 0))
+		intf = (PosixNetworkInterface *)(intf->coreIntf.next);
+
+	return intf;
+	}
+
+mDNSexport mDNSInterfaceID mDNSPlatformInterfaceIDfromInterfaceIndex(mDNS *const m, mDNSu32 index)
+	{
+	PosixNetworkInterface *intf;
+
+	assert(m != NULL);
+
+	if (index == kDNSServiceInterfaceIndexLocalOnly) return(mDNSInterface_LocalOnly);
+	if (index == kDNSServiceInterfaceIndexP2P      ) return(mDNSInterface_P2P);
+	if (index == kDNSServiceInterfaceIndexAny      ) return(mDNSInterface_Any);
+
+	intf = (PosixNetworkInterface*)(m->HostInterfaces);
+	while ((intf != NULL) && (mDNSu32) intf->index != index) 
+		intf = (PosixNetworkInterface *)(intf->coreIntf.next);
+
+	return (mDNSInterfaceID) intf;
+	}
+	
+mDNSexport mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID(mDNS *const m, mDNSInterfaceID id, mDNSBool suppressNetworkChange)
+	{
+	PosixNetworkInterface *intf;
+	(void) suppressNetworkChange; // Unused
+
+	assert(m != NULL);
+
+	if (id == mDNSInterface_LocalOnly) return(kDNSServiceInterfaceIndexLocalOnly);
+	if (id == mDNSInterface_P2P      ) return(kDNSServiceInterfaceIndexP2P);
+	if (id == mDNSInterface_Any      ) return(kDNSServiceInterfaceIndexAny);
+
+	intf = (PosixNetworkInterface*)(m->HostInterfaces);
+	while ((intf != NULL) && (mDNSInterfaceID) intf != id)
+		intf = (PosixNetworkInterface *)(intf->coreIntf.next);
+
+	return intf ? intf->index : 0;
+	}
+
+// Frees the specified PosixNetworkInterface structure. The underlying
+// interface must have already been deregistered with the mDNS core.
+mDNSlocal void FreePosixNetworkInterface(PosixNetworkInterface *intf)
+	{
+	assert(intf != NULL);
+	if (intf->intfName != NULL)        free((void *)intf->intfName);
+	if (intf->multicastSocket4 != -1)
+		{
+		int ipv4_closed = close(intf->multicastSocket4);
+		assert(ipv4_closed == 0);
+		}
+#if HAVE_IPV6
+	if (intf->multicastSocket6 != -1)
+		{
+		int ipv6_closed = close(intf->multicastSocket6);
+		assert(ipv6_closed == 0);
+		}
+#endif
+	free(intf);
+	}
+
+// Grab the first interface, deregister it, free it, and repeat until done.
+mDNSlocal void ClearInterfaceList(mDNS *const m)
+	{
+	assert(m != NULL);
+
+	while (m->HostInterfaces)
+		{
+		PosixNetworkInterface *intf = (PosixNetworkInterface*)(m->HostInterfaces);
+		mDNS_DeregisterInterface(m, &intf->coreIntf, mDNSfalse);
+		if (gMDNSPlatformPosixVerboseLevel > 0) fprintf(stderr, "Deregistered interface %s\n", intf->intfName);
+		FreePosixNetworkInterface(intf);
+		}
+	num_registered_interfaces = 0;
+	num_pkts_accepted = 0;
+	num_pkts_rejected = 0;
+	}
+
+// Sets up a send/receive socket.
+// If mDNSIPPort port is non-zero, then it's a multicast socket on the specified interface
+// If mDNSIPPort port is zero, then it's a randomly assigned port number, used for sending unicast queries
+mDNSlocal int SetupSocket(struct sockaddr *intfAddr, mDNSIPPort port, int interfaceIndex, int *sktPtr)
+	{
+	int err = 0;
+	static const int kOn = 1;
+	static const int kIntTwoFiveFive = 255;
+	static const unsigned char kByteTwoFiveFive = 255;
+	const mDNSBool JoinMulticastGroup = (port.NotAnInteger != 0);
+	
+	(void) interfaceIndex;	// This parameter unused on plaforms that don't have IPv6
+	assert(intfAddr != NULL);
+	assert(sktPtr != NULL);
+	assert(*sktPtr == -1);
+
+	// Open the socket...
+	if      (intfAddr->sa_family == AF_INET) *sktPtr = socket(PF_INET,  SOCK_DGRAM, IPPROTO_UDP);
+#if HAVE_IPV6
+	else if (intfAddr->sa_family == AF_INET6) *sktPtr = socket(PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
+#endif
+	else return EINVAL;
+
+	if (*sktPtr < 0) { err = errno; perror((intfAddr->sa_family == AF_INET) ? "socket AF_INET" : "socket AF_INET6"); }
+
+	// ... with a shared UDP port, if it's for multicast receiving
+	if (err == 0 && port.NotAnInteger)
+		{
+		#if defined(SO_REUSEPORT)
+			err = setsockopt(*sktPtr, SOL_SOCKET, SO_REUSEPORT, &kOn, sizeof(kOn));
+		#elif defined(SO_REUSEADDR)
+			err = setsockopt(*sktPtr, SOL_SOCKET, SO_REUSEADDR, &kOn, sizeof(kOn));
+		#else
+			#error This platform has no way to avoid address busy errors on multicast.
+		#endif
+		if (err < 0) { err = errno; perror("setsockopt - SO_REUSExxxx"); }
+		}
+
+	// We want to receive destination addresses and interface identifiers.
+	if (intfAddr->sa_family == AF_INET)
+		{
+		struct ip_mreq imr;
+		struct sockaddr_in bindAddr;
+		if (err == 0)
+			{
+			#if defined(IP_PKTINFO)									// Linux
+				err = setsockopt(*sktPtr, IPPROTO_IP, IP_PKTINFO, &kOn, sizeof(kOn));
+				if (err < 0) { err = errno; perror("setsockopt - IP_PKTINFO"); }
+			#elif defined(IP_RECVDSTADDR) || defined(IP_RECVIF)		// BSD and Solaris
+				#if defined(IP_RECVDSTADDR)
+					err = setsockopt(*sktPtr, IPPROTO_IP, IP_RECVDSTADDR, &kOn, sizeof(kOn));
+					if (err < 0) { err = errno; perror("setsockopt - IP_RECVDSTADDR"); }
+				#endif
+				#if defined(IP_RECVIF)
+					if (err == 0)
+						{
+						err = setsockopt(*sktPtr, IPPROTO_IP, IP_RECVIF, &kOn, sizeof(kOn));
+						if (err < 0) { err = errno; perror("setsockopt - IP_RECVIF"); }
+						}
+				#endif
+			#else
+				#warning This platform has no way to get the destination interface information -- will only work for single-homed hosts
+			#endif
+			}
+	#if defined(IP_RECVTTL)									// Linux
+		if (err == 0)
+			{
+			setsockopt(*sktPtr, IPPROTO_IP, IP_RECVTTL, &kOn, sizeof(kOn));
+			// We no longer depend on being able to get the received TTL, so don't worry if the option fails
+			}
+	#endif
+
+		// Add multicast group membership on this interface
+		if (err == 0 && JoinMulticastGroup)
+			{
+			imr.imr_multiaddr.s_addr = AllDNSLinkGroup_v4.ip.v4.NotAnInteger;
+			imr.imr_interface        = ((struct sockaddr_in*)intfAddr)->sin_addr;
+			err = setsockopt(*sktPtr, IPPROTO_IP, IP_ADD_MEMBERSHIP, &imr, sizeof(imr));
+			if (err < 0) { err = errno; perror("setsockopt - IP_ADD_MEMBERSHIP"); }
+			}
+
+		// Specify outgoing interface too
+		if (err == 0 && JoinMulticastGroup)
+			{
+			err = setsockopt(*sktPtr, IPPROTO_IP, IP_MULTICAST_IF, &((struct sockaddr_in*)intfAddr)->sin_addr, sizeof(struct in_addr));
+			if (err < 0) { err = errno; perror("setsockopt - IP_MULTICAST_IF"); }
+			}
+
+		// Per the mDNS spec, send unicast packets with TTL 255
+		if (err == 0)
+			{
+			err = setsockopt(*sktPtr, IPPROTO_IP, IP_TTL, &kIntTwoFiveFive, sizeof(kIntTwoFiveFive));
+			if (err < 0) { err = errno; perror("setsockopt - IP_TTL"); }
+			}
+
+		// and multicast packets with TTL 255 too
+		// There's some debate as to whether IP_MULTICAST_TTL is an int or a byte so we just try both.
+		if (err == 0)
+			{
+			err = setsockopt(*sktPtr, IPPROTO_IP, IP_MULTICAST_TTL, &kByteTwoFiveFive, sizeof(kByteTwoFiveFive));
+			if (err < 0 && errno == EINVAL)
+				err = setsockopt(*sktPtr, IPPROTO_IP, IP_MULTICAST_TTL, &kIntTwoFiveFive, sizeof(kIntTwoFiveFive));
+			if (err < 0) { err = errno; perror("setsockopt - IP_MULTICAST_TTL"); }
+			}
+
+		// And start listening for packets
+		if (err == 0)
+			{
+			bindAddr.sin_family      = AF_INET;
+			bindAddr.sin_port        = port.NotAnInteger;
+			bindAddr.sin_addr.s_addr = INADDR_ANY; // Want to receive multicasts AND unicasts on this socket
+			err = bind(*sktPtr, (struct sockaddr *) &bindAddr, sizeof(bindAddr));
+			if (err < 0) { err = errno; perror("bind"); fflush(stderr); }
+			}
+		} // endif (intfAddr->sa_family == AF_INET)
+
+#if HAVE_IPV6
+	else if (intfAddr->sa_family == AF_INET6)
+		{
+		struct ipv6_mreq imr6;
+		struct sockaddr_in6 bindAddr6;
+	#if defined(IPV6_PKTINFO)
+		if (err == 0)
+			{
+				err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_2292_PKTINFO, &kOn, sizeof(kOn));
+				if (err < 0) { err = errno; perror("setsockopt - IPV6_PKTINFO"); }
+			}
+	#else
+		#warning This platform has no way to get the destination interface information for IPv6 -- will only work for single-homed hosts
+	#endif
+	#if defined(IPV6_HOPLIMIT)
+		if (err == 0)
+			{
+				err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_2292_HOPLIMIT, &kOn, sizeof(kOn));
+				if (err < 0) { err = errno; perror("setsockopt - IPV6_HOPLIMIT"); }
+			}
+	#endif
+
+		// Add multicast group membership on this interface
+		if (err == 0 && JoinMulticastGroup)
+			{
+			imr6.ipv6mr_multiaddr       = *(const struct in6_addr*)&AllDNSLinkGroup_v6.ip.v6;
+			imr6.ipv6mr_interface       = interfaceIndex;
+			//LogMsg("Joining %.16a on %d", &imr6.ipv6mr_multiaddr, imr6.ipv6mr_interface);
+			err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_JOIN_GROUP, &imr6, sizeof(imr6));
+			if (err < 0)
+				{
+				err = errno;
+				verbosedebugf("IPV6_JOIN_GROUP %.16a on %d failed.\n", &imr6.ipv6mr_multiaddr, imr6.ipv6mr_interface);
+				perror("setsockopt - IPV6_JOIN_GROUP");
+				}
+			}
+
+		// Specify outgoing interface too
+		if (err == 0 && JoinMulticastGroup)
+			{
+			u_int	multicast_if = interfaceIndex;
+			err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_MULTICAST_IF, &multicast_if, sizeof(multicast_if));
+			if (err < 0) { err = errno; perror("setsockopt - IPV6_MULTICAST_IF"); }
+			}
+
+		// We want to receive only IPv6 packets on this socket.
+		// Without this option, we may get IPv4 addresses as mapped addresses.
+		if (err == 0)
+			{
+			err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_V6ONLY, &kOn, sizeof(kOn));
+			if (err < 0) { err = errno; perror("setsockopt - IPV6_V6ONLY"); }
+			}
+
+		// Per the mDNS spec, send unicast packets with TTL 255
+		if (err == 0)
+			{
+			err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_UNICAST_HOPS, &kIntTwoFiveFive, sizeof(kIntTwoFiveFive));
+			if (err < 0) { err = errno; perror("setsockopt - IPV6_UNICAST_HOPS"); }
+			}
+
+		// and multicast packets with TTL 255 too
+		// There's some debate as to whether IPV6_MULTICAST_HOPS is an int or a byte so we just try both.
+		if (err == 0)
+			{
+			err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &kByteTwoFiveFive, sizeof(kByteTwoFiveFive));
+			if (err < 0 && errno == EINVAL)
+				err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &kIntTwoFiveFive, sizeof(kIntTwoFiveFive));
+			if (err < 0) { err = errno; perror("setsockopt - IPV6_MULTICAST_HOPS"); }
+			}
+
+		// And start listening for packets
+		if (err == 0)
+			{
+			mDNSPlatformMemZero(&bindAddr6, sizeof(bindAddr6));
+#ifndef NOT_HAVE_SA_LEN
+			bindAddr6.sin6_len         = sizeof(bindAddr6);
+#endif
+			bindAddr6.sin6_family      = AF_INET6;
+			bindAddr6.sin6_port        = port.NotAnInteger;
+			bindAddr6.sin6_flowinfo    = 0;
+			bindAddr6.sin6_addr        = in6addr_any; // Want to receive multicasts AND unicasts on this socket
+			bindAddr6.sin6_scope_id    = 0;
+			err = bind(*sktPtr, (struct sockaddr *) &bindAddr6, sizeof(bindAddr6));
+			if (err < 0) { err = errno; perror("bind"); fflush(stderr); }
+			}
+		} // endif (intfAddr->sa_family == AF_INET6)
+#endif
+
+	// Set the socket to non-blocking.
+	if (err == 0)
+		{
+		err = fcntl(*sktPtr, F_GETFL, 0);
+		if (err < 0) err = errno;
+		else
+			{
+			err = fcntl(*sktPtr, F_SETFL, err | O_NONBLOCK);
+			if (err < 0) err = errno;
+			}
+		}
+
+	// Clean up
+	if (err != 0 && *sktPtr != -1)
+		{
+		int sktClosed = close(*sktPtr);
+		assert(sktClosed == 0);
+		*sktPtr = -1;
+		}
+	assert((err == 0) == (*sktPtr != -1));
+	return err;
+	}
+
+// Creates a PosixNetworkInterface for the interface whose IP address is
+// intfAddr and whose name is intfName and registers it with mDNS core.
+mDNSlocal int SetupOneInterface(mDNS *const m, struct sockaddr *intfAddr, struct sockaddr *intfMask, const char *intfName, int intfIndex)
+	{
+	int err = 0;
+	PosixNetworkInterface *intf;
+	PosixNetworkInterface *alias = NULL;
+
+	assert(m != NULL);
+	assert(intfAddr != NULL);
+	assert(intfName != NULL);
+	assert(intfMask != NULL);
+
+	// Allocate the interface structure itself.
+	intf = (PosixNetworkInterface*)malloc(sizeof(*intf));
+	if (intf == NULL) { assert(0); err = ENOMEM; }
+
+	// And make a copy of the intfName.
+	if (err == 0)
+		{
+		intf->intfName = strdup(intfName);
+		if (intf->intfName == NULL) { assert(0); err = ENOMEM; }
+		}
+
+	if (err == 0)
+		{
+		// Set up the fields required by the mDNS core.
+		SockAddrTomDNSAddr(intfAddr, &intf->coreIntf.ip, NULL);
+		SockAddrTomDNSAddr(intfMask, &intf->coreIntf.mask, NULL);
+
+		//LogMsg("SetupOneInterface: %#a %#a",  &intf->coreIntf.ip,  &intf->coreIntf.mask);
+		strncpy(intf->coreIntf.ifname, intfName, sizeof(intf->coreIntf.ifname));
+		intf->coreIntf.ifname[sizeof(intf->coreIntf.ifname)-1] = 0;
+		intf->coreIntf.Advertise = m->AdvertiseLocalAddresses;
+		intf->coreIntf.McastTxRx = mDNStrue;
+
+		// Set up the extra fields in PosixNetworkInterface.
+		assert(intf->intfName != NULL);         // intf->intfName already set up above
+		intf->index                = intfIndex;
+		intf->multicastSocket4     = -1;
+#if HAVE_IPV6
+		intf->multicastSocket6     = -1;
+#endif
+		alias                      = SearchForInterfaceByName(m, intf->intfName);
+		if (alias == NULL) alias   = intf;
+		intf->coreIntf.InterfaceID = (mDNSInterfaceID)alias;
+
+		if (alias != intf)
+			debugf("SetupOneInterface: %s %#a is an alias of %#a", intfName, &intf->coreIntf.ip, &alias->coreIntf.ip);
+		}
+
+	// Set up the multicast socket
+	if (err == 0)
+		{
+		if (alias->multicastSocket4 == -1 && intfAddr->sa_family == AF_INET)
+			err = SetupSocket(intfAddr, MulticastDNSPort, intf->index, &alias->multicastSocket4);
+#if HAVE_IPV6
+		else if (alias->multicastSocket6 == -1 && intfAddr->sa_family == AF_INET6)
+			err = SetupSocket(intfAddr, MulticastDNSPort, intf->index, &alias->multicastSocket6);
+#endif
+		}
+
+	// The interface is all ready to go, let's register it with the mDNS core.
+	if (err == 0)
+		err = mDNS_RegisterInterface(m, &intf->coreIntf, mDNSfalse);
+
+	// Clean up.
+	if (err == 0)
+		{
+		num_registered_interfaces++;
+		debugf("SetupOneInterface: %s %#a Registered", intf->intfName, &intf->coreIntf.ip);
+		if (gMDNSPlatformPosixVerboseLevel > 0)
+			fprintf(stderr, "Registered interface %s\n", intf->intfName);
+		}
+	else
+		{
+		// Use intfName instead of intf->intfName in the next line to avoid dereferencing NULL.
+		debugf("SetupOneInterface: %s %#a failed to register %d", intfName, &intf->coreIntf.ip, err);
+		if (intf) { FreePosixNetworkInterface(intf); intf = NULL; }
+		}
+
+	assert((err == 0) == (intf != NULL));
+
+	return err;
+	}
+
+// Call get_ifi_info() to obtain a list of active interfaces and call SetupOneInterface() on each one.
+mDNSlocal int SetupInterfaceList(mDNS *const m)
+	{
+	mDNSBool        foundav4       = mDNSfalse;
+	int             err            = 0;
+	struct ifi_info *intfList      = get_ifi_info(AF_INET, mDNStrue);
+	struct ifi_info *firstLoopback = NULL;
+
+	assert(m != NULL);
+	debugf("SetupInterfaceList");
+
+	if (intfList == NULL) err = ENOENT;
+
+#if HAVE_IPV6
+	if (err == 0)		/* Link the IPv6 list to the end of the IPv4 list */
+		{
+		struct ifi_info **p = &intfList;
+		while (*p) p = &(*p)->ifi_next;
+		*p = get_ifi_info(AF_INET6, mDNStrue);
+		}
+#endif
+
+	if (err == 0)
+		{
+		struct ifi_info *i = intfList;
+		while (i)
+			{
+			if (     ((i->ifi_addr->sa_family == AF_INET)
+#if HAVE_IPV6
+					  || (i->ifi_addr->sa_family == AF_INET6)
+#endif
+				) &&  (i->ifi_flags & IFF_UP) && !(i->ifi_flags & IFF_POINTOPOINT))
+				{
+				if (i->ifi_flags & IFF_LOOPBACK)
+					{
+					if (firstLoopback == NULL)
+						firstLoopback = i;
+					}
+				else if (i->ifi_flags & (IFF_MULTICAST | IFF_BROADCAST))  // http://b/25669326
+					{
+					if (SetupOneInterface(m, i->ifi_addr, i->ifi_netmask, i->ifi_name, i->ifi_index) == 0)
+						if (i->ifi_addr->sa_family == AF_INET)
+							foundav4 = mDNStrue;
+					}
+				}
+			i = i->ifi_next;
+			}
+
+		// If we found no normal interfaces but we did find a loopback interface, register the
+		// loopback interface.  This allows self-discovery if no interfaces are configured.
+		// Temporary workaround: Multicast loopback on IPv6 interfaces appears not to work.
+		// In the interim, we skip loopback interface only if we found at least one v4 interface to use
+		// if ((m->HostInterfaces == NULL) && (firstLoopback != NULL))
+		if (!foundav4 && firstLoopback)
+			(void) SetupOneInterface(m, firstLoopback->ifi_addr, firstLoopback->ifi_netmask, firstLoopback->ifi_name, firstLoopback->ifi_index);
+		}
+
+	// Clean up.
+	if (intfList != NULL) free_ifi_info(intfList);
+	return err;
+	}
+
+#if USES_NETLINK
+
+// See <http://www.faqs.org/rfcs/rfc3549.html> for a description of NetLink
+
+// Open a socket that will receive interface change notifications
+mDNSlocal mStatus OpenIfNotifySocket(int *pFD)
+	{
+	mStatus					err = mStatus_NoError;
+	struct sockaddr_nl		snl;
+	int sock;
+	int ret;
+
+	sock = socket(AF_NETLINK, SOCK_RAW, NETLINK_ROUTE);
+	if (sock < 0)
+		return errno;
+
+	// Configure read to be non-blocking because inbound msg size is not known in advance
+	(void) fcntl(sock, F_SETFL, O_NONBLOCK);
+
+	/* Subscribe the socket to Link & IP addr notifications. */
+	mDNSPlatformMemZero(&snl, sizeof snl);
+	snl.nl_family = AF_NETLINK;
+	snl.nl_groups = RTMGRP_LINK | RTMGRP_IPV4_IFADDR;
+	ret = bind(sock, (struct sockaddr *) &snl, sizeof snl);
+	if (0 == ret)
+		*pFD = sock;
+	else
+		err = errno;
+
+	return err;
+	}
+
+#if MDNS_DEBUGMSGS
+mDNSlocal void		PrintNetLinkMsg(const struct nlmsghdr *pNLMsg)
+	{
+	const char *kNLMsgTypes[] = { "", "NLMSG_NOOP", "NLMSG_ERROR", "NLMSG_DONE", "NLMSG_OVERRUN" };
+	const char *kNLRtMsgTypes[] = { "RTM_NEWLINK", "RTM_DELLINK", "RTM_GETLINK", "RTM_NEWADDR", "RTM_DELADDR", "RTM_GETADDR" };
+
+	printf("nlmsghdr len=%d, type=%s, flags=0x%x\n", pNLMsg->nlmsg_len, 
+			pNLMsg->nlmsg_type < RTM_BASE ? kNLMsgTypes[pNLMsg->nlmsg_type] : kNLRtMsgTypes[pNLMsg->nlmsg_type - RTM_BASE], 
+			pNLMsg->nlmsg_flags);
+
+	if (RTM_NEWLINK <= pNLMsg->nlmsg_type && pNLMsg->nlmsg_type <= RTM_GETLINK)
+		{
+		struct ifinfomsg	*pIfInfo = (struct ifinfomsg*) NLMSG_DATA(pNLMsg);
+		printf("ifinfomsg family=%d, type=%d, index=%d, flags=0x%x, change=0x%x\n", pIfInfo->ifi_family, 
+				pIfInfo->ifi_type, pIfInfo->ifi_index, pIfInfo->ifi_flags, pIfInfo->ifi_change);
+		
+		}
+	else if (RTM_NEWADDR <= pNLMsg->nlmsg_type && pNLMsg->nlmsg_type <= RTM_GETADDR)
+		{
+		struct ifaddrmsg	*pIfAddr = (struct ifaddrmsg*) NLMSG_DATA(pNLMsg);
+		printf("ifaddrmsg family=%d, index=%d, flags=0x%x\n", pIfAddr->ifa_family, 
+				pIfAddr->ifa_index, pIfAddr->ifa_flags);
+		}
+	printf("\n");
+	}
+#endif
+
+mDNSlocal mDNSu32		ProcessRoutingNotification(int sd)
+// Read through the messages on sd and if any indicate that any interface records should
+// be torn down and rebuilt, return affected indices as a bitmask. Otherwise return 0.
+	{
+	ssize_t					readCount;
+	char					buff[4096];	
+	struct nlmsghdr			*pNLMsg = (struct nlmsghdr*) buff;
+	mDNSu32				result = 0;
+	
+	// The structure here is more complex than it really ought to be because,
+	// unfortunately, there's no good way to size a buffer in advance large
+	// enough to hold all pending data and so avoid message fragmentation.
+	// (Note that FIONREAD is not supported on AF_NETLINK.)
+
+	readCount = read(sd, buff, sizeof buff);
+	while (1)
+		{
+		// Make sure we've got an entire nlmsghdr in the buffer, and payload, too.
+		// If not, discard already-processed messages in buffer and read more data.
+		if (((char*) &pNLMsg[1] > (buff + readCount)) ||	// i.e. *pNLMsg extends off end of buffer 
+			 ((char*) pNLMsg + pNLMsg->nlmsg_len > (buff + readCount)))
+			{
+			if (buff < (char*) pNLMsg)		// we have space to shuffle
+				{
+				// discard processed data
+				readCount -= ((char*) pNLMsg - buff);
+				memmove(buff, pNLMsg, readCount);
+				pNLMsg = (struct nlmsghdr*) buff;
+
+				// read more data
+				readCount += read(sd, buff + readCount, sizeof buff - readCount);
+				continue;					// spin around and revalidate with new readCount
+				}
+			else
+				break;	// Otherwise message does not fit in buffer
+			}
+
+#if MDNS_DEBUGMSGS
+		PrintNetLinkMsg(pNLMsg);
+#endif
+
+		// Process the NetLink message
+		if (pNLMsg->nlmsg_type == RTM_GETLINK || pNLMsg->nlmsg_type == RTM_NEWLINK)
+			result |= 1 << ((struct ifinfomsg*) NLMSG_DATA(pNLMsg))->ifi_index;
+		else if (pNLMsg->nlmsg_type == RTM_DELADDR || pNLMsg->nlmsg_type == RTM_NEWADDR)
+			result |= 1 << ((struct ifaddrmsg*) NLMSG_DATA(pNLMsg))->ifa_index;
+
+		// Advance pNLMsg to the next message in the buffer
+		if ((pNLMsg->nlmsg_flags & NLM_F_MULTI) != 0 && pNLMsg->nlmsg_type != NLMSG_DONE)
+			{
+			ssize_t	len = readCount - ((char*)pNLMsg - buff);
+			pNLMsg = NLMSG_NEXT(pNLMsg, len);
+			}
+		else
+			break;	// all done!
+		}
+
+	return result;
+	}
+
+#else // USES_NETLINK
+
+// Open a socket that will receive interface change notifications
+mDNSlocal mStatus OpenIfNotifySocket(int *pFD)
+	{
+	*pFD = socket(AF_ROUTE, SOCK_RAW, 0);
+
+	if (*pFD < 0)
+		return mStatus_UnknownErr;
+
+	// Configure read to be non-blocking because inbound msg size is not known in advance
+	(void) fcntl(*pFD, F_SETFL, O_NONBLOCK);
+
+	return mStatus_NoError;
+	}
+
+#if MDNS_DEBUGMSGS
+mDNSlocal void		PrintRoutingSocketMsg(const struct ifa_msghdr *pRSMsg)
+	{
+	const char *kRSMsgTypes[] = { "", "RTM_ADD", "RTM_DELETE", "RTM_CHANGE", "RTM_GET", "RTM_LOSING",
+					"RTM_REDIRECT", "RTM_MISS", "RTM_LOCK", "RTM_OLDADD", "RTM_OLDDEL", "RTM_RESOLVE", 
+					"RTM_NEWADDR", "RTM_DELADDR", "RTM_IFINFO", "RTM_NEWMADDR", "RTM_DELMADDR" };
+
+	int		index = pRSMsg->ifam_type == RTM_IFINFO ? ((struct if_msghdr*) pRSMsg)->ifm_index : pRSMsg->ifam_index;
+
+	printf("ifa_msghdr len=%d, type=%s, index=%d\n", pRSMsg->ifam_msglen, kRSMsgTypes[pRSMsg->ifam_type], index);
+	}
+#endif
+
+mDNSlocal mDNSu32		ProcessRoutingNotification(int sd)
+// Read through the messages on sd and if any indicate that any interface records should
+// be torn down and rebuilt, return affected indices as a bitmask. Otherwise return 0.
+	{
+	ssize_t					readCount;
+	char					buff[4096];	
+	struct ifa_msghdr		*pRSMsg = (struct ifa_msghdr*) buff;
+	mDNSu32				result = 0;
+
+	readCount = read(sd, buff, sizeof buff);
+	if (readCount < (ssize_t) sizeof(struct ifa_msghdr))
+		return mStatus_UnsupportedErr;		// cannot decipher message
+
+#if MDNS_DEBUGMSGS
+	PrintRoutingSocketMsg(pRSMsg);
+#endif
+
+	// Process the message
+	if (pRSMsg->ifam_type == RTM_NEWADDR || pRSMsg->ifam_type == RTM_DELADDR ||
+		 pRSMsg->ifam_type == RTM_IFINFO)
+		{
+		if (pRSMsg->ifam_type == RTM_IFINFO)
+			result |= 1 << ((struct if_msghdr*) pRSMsg)->ifm_index;
+		else
+			result |= 1 << pRSMsg->ifam_index;
+		}
+
+	return result;
+	}
+
+#endif // USES_NETLINK
+
+// Called when data appears on interface change notification socket
+mDNSlocal void InterfaceChangeCallback(int fd, short filter, void *context)
+	{
+	IfChangeRec		*pChgRec = (IfChangeRec*) context;
+	fd_set			readFDs;
+	mDNSu32		changedInterfaces = 0;
+	struct timeval	zeroTimeout = { 0, 0 };
+
+	(void)fd; // Unused
+	(void)filter; // Unused
+
+	FD_ZERO(&readFDs);
+	FD_SET(pChgRec->NotifySD, &readFDs);
+
+	do
+	{
+		changedInterfaces |= ProcessRoutingNotification(pChgRec->NotifySD);
+	}
+	while (0 < select(pChgRec->NotifySD + 1, &readFDs, (fd_set*) NULL, (fd_set*) NULL, &zeroTimeout));
+
+	// Currently we rebuild the entire interface list whenever any interface change is
+	// detected. If this ever proves to be a performance issue in a multi-homed 
+	// configuration, more care should be paid to changedInterfaces.
+	if (changedInterfaces)
+		mDNSPlatformPosixRefreshInterfaceList(pChgRec->mDNS);
+	}
+
+// Register with either a Routing Socket or RtNetLink to listen for interface changes.
+mDNSlocal mStatus WatchForInterfaceChange(mDNS *const m)
+	{
+	mStatus		err;
+	IfChangeRec	*pChgRec;
+
+	pChgRec = (IfChangeRec*) mDNSPlatformMemAllocate(sizeof *pChgRec);
+	if (pChgRec == NULL)
+		return mStatus_NoMemoryErr;
+
+	pChgRec->mDNS = m;
+	err = OpenIfNotifySocket(&pChgRec->NotifySD);
+	if (err == 0)
+		err = mDNSPosixAddFDToEventLoop(pChgRec->NotifySD, InterfaceChangeCallback, pChgRec);
+
+	return err;
+	}
+
+// Test to see if we're the first client running on UDP port 5353, by trying to bind to 5353 without using SO_REUSEPORT.
+// If we fail, someone else got here first. That's not a big problem; we can share the port for multicast responses --
+// we just need to be aware that we shouldn't expect to successfully receive unicast UDP responses.
+mDNSlocal mDNSBool mDNSPlatformInit_CanReceiveUnicast(void)
+	{
+	int err;
+	int s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+	struct sockaddr_in s5353;
+	s5353.sin_family      = AF_INET;
+	s5353.sin_port        = MulticastDNSPort.NotAnInteger;
+	s5353.sin_addr.s_addr = 0;
+	err = bind(s, (struct sockaddr *)&s5353, sizeof(s5353));
+	close(s);
+	if (err) debugf("No unicast UDP responses");
+	else     debugf("Unicast UDP responses okay");
+	return(err == 0);
+	}
+
+// mDNS core calls this routine to initialise the platform-specific data.
+mDNSexport mStatus mDNSPlatformInit(mDNS *const m)
+	{
+	int err = 0;
+	struct sockaddr sa;
+	assert(m != NULL);
+
+	if (mDNSPlatformInit_CanReceiveUnicast()) m->CanReceiveUnicastOn5353 = mDNStrue;
+
+	// Tell mDNS core the names of this machine.
+
+	// Set up the nice label
+	m->nicelabel.c[0] = 0;
+	GetUserSpecifiedFriendlyComputerName(&m->nicelabel);
+	if (m->nicelabel.c[0] == 0) MakeDomainLabelFromLiteralString(&m->nicelabel, "Computer");
+
+	// Set up the RFC 1034-compliant label
+	m->hostlabel.c[0] = 0;
+	GetUserSpecifiedRFC1034ComputerName(&m->hostlabel);
+	if (m->hostlabel.c[0] == 0) MakeDomainLabelFromLiteralString(&m->hostlabel, "Computer");
+
+	mDNS_SetFQDN(m);
+
+	sa.sa_family = AF_INET;
+	m->p->unicastSocket4 = -1;
+	if (err == mStatus_NoError) err = SetupSocket(&sa, zeroIPPort, 0, &m->p->unicastSocket4);
+#if HAVE_IPV6
+	sa.sa_family = AF_INET6;
+	m->p->unicastSocket6 = -1;
+	if (err == mStatus_NoError) err = SetupSocket(&sa, zeroIPPort, 0, &m->p->unicastSocket6);
+#endif
+
+	// Tell mDNS core about the network interfaces on this machine.
+	if (err == mStatus_NoError) err = SetupInterfaceList(m);
+
+	// Tell mDNS core about DNS Servers
+	mDNS_Lock(m);
+	if (err == mStatus_NoError) ParseDNSServers(m, uDNS_SERVERS_FILE);
+	mDNS_Unlock(m);
+
+	if (err == mStatus_NoError)
+		{
+		err = WatchForInterfaceChange(m);
+		// Failure to observe interface changes is non-fatal.
+		if (err != mStatus_NoError)
+			{
+			fprintf(stderr, "mDNS(%d) WARNING: Unable to detect interface changes (%d).\n", getpid(), err);
+			err = mStatus_NoError;
+			}
+		}
+
+	// We don't do asynchronous initialization on the Posix platform, so by the time
+	// we get here the setup will already have succeeded or failed.  If it succeeded,
+	// we should just call mDNSCoreInitComplete() immediately.
+	if (err == mStatus_NoError)
+		mDNSCoreInitComplete(m, mStatus_NoError);
+
+	return PosixErrorToStatus(err);
+	}
+
+// mDNS core calls this routine to clean up the platform-specific data.
+// In our case all we need to do is to tear down every network interface.
+mDNSexport void mDNSPlatformClose(mDNS *const m)
+	{
+	assert(m != NULL);
+	ClearInterfaceList(m);
+	if (m->p->unicastSocket4 != -1)
+		{
+		int ipv4_closed = close(m->p->unicastSocket4);
+		assert(ipv4_closed == 0);
+		}
+#if HAVE_IPV6
+	if (m->p->unicastSocket6 != -1)
+		{
+		int ipv6_closed = close(m->p->unicastSocket6);
+		assert(ipv6_closed == 0);
+		}
+#endif
+	}
+
+mDNSexport mStatus mDNSPlatformPosixRefreshInterfaceList(mDNS *const m)
+	{
+	int err;
+	ClearInterfaceList(m);
+	err = SetupInterfaceList(m);
+	return PosixErrorToStatus(err);
+	}
+
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark ***** Locking
+#endif
+
+// On the Posix platform, locking is a no-op because we only ever enter
+// mDNS core on the main thread.
+
+// mDNS core calls this routine when it wants to prevent
+// the platform from reentering mDNS core code.
+mDNSexport void    mDNSPlatformLock   (const mDNS *const m)
+	{
+	(void) m;	// Unused
+	}
+
+// mDNS core calls this routine when it release the lock taken by
+// mDNSPlatformLock and allow the platform to reenter mDNS core code.
+mDNSexport void    mDNSPlatformUnlock (const mDNS *const m)
+	{
+	(void) m;	// Unused
+	}
+
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark ***** Strings
+#endif
+
+// mDNS core calls this routine to copy C strings.
+// On the Posix platform this maps directly to the ANSI C strcpy.
+mDNSexport void    mDNSPlatformStrCopy(void *dst, const void *src)
+	{
+	strcpy((char *)dst, (char *)src);
+	}
+
+mDNSexport mDNSu32  mDNSPlatformStrLCopy(void *dst, const void *src, mDNSu32 len)
+{
+#if HAVE_STRLCPY
+    return ((mDNSu32)strlcpy((char *)dst, (const char *)src, len));
+#else
+    size_t srcLen;
+
+    srcLen = strlen((const char *)src);
+    if (srcLen < len)
+    {
+        memcpy(dst, src, srcLen + 1);
+    }
+    else if (len > 0)
+    {
+        memcpy(dst, src, len - 1);
+        ((char *)dst)[len - 1] = '\0';
+    }
+
+    return ((mDNSu32)srcLen);
+#endif
+}
+
+// mDNS core calls this routine to get the length of a C string.
+// On the Posix platform this maps directly to the ANSI C strlen.
+mDNSexport mDNSu32  mDNSPlatformStrLen (const void *src)
+	{
+	return strlen((char*)src);
+	}
+
+// mDNS core calls this routine to copy memory.
+// On the Posix platform this maps directly to the ANSI C memcpy.
+mDNSexport void    mDNSPlatformMemCopy(void *dst, const void *src, mDNSu32 len)
+	{
+	memcpy(dst, src, len);
+	}
+
+// mDNS core calls this routine to test whether blocks of memory are byte-for-byte
+// identical. On the Posix platform this is a simple wrapper around ANSI C memcmp.
+mDNSexport mDNSBool mDNSPlatformMemSame(const void *dst, const void *src, mDNSu32 len)
+	{
+	return memcmp(dst, src, len) == 0;
+	}
+
+// mDNS core calls this routine to clear blocks of memory.
+// On the Posix platform this is a simple wrapper around ANSI C memset.
+mDNSexport void    mDNSPlatformMemZero(void *dst, mDNSu32 len)
+	{
+	memset(dst, 0, len);
+	}
+
+mDNSexport void *  mDNSPlatformMemAllocate(mDNSu32 len) { return(malloc(len)); }
+mDNSexport void    mDNSPlatformMemFree    (void *mem)   { free(mem); }
+
+mDNSexport mDNSu32 mDNSPlatformRandomSeed(void)
+	{
+	struct timeval tv;
+	gettimeofday(&tv, NULL);
+	return(tv.tv_usec);
+	}
+
+mDNSexport mDNSs32  mDNSPlatformOneSecond = 1024;
+
+mDNSexport mStatus mDNSPlatformTimeInit(void)
+	{
+	// No special setup is required on Posix -- we just use gettimeofday();
+	// This is not really safe, because gettimeofday can go backwards if the user manually changes the date or time
+	// We should find a better way to do this
+	return(mStatus_NoError);
+	}
+
+mDNSexport mDNSs32  mDNSPlatformRawTime()
+	{
+	struct timeval tv;
+	gettimeofday(&tv, NULL);
+	// tv.tv_sec is seconds since 1st January 1970 (GMT, with no adjustment for daylight savings time)
+	// tv.tv_usec is microseconds since the start of this second (i.e. values 0 to 999999)
+	// We use the lower 22 bits of tv.tv_sec for the top 22 bits of our result
+	// and we multiply tv.tv_usec by 16 / 15625 to get a value in the range 0-1023 to go in the bottom 10 bits.
+	// This gives us a proper modular (cyclic) counter that has a resolution of roughly 1ms (actually 1/1024 second)
+	// and correctly cycles every 2^22 seconds (4194304 seconds = approx 48 days).
+	return((tv.tv_sec << 10) | (tv.tv_usec * 16 / 15625));
+	}
+
+mDNSexport mDNSs32 mDNSPlatformUTC(void)
+	{
+	return time(NULL);
+	}
+
+mDNSexport void mDNSPlatformSendWakeupPacket(mDNS *const m, mDNSInterfaceID InterfaceID, char *EthAddr, char *IPAddr, int iteration)
+	{
+	(void) m;
+	(void) InterfaceID;
+	(void) EthAddr;
+	(void) IPAddr;
+	(void) iteration;
+	}
+
+mDNSexport mDNSBool mDNSPlatformValidRecordForInterface(AuthRecord *rr, const NetworkInterfaceInfo *intf)
+	{
+	(void) rr;
+	(void) intf;
+
+	return 1;
+	}
+
+mDNSlocal void mDNSPosixAddToFDSet(int *nfds, fd_set *readfds, int s)
+	{
+	if (*nfds < s + 1) *nfds = s + 1;
+	FD_SET(s, readfds);
+	}
+
+mDNSexport void mDNSPosixGetFDSet(mDNS *m, int *nfds, fd_set *readfds, struct timeval *timeout)
+	{
+	mDNSs32 ticks;
+	struct timeval interval;
+
+	// 1. Call mDNS_Execute() to let mDNSCore do what it needs to do
+	mDNSs32 nextevent = mDNS_Execute(m);
+
+	// 2. Build our list of active file descriptors
+	PosixNetworkInterface *info = (PosixNetworkInterface *)(m->HostInterfaces);
+	if (m->p->unicastSocket4 != -1) mDNSPosixAddToFDSet(nfds, readfds, m->p->unicastSocket4);
+#if HAVE_IPV6
+	if (m->p->unicastSocket6 != -1) mDNSPosixAddToFDSet(nfds, readfds, m->p->unicastSocket6);
+#endif
+	while (info)
+		{
+		if (info->multicastSocket4 != -1) mDNSPosixAddToFDSet(nfds, readfds, info->multicastSocket4);
+#if HAVE_IPV6
+		if (info->multicastSocket6 != -1) mDNSPosixAddToFDSet(nfds, readfds, info->multicastSocket6);
+#endif
+		info = (PosixNetworkInterface *)(info->coreIntf.next);
+		}
+
+	// 3. Calculate the time remaining to the next scheduled event (in struct timeval format)
+	ticks = nextevent - mDNS_TimeNow(m);
+	if (ticks < 1) ticks = 1;
+	interval.tv_sec  = ticks >> 10;						// The high 22 bits are seconds
+	interval.tv_usec = ((ticks & 0x3FF) * 15625) / 16;	// The low 10 bits are 1024ths
+
+	// 4. If client's proposed timeout is more than what we want, then reduce it
+	if (timeout->tv_sec > interval.tv_sec ||
+		(timeout->tv_sec == interval.tv_sec && timeout->tv_usec > interval.tv_usec))
+		*timeout = interval;
+	}
+
+mDNSexport void mDNSPosixProcessFDSet(mDNS *const m, fd_set *readfds)
+	{
+	PosixNetworkInterface *info;
+	assert(m       != NULL);
+	assert(readfds != NULL);
+	info = (PosixNetworkInterface *)(m->HostInterfaces);
+
+	if (m->p->unicastSocket4 != -1 && FD_ISSET(m->p->unicastSocket4, readfds))
+		{
+		FD_CLR(m->p->unicastSocket4, readfds);
+		SocketDataReady(m, NULL, m->p->unicastSocket4);
+		}
+#if HAVE_IPV6
+	if (m->p->unicastSocket6 != -1 && FD_ISSET(m->p->unicastSocket6, readfds))
+		{
+		FD_CLR(m->p->unicastSocket6, readfds);
+		SocketDataReady(m, NULL, m->p->unicastSocket6);
+		}
+#endif
+
+	while (info)
+		{
+		if (info->multicastSocket4 != -1 && FD_ISSET(info->multicastSocket4, readfds))
+			{
+			FD_CLR(info->multicastSocket4, readfds);
+			SocketDataReady(m, info, info->multicastSocket4);
+			}
+#if HAVE_IPV6
+		if (info->multicastSocket6 != -1 && FD_ISSET(info->multicastSocket6, readfds))
+			{
+			FD_CLR(info->multicastSocket6, readfds);
+			SocketDataReady(m, info, info->multicastSocket6);
+			}
+#endif
+		info = (PosixNetworkInterface *)(info->coreIntf.next);
+		}
+	}
+
+// update gMaxFD
+mDNSlocal void	DetermineMaxEventFD(void)
+	{
+	PosixEventSource	*iSource;
+	
+	gMaxFD = 0;
+	for (iSource=(PosixEventSource*)gEventSources.Head; iSource; iSource = iSource->Next)
+		if (gMaxFD < iSource->fd)
+			gMaxFD = iSource->fd;
+	}
+
+// Add a file descriptor to the set that mDNSPosixRunEventLoopOnce() listens to.
+mStatus mDNSPosixAddFDToEventLoop(int fd, mDNSPosixEventCallback callback, void *context)
+	{
+	PosixEventSource	*newSource;
+	
+	if (gEventSources.LinkOffset == 0)
+		InitLinkedList(&gEventSources, offsetof(PosixEventSource, Next));
+
+	if (fd >= (int) FD_SETSIZE || fd < 0)
+		return mStatus_UnsupportedErr;
+	if (callback == NULL)
+		return mStatus_BadParamErr;
+
+	newSource = (PosixEventSource*) malloc(sizeof *newSource);
+	if (NULL == newSource)
+		return mStatus_NoMemoryErr;
+
+	newSource->Callback = callback;
+	newSource->Context = context;
+	newSource->fd = fd;
+
+	AddToTail(&gEventSources, newSource);
+	FD_SET(fd, &gEventFDs);
+
+	DetermineMaxEventFD();
+
+	return mStatus_NoError;
+	}
+
+// Remove a file descriptor from the set that mDNSPosixRunEventLoopOnce() listens to.
+mStatus mDNSPosixRemoveFDFromEventLoop(int fd)
+	{
+	PosixEventSource	*iSource;
+	
+	for (iSource=(PosixEventSource*)gEventSources.Head; iSource; iSource = iSource->Next)
+		{
+		if (fd == iSource->fd)
+			{
+			FD_CLR(fd, &gEventFDs);
+			RemoveFromList(&gEventSources, iSource);
+			free(iSource);
+			DetermineMaxEventFD();
+			return mStatus_NoError;
+			}
+		}
+	return mStatus_NoSuchNameErr;
+	}
+
+// Simply note the received signal in gEventSignals.
+mDNSlocal void	NoteSignal(int signum)
+	{
+	sigaddset(&gEventSignals, signum);
+	}
+
+// Tell the event package to listen for signal and report it in mDNSPosixRunEventLoopOnce().
+mStatus mDNSPosixListenForSignalInEventLoop(int signum)
+	{
+	struct sigaction	action;
+	mStatus				err;
+
+	mDNSPlatformMemZero(&action, sizeof action);		// more portable than member-wise assignment
+	action.sa_handler = NoteSignal;
+	err = sigaction(signum, &action, (struct sigaction*) NULL);
+	
+	sigaddset(&gEventSignalSet, signum);
+
+	return err;
+	}
+
+// Tell the event package to stop listening for signal in mDNSPosixRunEventLoopOnce().
+mStatus mDNSPosixIgnoreSignalInEventLoop(int signum)
+	{
+	struct sigaction	action;
+	mStatus				err;
+
+	mDNSPlatformMemZero(&action, sizeof action);		// more portable than member-wise assignment
+	action.sa_handler = SIG_DFL;
+	err = sigaction(signum, &action, (struct sigaction*) NULL);
+	
+	sigdelset(&gEventSignalSet, signum);
+
+	return err;
+	}
+
+// Do a single pass through the attendent event sources and dispatch any found to their callbacks.
+// Return as soon as internal timeout expires, or a signal we're listening for is received.
+mStatus mDNSPosixRunEventLoopOnce(mDNS *m, const struct timeval *pTimeout, 
+									sigset_t *pSignalsReceived, mDNSBool *pDataDispatched)
+	{
+	fd_set			listenFDs = gEventFDs;
+	int				fdMax = 0, numReady;
+	struct timeval	timeout = *pTimeout;
+	
+	// Include the sockets that are listening to the wire in our select() set
+	mDNSPosixGetFDSet(m, &fdMax, &listenFDs, &timeout);	// timeout may get modified
+	if (fdMax < gMaxFD)
+		fdMax = gMaxFD;
+
+	numReady = select(fdMax + 1, &listenFDs, (fd_set*) NULL, (fd_set*) NULL, &timeout);
+
+	// If any data appeared, invoke its callback
+	if (numReady > 0)
+		{
+		PosixEventSource	*iSource;
+
+		(void) mDNSPosixProcessFDSet(m, &listenFDs);	// call this first to process wire data for clients
+
+		for (iSource=(PosixEventSource*)gEventSources.Head; iSource; iSource = iSource->Next)
+			{
+			if (FD_ISSET(iSource->fd, &listenFDs))
+				{
+				iSource->Callback(iSource->fd, 0, iSource->Context);
+				break;	// in case callback removed elements from gEventSources
+				}
+			}
+		*pDataDispatched = mDNStrue;
+		}
+	else
+		*pDataDispatched = mDNSfalse;
+
+	(void) sigprocmask(SIG_BLOCK, &gEventSignalSet, (sigset_t*) NULL);
+	*pSignalsReceived = gEventSignals;
+	sigemptyset(&gEventSignals);
+	(void) sigprocmask(SIG_UNBLOCK, &gEventSignalSet, (sigset_t*) NULL);
+
+	return mStatus_NoError;
+	}
diff --git a/mdnsresponder/mDNSPosix/mDNSPosix.h b/mdnsresponder/mDNSPosix/mDNSPosix.h
new file mode 100755
index 0000000..4c3dfc1
--- /dev/null
+++ b/mdnsresponder/mDNSPosix/mDNSPosix.h
@@ -0,0 +1,88 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+#ifndef __mDNSPlatformPosix_h
+#define __mDNSPlatformPosix_h
+
+#include <signal.h>
+#include <sys/time.h>
+#ifdef __ANDROID__
+#include <sys/select.h>
+#endif
+
+#ifdef  __cplusplus
+    extern "C" {
+#endif
+
+// PosixNetworkInterface is a record extension of the core NetworkInterfaceInfo
+// type that supports extra fields needed by the Posix platform.
+//
+// IMPORTANT: coreIntf must be the first field in the structure because
+// we cast between pointers to the two different types regularly.
+
+typedef struct PosixNetworkInterface PosixNetworkInterface;
+
+struct PosixNetworkInterface
+	{
+	NetworkInterfaceInfo    coreIntf;
+	const char *            intfName;
+	PosixNetworkInterface * aliasIntf;
+	int                     index;
+	int                     multicastSocket4;
+#if HAVE_IPV6
+	int                     multicastSocket6;
+#endif
+	};
+
+// This is a global because debugf_() needs to be able to check its value
+extern int gMDNSPlatformPosixVerboseLevel;
+
+struct mDNS_PlatformSupport_struct
+	{
+	int unicastSocket4;
+#if HAVE_IPV6
+	int unicastSocket6;
+#endif
+	};
+
+#define uDNS_SERVERS_FILE "/etc/resolv.conf"
+extern int ParseDNSServers(mDNS *m, const char *filePath);
+extern mStatus mDNSPlatformPosixRefreshInterfaceList(mDNS *const m);
+    // See comment in implementation.
+
+// Call mDNSPosixGetFDSet before calling select(), to update the parameters
+// as may be necessary to meet the needs of the mDNSCore code.
+// The timeout pointer MUST NOT be NULL.
+// Set timeout->tv_sec to 0x3FFFFFFF if you want to have effectively no timeout
+// After calling mDNSPosixGetFDSet(), call select(nfds, &readfds, NULL, NULL, &timeout); as usual
+// After select() returns, call mDNSPosixProcessFDSet() to let mDNSCore do its work
+extern void mDNSPosixGetFDSet(mDNS *m, int *nfds, fd_set *readfds, struct timeval *timeout);
+extern void mDNSPosixProcessFDSet(mDNS *const m, fd_set *readfds);
+
+typedef	void (*mDNSPosixEventCallback)(int fd, short filter, void *context);
+
+extern mStatus mDNSPosixAddFDToEventLoop( int fd, mDNSPosixEventCallback callback, void *context);
+extern mStatus mDNSPosixRemoveFDFromEventLoop( int fd);
+extern mStatus mDNSPosixListenForSignalInEventLoop( int signum);
+extern mStatus mDNSPosixIgnoreSignalInEventLoop( int signum);
+extern mStatus mDNSPosixRunEventLoopOnce( mDNS *m, const struct timeval *pTimeout, sigset_t *pSignalsReceived, mDNSBool *pDataDispatched);
+
+#ifdef  __cplusplus
+    }
+#endif
+
+#endif
diff --git a/mdnsresponder/mDNSPosix/mDNSUNP.c b/mdnsresponder/mDNSPosix/mDNSUNP.c
new file mode 100755
index 0000000..5379b6b
--- /dev/null
+++ b/mdnsresponder/mDNSPosix/mDNSUNP.c
@@ -0,0 +1,726 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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 "mDNSUNP.h"
+
+#include <errno.h>
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/uio.h>
+#include <sys/ioctl.h>
+#include <signal.h>
+#include <unistd.h>
+#include <stdio.h>
+
+/* Some weird platforms derived from 4.4BSD Lite (e.g. EFI) need the ALIGN(P)
+   macro, usually defined in <sys/param.h> or someplace like that, to make sure the
+   CMSG_NXTHDR macro is well-formed. On such platforms, the symbol NEED_ALIGN_MACRO
+   should be set to the name of the header to include to get the ALIGN(P) macro.
+*/
+#ifdef NEED_ALIGN_MACRO
+#include NEED_ALIGN_MACRO
+#endif
+
+/* Solaris defined SIOCGIFCONF etc in <sys/sockio.h> but 
+   other platforms don't even have that include file.  So, 
+   if we haven't yet got a definition, let's try to find 
+   <sys/sockio.h>.
+*/
+
+#ifndef SIOCGIFCONF
+    #include <sys/sockio.h>
+#endif
+
+/* sockaddr_dl is only referenced if we're using IP_RECVIF, 
+   so only include the header in that case.
+*/
+
+#ifdef  IP_RECVIF
+    #include <net/if_dl.h>
+#endif
+
+#if defined(AF_INET6) && HAVE_IPV6 && !HAVE_LINUX
+#include <net/if_var.h>
+#include <netinet/in_var.h>
+// Note: netinet/in_var.h implicitly includes netinet6/in6_var.h for us
+#endif
+
+#if defined(AF_INET6) && HAVE_IPV6 && HAVE_LINUX
+#include <netdb.h>
+#include <arpa/inet.h>
+
+/* Converts a prefix length to IPv6 network mask */
+void plen_to_mask(int plen, char *addr) {
+	int i;
+	int colons=7; /* Number of colons in IPv6 address */
+	int bits_in_block=16; /* Bits per IPv6 block */
+	for(i=0;i<=colons;i++) {
+		int block, ones=0xffff, ones_in_block;
+		if (plen>bits_in_block) ones_in_block=bits_in_block;
+		else                    ones_in_block=plen;
+		block = ones & (ones << (bits_in_block-ones_in_block));
+		i==0 ? sprintf(addr, "%x", block) : sprintf(addr, "%s:%x", addr, block);
+		plen -= ones_in_block;
+		}
+	}
+
+/* Gets IPv6 interface information from the /proc filesystem in linux*/
+struct ifi_info *get_ifi_info_linuxv6(int family, int doaliases)
+	{
+		struct ifi_info *ifi, *ifihead, **ifipnext, *ifipold, **ifiptr;
+	FILE *fp = NULL;
+	char addr[8][5];
+	int flags, myflags, index, plen, scope;
+	char ifname[IFNAMSIZ], lastname[IFNAMSIZ];
+	char addr6[32+7+1]; /* don't forget the seven ':' */
+	struct addrinfo hints, *res0;
+	struct sockaddr_in6 *sin6;
+	struct in6_addr *addrptr;
+	int err;
+	int sockfd = -1;
+	struct ifreq ifr;
+
+	res0=NULL;
+	ifihead = NULL;
+	ifipnext = &ifihead;
+	lastname[0] = 0;
+
+	if ((fp = fopen(PROC_IFINET6_PATH, "r")) != NULL) {
+		sockfd = socket(AF_INET6, SOCK_DGRAM, 0);
+		if (sockfd < 0) {
+			goto gotError;
+		}
+		while (fscanf(fp,
+					  "%4s%4s%4s%4s%4s%4s%4s%4s %02x %02x %02x %02x %15s\n",
+					  addr[0],addr[1],addr[2],addr[3],
+					  addr[4],addr[5],addr[6],addr[7],
+					  &index, &plen, &scope, &flags, ifname) != EOF) {
+
+			myflags = 0;
+			if (strncmp(lastname, ifname, IFNAMSIZ) == 0) {
+				if (doaliases == 0)
+					continue;   /* already processed this interface */
+				myflags = IFI_ALIAS;
+				}
+			strncpy(lastname, ifname, IFNAMSIZ);
+			ifi = (struct ifi_info*)calloc(1, sizeof(struct ifi_info));
+			if (ifi == NULL) {
+				goto gotError;
+			}
+			
+			ifipold   = *ifipnext;       /* need this later */
+			ifiptr    = ifipnext;
+			*ifipnext = ifi;            /* prev points to this new one */
+			ifipnext = &ifi->ifi_next;  /* pointer to next one goes here */
+
+			sprintf(addr6, "%s:%s:%s:%s:%s:%s:%s:%s",
+					addr[0],addr[1],addr[2],addr[3],
+					addr[4],addr[5],addr[6],addr[7]);
+
+			/* Add address of the interface */
+			memset(&hints, 0, sizeof(hints));
+			hints.ai_family = AF_INET6;
+			hints.ai_flags = AI_NUMERICHOST;
+			err = getaddrinfo(addr6, NULL, &hints, &res0);
+			if (err) {
+				goto gotError;
+				}
+			ifi->ifi_addr = calloc(1, sizeof(struct sockaddr_in6));
+			if (ifi->ifi_addr == NULL) {
+				goto gotError;
+				}
+			memcpy(ifi->ifi_addr, res0->ai_addr, sizeof(struct sockaddr_in6));
+
+			/* Add netmask of the interface */
+			char ipv6addr[INET6_ADDRSTRLEN];
+			plen_to_mask(plen, ipv6addr);
+			ifi->ifi_netmask = calloc(1, sizeof(struct sockaddr_in6));
+			if (ifi->ifi_addr == NULL) {
+				goto gotError;
+				}
+			sin6=calloc(1, sizeof(struct sockaddr_in6));
+			addrptr=calloc(1, sizeof(struct in6_addr));
+			inet_pton(family, ipv6addr, addrptr);
+			sin6->sin6_family=family;
+			sin6->sin6_addr=*addrptr;
+			sin6->sin6_scope_id=scope;
+			memcpy(ifi->ifi_netmask, sin6, sizeof(struct sockaddr_in6));
+			free(sin6);
+
+
+			/* Add interface name */
+			strncpy(ifi->ifi_name, ifname, IFI_NAME);
+
+			/* Add interface index */
+			ifi->ifi_index = index;
+
+			/* Add interface flags*/
+			strncpy(ifr.ifr_name, ifname, IFNAMSIZ);
+			if (ioctl(sockfd, SIOCGIFFLAGS, &ifr) < 0) {
+				if (errno == EADDRNOTAVAIL) {
+					/* 
+					 * If the main interface is configured with no IP address but 
+					 * an alias interface exists with an IP address, you get 
+					 * EADDRNOTAVAIL for the main interface 
+					 */
+					free(ifi->ifi_addr);
+					free(ifi);
+					ifipnext  = ifiptr; 
+					*ifipnext = ifipold;
+					continue;
+				} else {
+					goto gotError;
+				}
+			}
+			ifi->ifi_flags = ifr.ifr_flags;
+			freeaddrinfo(res0);
+			res0=NULL;
+			}
+		}
+	goto done;
+
+	gotError:
+	if (ifihead != NULL) {
+		free_ifi_info(ifihead);
+		ifihead = NULL;
+		}
+	if (res0 != NULL) {
+		freeaddrinfo(res0);
+		res0=NULL;
+		}
+	done:
+	if (sockfd != -1) {
+// __ANDROID__ : replaced assert(close(..))
+		int sockfd_closed = close(sockfd);
+		assert(sockfd_closed == 0);
+		}
+// __ANDROID__ : if fp was opened, it needs to be closed
+	if (fp != NULL) {
+		int fd_closed = fclose(fp);
+		assert(fd_closed == 0);
+		}
+	return(ifihead);    /* pointer to first structure in linked list */
+	}
+#endif // defined(AF_INET6) && HAVE_IPV6 && HAVE_LINUX
+
+struct ifi_info *get_ifi_info(int family, int doaliases)
+{
+    int                 junk;
+    struct ifi_info     *ifi, *ifihead, **ifipnext, *ifipold, **ifiptr;
+    int                 sockfd, sockf6, len, lastlen, flags, myflags;
+#ifdef NOT_HAVE_IF_NAMETOINDEX
+    int                 index = 200;
+#endif
+    char                *ptr, *buf, lastname[IFNAMSIZ], *cptr;
+    struct ifconf       ifc;
+    struct ifreq        *ifr, ifrcopy;
+    struct sockaddr_in  *sinptr;
+    
+#if defined(AF_INET6) && HAVE_IPV6
+    struct sockaddr_in6 *sinptr6;
+#endif
+
+#if defined(AF_INET6) && HAVE_IPV6 && HAVE_LINUX
+ if (family == AF_INET6) return get_ifi_info_linuxv6(family, doaliases);
+#endif
+
+	sockfd = -1;
+    sockf6 = -1;
+    buf = NULL;
+    ifihead = NULL;
+    
+    sockfd = socket(AF_INET, SOCK_DGRAM, 0);
+    if (sockfd < 0) {
+        goto gotError;
+    }
+
+    lastlen = 0;
+    len = 100 * sizeof(struct ifreq);   /* initial buffer size guess */
+    for ( ; ; ) {
+        buf = (char*)malloc(len);
+        if (buf == NULL) {
+            goto gotError;
+        }
+        ifc.ifc_len = len;
+        ifc.ifc_buf = buf;
+        if (ioctl(sockfd, SIOCGIFCONF, &ifc) < 0) {
+            if (errno != EINVAL || lastlen != 0) {
+                goto gotError;
+            }
+        } else {
+            if (ifc.ifc_len == lastlen)
+                break;      /* success, len has not changed */
+            lastlen = ifc.ifc_len;
+        }
+        len += 10 * sizeof(struct ifreq);   /* increment */
+        free(buf);
+    }
+    ifihead = NULL;
+    ifipnext = &ifihead;
+    lastname[0] = 0;
+/* end get_ifi_info1 */
+
+/* include get_ifi_info2 */
+    for (ptr = buf; ptr < buf + ifc.ifc_len; ) {
+        ifr = (struct ifreq *) ptr;
+
+        /* Advance to next one in buffer */
+        if (sizeof(struct ifreq) > sizeof(ifr->ifr_name) + GET_SA_LEN(ifr->ifr_addr))
+            ptr += sizeof(struct ifreq);
+        else
+            ptr += sizeof(ifr->ifr_name) + GET_SA_LEN(ifr->ifr_addr);
+
+//      fprintf(stderr, "intf %p name=%s AF=%d\n", index, ifr->ifr_name, ifr->ifr_addr.sa_family);
+        
+        if (ifr->ifr_addr.sa_family != family)
+            continue;   /* ignore if not desired address family */
+
+        myflags = 0;
+        if ( (cptr = strchr(ifr->ifr_name, ':')) != NULL)
+            *cptr = 0;      /* replace colon will null */
+        if (strncmp(lastname, ifr->ifr_name, IFNAMSIZ) == 0) {
+            if (doaliases == 0)
+                continue;   /* already processed this interface */
+            myflags = IFI_ALIAS;
+        }
+        memcpy(lastname, ifr->ifr_name, IFNAMSIZ);
+
+        ifrcopy = *ifr;
+        if (ioctl(sockfd, SIOCGIFFLAGS, &ifrcopy) < 0) {
+            goto gotError;
+        }
+        
+        flags = ifrcopy.ifr_flags;
+        if ((flags & IFF_UP) == 0)
+            continue;   /* ignore if interface not up */
+
+        ifi = (struct ifi_info*)calloc(1, sizeof(struct ifi_info));
+        if (ifi == NULL) {
+            goto gotError;
+        }
+		ifipold   = *ifipnext;       /* need this later */
+		ifiptr    = ifipnext;
+		*ifipnext = ifi;             /* prev points to this new one */
+		ifipnext  = &ifi->ifi_next;  /* pointer to next one goes here */
+		
+        ifi->ifi_flags = flags;     /* IFF_xxx values */
+        ifi->ifi_myflags = myflags; /* IFI_xxx values */
+#ifndef NOT_HAVE_IF_NAMETOINDEX
+        ifi->ifi_index = if_nametoindex(ifr->ifr_name);
+#else
+        ifrcopy = *ifr;
+#ifdef SIOCGIFINDEX
+		if ( 0 >= ioctl(sockfd, SIOCGIFINDEX, &ifrcopy))
+            ifi->ifi_index = ifrcopy.ifr_index;
+        else
+#endif
+            ifi->ifi_index = index++;	/* SIOCGIFINDEX is broken on Solaris 2.5ish, so fake it */
+#endif
+        memcpy(ifi->ifi_name, ifr->ifr_name, IFI_NAME);
+        ifi->ifi_name[IFI_NAME-1] = '\0';
+/* end get_ifi_info2 */
+/* include get_ifi_info3 */
+        switch (ifr->ifr_addr.sa_family) {
+        case AF_INET:
+            sinptr = (struct sockaddr_in *) &ifr->ifr_addr;
+            if (ifi->ifi_addr == NULL) {
+                ifi->ifi_addr = (struct sockaddr*)calloc(1, sizeof(struct sockaddr_in));
+                if (ifi->ifi_addr == NULL) {
+                    goto gotError;
+                }
+                memcpy(ifi->ifi_addr, sinptr, sizeof(struct sockaddr_in));
+
+#ifdef  SIOCGIFNETMASK
+				if (ioctl(sockfd, SIOCGIFNETMASK, &ifrcopy) < 0) {
+					if (errno == EADDRNOTAVAIL) {
+						/* 
+						 * If the main interface is configured with no IP address but 
+						 * an alias interface exists with an IP address, you get 
+						 * EADDRNOTAVAIL for the main interface 
+						 */
+						free(ifi->ifi_addr);
+						free(ifi);
+						ifipnext  = ifiptr; 
+						*ifipnext = ifipold;
+						continue;
+					} else {
+						goto gotError;
+					}				
+				}
+
+				ifi->ifi_netmask = (struct sockaddr*)calloc(1, sizeof(struct sockaddr_in));
+				if (ifi->ifi_netmask == NULL) goto gotError;
+				sinptr = (struct sockaddr_in *) &ifrcopy.ifr_addr;
+				/* The BSD ioctls (including Mac OS X) stick some weird values in for sin_len and sin_family */
+#ifndef NOT_HAVE_SA_LEN
+				sinptr->sin_len    = sizeof(struct sockaddr_in);
+#endif
+				sinptr->sin_family = AF_INET;
+				memcpy(ifi->ifi_netmask, sinptr, sizeof(struct sockaddr_in));
+#endif
+
+#ifdef  SIOCGIFBRDADDR
+                if (flags & IFF_BROADCAST) {
+                    if (ioctl(sockfd, SIOCGIFBRDADDR, &ifrcopy) < 0) {
+                        goto gotError;
+                    }
+                    sinptr = (struct sockaddr_in *) &ifrcopy.ifr_broadaddr;
+					/* The BSD ioctls (including Mac OS X) stick some weird values in for sin_len and sin_family */
+#ifndef NOT_HAVE_SA_LEN
+					sinptr->sin_len    = sizeof( struct sockaddr_in );
+#endif
+					sinptr->sin_family = AF_INET;
+                    ifi->ifi_brdaddr = (struct sockaddr*)calloc(1, sizeof(struct sockaddr_in));
+                    if (ifi->ifi_brdaddr == NULL) {
+                        goto gotError;
+                    }
+                    memcpy(ifi->ifi_brdaddr, sinptr, sizeof(struct sockaddr_in));
+                }
+#endif
+
+#ifdef  SIOCGIFDSTADDR
+                if (flags & IFF_POINTOPOINT) {
+                    if (ioctl(sockfd, SIOCGIFDSTADDR, &ifrcopy) < 0) {
+                        goto gotError;
+                    }
+                    sinptr = (struct sockaddr_in *) &ifrcopy.ifr_dstaddr;
+                    /* The BSD ioctls (including Mac OS X) stick some weird values in for sin_len and sin_family */
+#ifndef NOT_HAVE_SA_LEN
+					sinptr->sin_len    = sizeof( struct sockaddr_in );
+#endif
+					sinptr->sin_family = AF_INET;
+                    ifi->ifi_dstaddr = (struct sockaddr*)calloc(1, sizeof(struct sockaddr_in));
+                    if (ifi->ifi_dstaddr == NULL) {
+                        goto gotError;
+                    }
+                    memcpy(ifi->ifi_dstaddr, sinptr, sizeof(struct sockaddr_in));
+                }
+#endif
+            }
+            break;
+
+#if defined(AF_INET6) && HAVE_IPV6
+        case AF_INET6:
+            sinptr6 = (struct sockaddr_in6 *) &ifr->ifr_addr;
+            if (ifi->ifi_addr == NULL) {
+                ifi->ifi_addr = calloc(1, sizeof(struct sockaddr_in6));
+                if (ifi->ifi_addr == NULL) {
+                    goto gotError;
+                }
+                
+                /* Some platforms (*BSD) inject the prefix in IPv6LL addresses */
+                /* We need to strip that out */
+                if (IN6_IS_ADDR_LINKLOCAL(&sinptr6->sin6_addr))
+                	sinptr6->sin6_addr.s6_addr[2] = sinptr6->sin6_addr.s6_addr[3] = 0;
+                memcpy(ifi->ifi_addr, sinptr6, sizeof(struct sockaddr_in6));
+
+#ifdef  SIOCGIFNETMASK_IN6
+				{
+				struct in6_ifreq ifr6;
+				if (sockf6 == -1)
+					sockf6 = socket(AF_INET6, SOCK_DGRAM, 0);
+				memset(&ifr6, 0, sizeof(ifr6));
+				memcpy(&ifr6.ifr_name,           &ifr->ifr_name, sizeof(ifr6.ifr_name          ));
+				memcpy(&ifr6.ifr_ifru.ifru_addr, &ifr->ifr_addr, sizeof(ifr6.ifr_ifru.ifru_addr));
+				if (ioctl(sockf6, SIOCGIFNETMASK_IN6, &ifr6) < 0) {
+					if (errno == EADDRNOTAVAIL) {
+						/* 
+						 * If the main interface is configured with no IP address but 
+						 * an alias interface exists with an IP address, you get 
+						 * EADDRNOTAVAIL for the main interface 
+						 */
+						free(ifi->ifi_addr);
+						free(ifi);
+						ifipnext  = ifiptr; 
+						*ifipnext = ifipold;
+						continue;
+					} else {
+						goto gotError;
+					}				
+				}
+				ifi->ifi_netmask = (struct sockaddr*)calloc(1, sizeof(struct sockaddr_in6));
+				if (ifi->ifi_netmask == NULL) goto gotError;
+				sinptr6 = (struct sockaddr_in6 *) &ifr6.ifr_ifru.ifru_addr;
+				memcpy(ifi->ifi_netmask, sinptr6, sizeof(struct sockaddr_in6));
+				}
+#endif
+            }
+            break;
+#endif
+
+        default:
+            break;
+        }
+    }
+    goto done;
+    
+gotError:
+    if (ifihead != NULL) {
+        free_ifi_info(ifihead);
+        ifihead = NULL;
+    }
+
+done:
+    if (buf != NULL) {
+        free(buf);
+    }
+    if (sockfd != -1) {
+        junk = close(sockfd);
+        assert(junk == 0);
+    }
+    if (sockf6 != -1) {
+        junk = close(sockf6);
+        assert(junk == 0);
+    }
+    return(ifihead);    /* pointer to first structure in linked list */
+}
+/* end get_ifi_info3 */
+
+/* include free_ifi_info */
+void
+free_ifi_info(struct ifi_info *ifihead)
+{
+    struct ifi_info *ifi, *ifinext;
+
+    for (ifi = ifihead; ifi != NULL; ifi = ifinext) {
+        if (ifi->ifi_addr != NULL)
+            free(ifi->ifi_addr);
+        if (ifi->ifi_netmask != NULL)
+            free(ifi->ifi_netmask);
+        if (ifi->ifi_brdaddr != NULL)
+            free(ifi->ifi_brdaddr);
+        if (ifi->ifi_dstaddr != NULL)
+            free(ifi->ifi_dstaddr);
+        ifinext = ifi->ifi_next;    /* can't fetch ifi_next after free() */
+        free(ifi);                  /* the ifi_info{} itself */
+    }
+}
+/* end free_ifi_info */
+
+ssize_t 
+recvfrom_flags(int fd, void *ptr, size_t nbytes, int *flagsp,
+               struct sockaddr *sa, socklen_t *salenptr, struct my_in_pktinfo *pktp, u_char *ttl)
+{
+    struct msghdr   msg;
+    struct iovec    iov[1];
+    ssize_t         n;
+
+#ifdef CMSG_FIRSTHDR
+    struct cmsghdr  *cmptr;
+    union {
+      struct cmsghdr    cm;
+      char              control[1024];
+    } control_un;
+
+	*ttl = 255;			// If kernel fails to provide TTL data then assume the TTL was 255 as it should be
+
+    msg.msg_control = control_un.control;
+    msg.msg_controllen = sizeof(control_un.control);
+    msg.msg_flags = 0;
+#else
+    memset(&msg, 0, sizeof(msg));   /* make certain msg_accrightslen = 0 */
+#endif /* CMSG_FIRSTHDR */
+
+    msg.msg_name = (char *) sa;
+    msg.msg_namelen = *salenptr;
+    iov[0].iov_base = (char *)ptr;
+    iov[0].iov_len = nbytes;
+    msg.msg_iov = iov;
+    msg.msg_iovlen = 1;
+
+    if ( (n = recvmsg(fd, &msg, *flagsp)) < 0)
+        return(n);
+
+    *salenptr = msg.msg_namelen;    /* pass back results */
+    if (pktp) {
+        /* 0.0.0.0, i/f = -1 */
+        /* We set the interface to -1 so that the caller can 
+           tell whether we returned a meaningful value or 
+           just some default.  Previously this code just 
+           set the value to 0, but I'm concerned that 0 
+           might be a valid interface value.
+        */
+        memset(pktp, 0, sizeof(struct my_in_pktinfo));
+        pktp->ipi_ifindex = -1;
+    }
+/* end recvfrom_flags1 */
+
+/* include recvfrom_flags2 */
+#ifndef CMSG_FIRSTHDR
+	#warning CMSG_FIRSTHDR not defined. Will not be able to determine destination address, received interface, etc.
+    *flagsp = 0;                    /* pass back results */
+    return(n);
+#else
+
+    *flagsp = msg.msg_flags;        /* pass back results */
+    if (msg.msg_controllen < (socklen_t)sizeof(struct cmsghdr) ||
+        (msg.msg_flags & MSG_CTRUNC) || pktp == NULL)
+        return(n);
+
+    for (cmptr = CMSG_FIRSTHDR(&msg); cmptr != NULL;
+         cmptr = CMSG_NXTHDR(&msg, cmptr)) {
+
+#ifdef  IP_PKTINFO
+#if in_pktinfo_definition_is_missing
+struct in_pktinfo
+{
+        int             ipi_ifindex;
+        struct in_addr  ipi_spec_dst;
+        struct in_addr  ipi_addr;
+};
+#endif
+        if (cmptr->cmsg_level == IPPROTO_IP && 
+            cmptr->cmsg_type == IP_PKTINFO) {
+            struct in_pktinfo *tmp;
+            struct sockaddr_in *sin = (struct sockaddr_in*)&pktp->ipi_addr;
+            
+            tmp = (struct in_pktinfo *) CMSG_DATA(cmptr);
+            sin->sin_family = AF_INET;
+            sin->sin_addr = tmp->ipi_addr;
+            sin->sin_port = 0;
+            pktp->ipi_ifindex = tmp->ipi_ifindex;
+            continue;
+        }
+#endif
+
+#ifdef  IP_RECVDSTADDR
+        if (cmptr->cmsg_level == IPPROTO_IP &&
+            cmptr->cmsg_type == IP_RECVDSTADDR) {
+            struct sockaddr_in *sin = (struct sockaddr_in*)&pktp->ipi_addr;
+            
+            sin->sin_family = AF_INET;
+            sin->sin_addr = *(struct in_addr*)CMSG_DATA(cmptr);
+            sin->sin_port = 0;
+            continue;
+        }
+#endif
+
+#ifdef  IP_RECVIF
+        if (cmptr->cmsg_level == IPPROTO_IP &&
+            cmptr->cmsg_type == IP_RECVIF) {
+            struct sockaddr_dl  *sdl = (struct sockaddr_dl *) CMSG_DATA(cmptr);
+#ifndef HAVE_BROKEN_RECVIF_NAME
+            int nameLen = (sdl->sdl_nlen < IFI_NAME - 1) ? sdl->sdl_nlen : (IFI_NAME - 1);
+            strncpy(pktp->ipi_ifname, sdl->sdl_data, nameLen);
+#endif
+            pktp->ipi_ifindex = sdl->sdl_index;
+#ifdef HAVE_BROKEN_RECVIF_NAME
+			if (sdl->sdl_index == 0) {
+				pktp->ipi_ifindex = *(uint_t*)sdl;
+			}
+#endif            
+            assert(pktp->ipi_ifname[IFI_NAME - 1] == 0);
+            // null terminated because of memset above
+            continue;
+        }
+#endif
+
+#ifdef  IP_RECVTTL
+        if (cmptr->cmsg_level == IPPROTO_IP &&
+            cmptr->cmsg_type == IP_RECVTTL) {
+			*ttl = *(u_char*)CMSG_DATA(cmptr);
+            continue;
+        }
+        else if (cmptr->cmsg_level == IPPROTO_IP &&
+            cmptr->cmsg_type == IP_TTL) {		// some implementations seem to send IP_TTL instead of IP_RECVTTL
+			*ttl = *(int*)CMSG_DATA(cmptr);
+            continue;
+        }
+#endif
+
+#if defined(IPV6_PKTINFO) && HAVE_IPV6 
+		if (cmptr->cmsg_level == IPPROTO_IPV6 && 
+            cmptr->cmsg_type  == IPV6_2292_PKTINFO) {
+            struct sockaddr_in6 *sin6 = (struct sockaddr_in6*)&pktp->ipi_addr;
+			struct in6_pktinfo *ip6_info = (struct in6_pktinfo*)CMSG_DATA(cmptr);
+			
+            sin6->sin6_family   = AF_INET6;
+#ifndef NOT_HAVE_SA_LEN
+            sin6->sin6_len      = sizeof(*sin6);
+#endif
+            sin6->sin6_addr     = ip6_info->ipi6_addr;
+            sin6->sin6_flowinfo = 0;
+            sin6->sin6_scope_id = 0;
+            sin6->sin6_port     = 0;
+			pktp->ipi_ifindex   = ip6_info->ipi6_ifindex;
+            continue;
+        }
+#endif
+
+#if defined(IPV6_HOPLIMIT) && HAVE_IPV6
+        if (cmptr->cmsg_level == IPPROTO_IPV6 && 
+            cmptr->cmsg_type == IPV6_2292_HOPLIMIT) {
+			*ttl = *(int*)CMSG_DATA(cmptr);
+            continue;
+        }
+#endif
+        assert(0);  // unknown ancillary data
+    }
+    return(n);
+#endif /* CMSG_FIRSTHDR */
+}
+
+// **********************************************************************************************
+
+// daemonize the process. Adapted from "Unix Network Programming" vol 1 by Stevens, section 12.4.
+// Returns 0 on success, -1 on failure.
+
+#ifdef NOT_HAVE_DAEMON
+#include <fcntl.h>
+#include <sys/stat.h>
+#include <sys/signal.h>
+
+int daemon(int nochdir, int noclose)
+    {
+	switch (fork())
+		{
+		case -1: return (-1);	// Fork failed
+		case 0: break;		// Child -- continue
+		default: _exit(0);	// Parent -- exit
+		}
+
+	if (setsid() == -1) return(-1);
+
+	signal(SIGHUP, SIG_IGN);
+
+	switch (fork())				// Fork again, primarily for reasons of Unix trivia
+		{
+		case -1: return (-1);	// Fork failed
+		case 0:  break;			// Child -- continue
+		default: _exit(0);		// Parent -- exit
+		}
+
+	if (!nochdir) (void)chdir("/");
+	umask(0);
+
+	if (!noclose)
+		{
+		int fd = open("/dev/null", O_RDWR, 0);
+		if (fd != -1)
+			{
+			// Avoid unnecessarily duplicating a file descriptor to itself
+			if (fd != STDIN_FILENO) (void)dup2(fd, STDIN_FILENO);
+			if (fd != STDOUT_FILENO) (void)dup2(fd, STDOUT_FILENO);
+			if (fd != STDERR_FILENO) (void)dup2(fd, STDERR_FILENO);
+			if (fd != STDIN_FILENO && fd != STDOUT_FILENO && fd != STDERR_FILENO) 
+				(void)close (fd);
+			}
+		}
+	return (0);
+    }
+#endif /* NOT_HAVE_DAEMON */
diff --git a/mdnsresponder/mDNSPosix/mDNSUNP.h b/mdnsresponder/mDNSPosix/mDNSUNP.h
new file mode 100755
index 0000000..e9c8e22
--- /dev/null
+++ b/mdnsresponder/mDNSPosix/mDNSUNP.h
@@ -0,0 +1,130 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+#ifndef __mDNSUNP_h
+#define __mDNSUNP_h
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+
+#ifdef HAVE_LINUX
+#include <linux/socket.h>
+#define IPV6_2292_PKTINFO  IPV6_2292PKTINFO
+#define IPV6_2292_HOPLIMIT IPV6_2292HOPLIMIT
+#else
+// The following are the supported non-linux posix OSes -
+// netbsd, freebsd and openbsd.
+#if HAVE_IPV6
+#define IPV6_2292_PKTINFO  19
+#define IPV6_2292_HOPLIMIT 20
+#endif 
+#endif
+
+#ifdef  __cplusplus
+    extern "C" {
+#endif
+
+#ifdef NOT_HAVE_SOCKLEN_T
+    typedef unsigned int socklen_t;
+#endif
+
+#if !defined(_SS_MAXSIZE) && !defined(__ANDROID__)
+#if HAVE_IPV6
+#define sockaddr_storage sockaddr_in6
+#else
+#define sockaddr_storage sockaddr
+#endif // HAVE_IPV6
+#endif // !defined(_SS_MAXSIZE) && !defined(__ANDROID__)
+
+#ifndef NOT_HAVE_SA_LEN
+#define GET_SA_LEN(X) (sizeof(struct sockaddr) > ((struct sockaddr*)&(X))->sa_len ? \
+                       sizeof(struct sockaddr) : ((struct sockaddr*)&(X))->sa_len   )
+#elif HAVE_IPV6
+#define GET_SA_LEN(X) (((struct sockaddr*)&(X))->sa_family == AF_INET  ? sizeof(struct sockaddr_in) : \
+                       ((struct sockaddr*)&(X))->sa_family == AF_INET6 ? sizeof(struct sockaddr_in6) : sizeof(struct sockaddr))
+#else
+#define GET_SA_LEN(X) (((struct sockaddr*)&(X))->sa_family == AF_INET ? sizeof(struct sockaddr_in) : sizeof(struct sockaddr))
+#endif
+
+#define IFI_NAME    16          /* same as IFNAMSIZ in <net/if.h> */
+#define IFI_HADDR    8          /* allow for 64-bit EUI-64 in future */
+
+// Renamed from my_in_pktinfo because in_pktinfo is used by Linux.
+
+struct my_in_pktinfo {
+    struct sockaddr_storage ipi_addr;
+    int                     ipi_ifindex;            /* received interface index */
+    char                    ipi_ifname[IFI_NAME];   /* received interface name  */
+};
+
+/* From the text (Stevens, section 20.2): */
+/* 'As an example of recvmsg we will write a function named recvfrom_flags that */
+/* is similar to recvfrom but also returns: */
+/*	1. the returned msg_flags value, */
+/*	2. the destination addres of the received datagram (from the IP_RECVDSTADDR socket option, and */
+/*	3. the index of the interface on which the datagram was received (the IP_RECVIF socket option).' */
+extern ssize_t recvfrom_flags(int fd, void *ptr, size_t nbytes, int *flagsp,
+               struct sockaddr *sa, socklen_t *salenptr, struct my_in_pktinfo *pktp, u_char *ttl);
+
+struct ifi_info {
+  char    ifi_name[IFI_NAME];   /* interface name, null terminated */
+  u_char  ifi_haddr[IFI_HADDR]; /* hardware address */
+  u_short ifi_hlen;             /* #bytes in hardware address: 0, 6, 8 */
+  short   ifi_flags;            /* IFF_xxx constants from <net/if.h> */
+  short   ifi_myflags;          /* our own IFI_xxx flags */
+  int     ifi_index;            /* interface index */
+  struct sockaddr  *ifi_addr;   /* primary address */
+  struct sockaddr  *ifi_netmask;
+  struct sockaddr  *ifi_brdaddr;/* broadcast address */
+  struct sockaddr  *ifi_dstaddr;/* destination address */
+  struct ifi_info  *ifi_next;   /* next of these structures */
+};
+
+#if defined(AF_INET6) && HAVE_IPV6 && HAVE_LINUX
+#define PROC_IFINET6_PATH "/proc/net/if_inet6"
+extern struct ifi_info  *get_ifi_info_linuxv6(int family, int doaliases);
+#endif
+	
+#if defined(AF_INET6) && HAVE_IPV6
+#define INET6_ADDRSTRLEN 46 /*Maximum length of IPv6 address */
+#endif
+	
+
+	
+#define IFI_ALIAS   1           /* ifi_addr is an alias */
+
+/* From the text (Stevens, section 16.6): */
+/* 'Since many programs need to know all the interfaces on a system, we will develop a */
+/* function of our own named get_ifi_info that returns a linked list of structures, one */
+/* for each interface that is currently "up."' */
+extern struct ifi_info  *get_ifi_info(int family, int doaliases);
+
+/* 'The free_ifi_info function, which takes a pointer that was */
+/* returned by get_ifi_info and frees all the dynamic memory.' */
+extern void             free_ifi_info(struct ifi_info *);
+
+#ifdef NOT_HAVE_DAEMON
+extern int daemon(int nochdir, int noclose);
+#endif
+
+#ifdef  __cplusplus
+    }
+#endif
+
+#endif
diff --git a/mdnsresponder/mDNSPosix/mdnsd.sh b/mdnsresponder/mDNSPosix/mdnsd.sh
new file mode 100644
index 0000000..14fef9b
--- /dev/null
+++ b/mdnsresponder/mDNSPosix/mdnsd.sh
@@ -0,0 +1,73 @@
+#!/bin/sh
+# Emacs settings: -*- tab-width: 4 -*-
+#
+# Copyright (c) 2002-2006 Apple Computer, Inc. All rights reserved.
+#
+# Licensed 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.
+#
+# Linux /etc/init.d script to start/stop the mdnsd daemon.
+#
+# The following lines are used by the *BSD rcorder system to decide
+# the order it's going to run the rc.d scripts at startup time.
+# PROVIDE: mdnsd
+# REQUIRE: NETWORKING
+
+if [ -r /usr/sbin/mdnsd ]; then
+    DAEMON=/usr/sbin/mdnsd
+else
+    DAEMON=/usr/local/sbin/mdnsd
+fi
+
+test -r $DAEMON || exit 0
+
+# Some systems have start-stop-daemon, some don't. 
+if [ -r /sbin/start-stop-daemon ]; then
+	START="start-stop-daemon --start --quiet --exec"
+	# Suse Linux doesn't work with symbolic signal names, but we really don't need
+	# to specify "-s TERM" since SIGTERM (15) is the default stop signal anway
+	# STOP="start-stop-daemon --stop -s TERM --quiet --oknodo --exec"
+	STOP="start-stop-daemon --stop --quiet --oknodo --exec"
+else
+	killmdnsd() {
+		kill -TERM `cat /var/run/mdnsd.pid`
+	}
+	START=
+	STOP=killmdnsd
+fi
+
+case "$1" in
+    start)
+	echo -n "Starting Apple Darwin Multicast DNS / DNS Service Discovery daemon:"
+	echo -n " mdnsd"
+        $START $DAEMON
+	echo "."
+	;;
+    stop)
+        echo -n "Stopping Apple Darwin Multicast DNS / DNS Service Discovery daemon:"
+        echo -n " mdnsd" ; $STOP $DAEMON
+        echo "."
+	;;
+    reload|restart|force-reload)
+		echo -n "Restarting Apple Darwin Multicast DNS / DNS Service Discovery daemon:"
+		$STOP $DAEMON
+		sleep 1
+		$START $DAEMON
+		echo -n " mdnsd"
+	;;
+    *)
+	echo "Usage: /etc/init.d/mDNS {start|stop|reload|restart}"
+	exit 1
+	;;
+esac
+
+exit 0
diff --git a/mdnsresponder/mDNSPosix/nss_ReadMe.txt b/mdnsresponder/mDNSPosix/nss_ReadMe.txt
new file mode 100755
index 0000000..2e3023c
--- /dev/null
+++ b/mdnsresponder/mDNSPosix/nss_ReadMe.txt
@@ -0,0 +1,125 @@
+# Readme for libnss_mdns
+
+Andrew White <Andrew.White@nicta.com.au>
+June 2004
+
+Before using this software, see "Licensing" at bottom of this file.
+
+
+# Introduction
+
+This code implements a module for the Name Service Switch to perform
+hostname lookups using the Darwin mDNSResponder / mdnsd.  This code has
+been tested on Debian and Redhat Linux.  It may work on other platforms. 
+It *will not* work on Darwin or Mac OS X - the necessary functionality is
+already built into the operation system.
+
+
+# Building and Installing:
+
+See "ReadMe.txt" for instructions on building and installing.
+
+When you run "make install" as described in that file:
+o libnss_mdns-0.2.so and libnss_mdns.so.2 are installed in /lib,
+o manual pages libnss_mdns(8) and nss_mdns.conf(5) are installed,
+o nss_mdns.conf is installed in /etc, and
+o /etc/nsswitch.conf is modified to add "mdns" on the "hosts:" line
+
+This will cause dns lookups to be passed via mdnsd before trying the dns.
+
+
+# Testing
+
+For most purposes, 'ping myhostname.local' will tell you if mdns is
+working.  If MDNS_VERBOSE was set in nss_mdns.c during compilation then
+lots of chatty debug messages will be dumped to LOG_DEBUG in syslog. 
+Otherwise, nss_mdns will only log if something isn't behaving quite right.
+
+
+# Implementation details
+
+libnss_mdns provides alternative back-end implementations of the libc
+functions gethostbyname, gethostbyname2 and gethostbyaddr, using the Name
+Service Switch mechanism.  More information on writing nsswitch modules
+can be found via 'info libc "Name Service Switch"', if installed.
+
+
+# Licensing
+
+This software is licensed under the NICTA Public Software License, version
+1.0, printed below:
+
+NICTA Public Software Licence
+Version 1.0
+
+Copyright © 2004 National ICT Australia Ltd
+
+All rights reserved.
+
+By this licence, National ICT Australia Ltd (NICTA) grants permission,
+free of charge, to any person who obtains a copy of this software
+and any associated documentation files ("the Software") to use and
+deal with the Software in source code and binary forms without
+restriction, with or without modification, and to permit persons
+to whom the Software is furnished to do so, provided that the
+following conditions are met:
+
+- Redistributions of source code must retain the above copyright
+  notice, this list of conditions and the following disclaimers.
+- Redistributions in binary form must reproduce the above copyright
+  notice, this list of conditions and the following disclaimers in
+  the documentation and/or other materials provided with the
+  distribution.
+- The name of NICTA may not be used to endorse or promote products
+  derived from this Software without specific prior written permission.
+
+EXCEPT AS EXPRESSLY STATED IN THIS LICENCE AND TO THE FULL EXTENT
+PERMITTED BY APPLICABLE LAW, THE SOFTWARE IS PROVIDED "AS-IS" AND
+NICTA MAKES NO REPRESENTATIONS, WARRANTIES OR CONDITIONS OF ANY
+KIND, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, ANY
+REPRESENTATIONS, WARRANTIES OR CONDITIONS REGARDING THE CONTENTS
+OR ACCURACY OF THE SOFTWARE, OR OF TITLE, MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, THE ABSENCE OF LATENT
+OR OTHER DEFECTS, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR
+NOT DISCOVERABLE.
+
+TO THE FULL EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT WILL
+NICTA BE LIABLE ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
+NEGLIGENCE) FOR ANY LOSS OR DAMAGE WHATSOEVER, INCLUDING (WITHOUT
+LIMITATION) LOSS OF PRODUCTION OR OPERATION TIME, LOSS, DAMAGE OR
+CORRUPTION OF DATA OR RECORDS; OR LOSS OF ANTICIPATED SAVINGS,
+OPPORTUNITY, REVENUE, PROFIT OR GOODWILL, OR OTHER ECONOMIC LOSS;
+OR ANY SPECIAL, INCIDENTAL, INDIRECT, CONSEQUENTIAL, PUNITIVE OR
+EXEMPLARY DAMAGES ARISING OUT OF OR IN CONNECTION WITH THIS LICENCE,
+THE SOFTWARE OR THE USE OF THE SOFTWARE, EVEN IF NICTA HAS BEEN
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+If applicable legislation implies warranties or conditions, or
+imposes obligations or liability on NICTA in respect of the Software
+that cannot be wholly or partly excluded, restricted or modified,
+NICTA's liability is limited, to the full extent permitted by the
+applicable legislation, at its option, to:
+
+a. in the case of goods, any one or more of the following:
+  i.   the replacement of the goods or the supply of equivalent goods;
+  ii.  the repair of the goods;
+  iii. the payment of the cost of replacing the goods or of acquiring
+       equivalent goods;
+  iv.  the payment of the cost of having the goods repaired; or
+b. in the case of services:
+  i.   the supplying of the services again; or 
+  ii.  the payment of the cost of having the services supplied
+       again.
+
+
+# Links:
+
+NICTA
+	http://www.nicta.com.au/
+Darwin
+	http://developer.apple.com/darwin/
+DNS service discovery and link-local
+	http://http://zeroconf.org/
+	http://http://multicastdns.org/
+	http://http://dns-sd.org/
+	http://http://dotlocal.org/
diff --git a/mdnsresponder/mDNSPosix/nss_mdns.c b/mdnsresponder/mDNSPosix/nss_mdns.c
new file mode 100755
index 0000000..5af2f8e
--- /dev/null
+++ b/mdnsresponder/mDNSPosix/nss_mdns.c
@@ -0,0 +1,2723 @@
+/*
+NICTA Public Software Licence
+Version 1.0
+
+Copyright © 2004 National ICT Australia Ltd
+
+All rights reserved.
+
+By this licence, National ICT Australia Ltd (NICTA) grants permission,
+free of charge, to any person who obtains a copy of this software
+and any associated documentation files ("the Software") to use and
+deal with the Software in source code and binary forms without
+restriction, with or without modification, and to permit persons
+to whom the Software is furnished to do so, provided that the
+following conditions are met:
+
+- Redistributions of source code must retain the above copyright
+  notice, this list of conditions and the following disclaimers.
+- Redistributions in binary form must reproduce the above copyright
+  notice, this list of conditions and the following disclaimers in
+  the documentation and/or other materials provided with the
+  distribution.
+- The name of NICTA may not be used to endorse or promote products
+  derived from this Software without specific prior written permission.
+
+EXCEPT AS EXPRESSLY STATED IN THIS LICENCE AND TO THE FULL EXTENT
+PERMITTED BY APPLICABLE LAW, THE SOFTWARE IS PROVIDED "AS-IS" AND
+NICTA MAKES NO REPRESENTATIONS, WARRANTIES OR CONDITIONS OF ANY
+KIND, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, ANY
+REPRESENTATIONS, WARRANTIES OR CONDITIONS REGARDING THE CONTENTS
+OR ACCURACY OF THE SOFTWARE, OR OF TITLE, MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, THE ABSENCE OF LATENT
+OR OTHER DEFECTS, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR
+NOT DISCOVERABLE.
+
+TO THE FULL EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT WILL
+NICTA BE LIABLE ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
+NEGLIGENCE) FOR ANY LOSS OR DAMAGE WHATSOEVER, INCLUDING (WITHOUT
+LIMITATION) LOSS OF PRODUCTION OR OPERATION TIME, LOSS, DAMAGE OR
+CORRUPTION OF DATA OR RECORDS; OR LOSS OF ANTICIPATED SAVINGS,
+OPPORTUNITY, REVENUE, PROFIT OR GOODWILL, OR OTHER ECONOMIC LOSS;
+OR ANY SPECIAL, INCIDENTAL, INDIRECT, CONSEQUENTIAL, PUNITIVE OR
+EXEMPLARY DAMAGES ARISING OUT OF OR IN CONNECTION WITH THIS LICENCE,
+THE SOFTWARE OR THE USE OF THE SOFTWARE, EVEN IF NICTA HAS BEEN
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+
+If applicable legislation implies warranties or conditions, or
+imposes obligations or liability on NICTA in respect of the Software
+that cannot be wholly or partly excluded, restricted or modified,
+NICTA's liability is limited, to the full extent permitted by the
+applicable legislation, at its option, to:
+
+a. in the case of goods, any one or more of the following:
+  i.   the replacement of the goods or the supply of equivalent goods;
+  ii.  the repair of the goods;
+  iii. the payment of the cost of replacing the goods or of acquiring
+       equivalent goods;
+  iv.  the payment of the cost of having the goods repaired; or
+b. in the case of services:
+  i.   the supplying of the services again; or 
+  ii.  the payment of the cost of having the services supplied
+       again.
+ */
+
+/*
+	NSSwitch Implementation of mDNS interface.
+	
+	Andrew White (Andrew.White@nicta.com.au)
+	May 2004
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <string.h>
+#include <errno.h>
+#include <syslog.h>
+#include <pthread.h>
+#include <ctype.h>
+
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/socket.h>
+
+#include <netinet/in.h>
+
+#include <arpa/inet.h>
+#define BIND_8_COMPAT 1
+#include <arpa/nameser.h>
+
+#include <dns_sd.h>
+
+
+//----------
+// Public functions
+
+/*
+	Count the number of dots in a name string.
+ */
+int
+count_dots (const char * name);
+
+
+/*
+	Test whether a domain name is local.
+
+	Returns
+		1 if name ends with ".local" or ".local."
+		0 otherwise
+ */
+int
+islocal (const char * name);
+
+
+/*
+	Format an address structure as a string appropriate for DNS reverse (PTR)
+	lookup, based on address type.
+	
+	Parameters
+		prefixlen
+			Prefix length, in bits.  When formatting, this will be rounded up
+			to the nearest appropriate size.  If -1, assume maximum.
+		buf
+			Output buffer.  Must be long enough to hold largest possible
+			output.
+	Returns
+		Pointer to (first character of) output buffer,
+		or NULL on error.
+ */
+char *
+format_reverse_addr (int af, const void * addr, int prefixlen, char * buf);
+
+
+/*
+	Format an address structure as a string appropriate for DNS reverse (PTR)
+	lookup for AF_INET.  Output is in .in-addr.arpa domain.
+	
+	Parameters
+		prefixlen
+			Prefix length, in bits.  When formatting, this will be rounded up
+			to the nearest byte (8).  If -1, assume 32.
+		buf
+			Output buffer.  Must be long enough to hold largest possible
+			output.  For AF_INET, this is 29 characters (including null).
+	Returns
+		Pointer to (first character of) output buffer,
+		or NULL on error.
+ */
+char *
+format_reverse_addr_in (
+	const struct in_addr * addr,
+	int prefixlen,
+	char * buf
+);
+#define DNS_PTR_AF_INET_SIZE 29
+
+/*
+	Format an address structure as a string appropriate for DNS reverse (PTR)
+	lookup for AF_INET6.  Output is in .ip6.arpa domain.
+	
+	Parameters
+		prefixlen
+			Prefix length, in bits.  When formatting, this will be rounded up
+			to the nearest nibble (4).  If -1, assume 128.
+		buf
+			Output buffer.  Must be long enough to hold largest possible
+			output.  For AF_INET6, this is 72 characters (including null).
+	Returns
+		Pointer to (first character of) output buffer,
+		or NULL on error.
+ */
+char *
+format_reverse_addr_in6 (
+	const struct in6_addr * addr,
+	int prefixlen,
+	char * buf
+);
+#define DNS_PTR_AF_INET6_SIZE 72
+
+
+/*
+	Compare whether the given dns name has the given domain suffix.
+	A single leading '.' on the name or leading or trailing '.' on the
+	domain is ignored for the purposes of the comparison.
+	Multiple leading or trailing '.'s are an error.  Other DNS syntax
+	errors are not checked for.  The comparison is case insensitive.
+	
+	Returns
+		1 on success (match)
+		0 on failure (no match)
+		< 0 on error
+ */
+int
+cmp_dns_suffix (const char * name, const char * domain);
+enum
+{
+	CMP_DNS_SUFFIX_SUCCESS = 1,
+	CMP_DNS_SUFFIX_FAILURE = 0,
+	CMP_DNS_SUFFIX_BAD_NAME = 1,
+	CMP_DNS_SUFFIX_BAD_DOMAIN = -2
+};
+
+typedef int ns_type_t;
+typedef int ns_class_t;
+
+/*
+	Convert a DNS resource record (RR) code to an address family (AF) code.
+	
+	Parameters
+		rrtype
+			resource record type (from nameser.h)
+
+	Returns
+		Appropriate AF code (from socket.h), or AF_UNSPEC if an appropriate
+		mapping couldn't be determined
+ */
+int
+rr_to_af (ns_type_t rrtype);
+
+
+/*
+	Convert an address family (AF) code to a DNS resource record (RR) code.
+	
+	Parameters
+		int
+			address family code (from socket.h)
+	Returns
+		Appropriate RR code (from nameser.h), or ns_t_invalid if an appropriate
+		mapping couldn't be determined
+ */
+ns_type_t
+af_to_rr (int af);
+
+
+/*
+	Convert a string to an address family (case insensitive).
+
+	Returns
+		Matching AF code, or AF_UNSPEC if no match found.
+ */
+int
+str_to_af (const char * str);
+
+
+/*
+	Convert a string to an ns_class_t (case insensitive).
+
+	Returns
+		Matching ns_class_t, or ns_c_invalid if no match found.
+ */
+ns_class_t
+str_to_ns_class (const char * str);
+
+
+/*
+	Convert a string to an ns_type_t (case insensitive).
+
+	Returns
+		Matching ns_type_t, or ns_t_invalid if no match found.
+ */
+ns_type_t
+str_to_ns_type (const char * str);
+
+
+/*
+	Convert an address family code to a string.
+
+	Returns
+		String representation of AF,
+		or NULL if address family unrecognised or invalid.
+ */
+const char *
+af_to_str (int in);
+
+
+/*
+	Convert an ns_class_t code to a string.
+
+	Returns
+		String representation of ns_class_t,
+		or NULL if ns_class_t unrecognised or invalid.
+ */
+const char *
+ns_class_to_str (ns_class_t in);
+
+
+/*
+	Convert an ns_type_t code to a string.
+
+	Returns
+		String representation of ns_type_t,
+		or NULL if ns_type_t unrecognised or invalid.
+ */
+const char *
+ns_type_to_str (ns_type_t in);
+
+
+/*
+	Convert DNS rdata in label format (RFC1034, RFC1035) to a name.
+	
+	On error, partial data is written to name (as much as was successfully
+	processed) and an error code is returned.  Errors include a name too
+	long for the buffer and a pointer in the label (which cannot be
+	resolved).
+	
+	Parameters
+		rdata
+			Rdata formatted as series of labels.
+		rdlen
+			Length of rdata buffer.
+		name
+			Buffer to store fully qualified result in.
+			By RFC1034 section 3.1, a 255 character buffer (256 characters
+			including null) is long enough for any legal name.
+		name_len
+			Number of characters available in name buffer, not including
+			trailing null.
+	
+	Returns
+		Length of name buffer (not including trailing null).
+		< 0 on error.
+		A return of 0 implies the empty domain.
+ */
+int
+dns_rdata_to_name (const char * rdata, int rdlen, char * name, int name_len);
+enum
+{
+	DNS_RDATA_TO_NAME_BAD_FORMAT = -1,
+		// Format is broken.  Usually because we ran out of data
+		// (according to rdata) before the labels said we should.
+	DNS_RDATA_TO_NAME_TOO_LONG = -2,
+		// The converted rdata is longer than the name buffer.
+	DNS_RDATA_TO_NAME_PTR = -3,
+		// The rdata contains a pointer.
+};
+
+#define DNS_LABEL_MAXLEN 63
+	// Maximum length of a single DNS label
+#define DNS_NAME_MAXLEN 256
+	// Maximum length of a DNS name
+
+//----------
+// Public types
+
+typedef int errcode_t;
+	// Used for 0 = success, non-zero = error code functions
+
+
+//----------
+// Public functions
+
+/*
+	Test whether a domain name is in a domain covered by nss_mdns.
+	The name is assumed to be fully qualified (trailing dot optional);
+	unqualified names will be processed but may return unusual results
+	if the unqualified prefix happens to match a domain suffix.
+	
+	Returns
+		 1 success
+		 0 failure
+		-1 error, check errno
+ */
+int
+config_is_mdns_suffix (const char * name);
+
+
+/*
+	Loads all relevant data from configuration file.  Other code should
+	rarely need to call this function, since all other public configuration
+	functions do so implicitly.  Once loaded, configuration info doesn't
+	change.
+	
+	Returns
+		0 configuration ready
+		non-zero configuration error code
+ */
+errcode_t
+init_config ();
+
+#define ENTNAME  hostent
+#define DATABASE "hosts"
+
+#include <nss.h>
+	// For nss_status
+#include <netdb.h>
+	// For hostent
+#include <sys/types.h>
+	// For size_t
+
+typedef enum nss_status nss_status;
+typedef struct hostent hostent;
+
+/*
+gethostbyname implementation
+
+	name:
+		name to look up
+	result_buf:
+		resulting entry
+	buf:
+		auxillary buffer
+	buflen:
+		length of auxillary buffer
+	errnop:
+		pointer to errno
+	h_errnop:
+		pointer to h_errno
+ */
+nss_status
+_nss_mdns_gethostbyname_r (
+	const char *name,
+	hostent * result_buf,
+	char *buf,
+	size_t buflen,
+	int *errnop,
+	int *h_errnop
+);
+
+
+/*
+gethostbyname2 implementation
+
+	name:
+		name to look up
+	af:
+		address family
+	result_buf:
+		resulting entry
+	buf:
+		auxillary buffer
+	buflen:
+		length of auxillary buffer
+	errnop:
+		pointer to errno
+	h_errnop:
+		pointer to h_errno
+ */
+nss_status
+_nss_mdns_gethostbyname2_r (
+	const char *name,
+	int af,
+	hostent * result_buf,
+	char *buf,
+	size_t buflen,
+	int *errnop,
+	int *h_errnop
+);
+
+
+/*
+gethostbyaddr implementation
+
+	addr:
+		address structure to look up
+	len:
+		length of address structure
+	af:
+		address family
+	result_buf:
+		resulting entry
+	buf:
+		auxillary buffer
+	buflen:
+		length of auxillary buffer
+	errnop:
+		pointer to errno
+	h_errnop:
+		pointer to h_errno
+ */
+nss_status
+_nss_mdns_gethostbyaddr_r (
+	const void *addr,
+	socklen_t len,
+	int af,
+	hostent * result_buf,
+	char *buf,
+	size_t buflen,
+	int *errnop,
+	int *h_errnop
+);
+
+
+//----------
+// Types and Constants
+
+const int MDNS_VERBOSE = 0;
+	// This enables verbose syslog messages
+	// If zero, only "imporant" messages will appear in syslog
+
+#define k_hostname_maxlen 256
+	// As per RFC1034 and RFC1035
+#define k_aliases_max 15
+#define k_addrs_max 15
+
+typedef struct buf_header
+{
+	char hostname [k_hostname_maxlen + 1];
+	char * aliases [k_aliases_max + 1];
+	char * addrs [k_addrs_max + 1];
+} buf_header_t;
+
+typedef struct result_map
+{
+	int done;
+	nss_status status;
+	hostent * hostent;
+	buf_header_t * header;
+	int aliases_count;
+	int addrs_count;
+	char * buffer;
+	int addr_idx;
+		// Index for addresses - grow from low end
+		// Index points to first empty space
+	int alias_idx;
+		// Index for aliases - grow from high end
+		// Index points to lowest entry
+	int r_errno;
+	int r_h_errno;
+} result_map_t;
+
+static const struct timeval
+	k_select_time = { 0, 500000 };
+		// 0 seconds, 500 milliseconds
+
+//----------
+// Local prototypes
+
+static nss_status
+mdns_gethostbyname2 (
+	const char *name,
+	int af,
+	hostent * result_buf,
+	char *buf,
+	size_t buflen,
+	int *errnop,
+	int *h_errnop
+);
+
+
+/*
+	Lookup name using mDNS server
+ */
+static nss_status
+mdns_lookup_name (
+	const char * fullname,
+	int af,
+	result_map_t * result
+);
+
+/*
+	Lookup address using mDNS server
+ */
+static nss_status
+mdns_lookup_addr (
+	const void * addr,
+	socklen_t len,
+	int af,
+	const char * addr_str,
+	result_map_t * result
+);
+
+
+/*
+	Handle incoming MDNS events
+ */
+static nss_status
+handle_events (DNSServiceRef sdref, result_map_t * result, const char * str);
+
+
+// Callback for mdns_lookup operations
+//DNSServiceQueryRecordReply mdns_lookup_callback;
+typedef void
+mdns_lookup_callback_t
+(
+	DNSServiceRef		sdref,
+	DNSServiceFlags		flags,
+	uint32_t			interface_index,
+	DNSServiceErrorType	error_code,
+	const char			*fullname,	  
+	uint16_t			rrtype,
+	uint16_t			rrclass,
+	uint16_t			rdlen,
+	const void			*rdata,
+	uint32_t			ttl,
+	void				*context
+);
+
+mdns_lookup_callback_t mdns_lookup_callback;
+
+
+static int
+init_result (
+	result_map_t * result,
+	hostent * result_buf,
+	char * buf,
+	size_t buflen
+);
+
+static int
+callback_body_ptr (
+	const char * fullname,
+	result_map_t * result,
+	int rdlen,
+	const void * rdata
+);
+
+static void *
+add_address_to_buffer (result_map_t * result, const void * data, int len);
+static char *
+add_alias_to_buffer (result_map_t * result, const char * data, int len);
+static char *
+add_hostname_len (result_map_t * result, const char * fullname, int len);
+static char *
+add_hostname_or_alias (result_map_t * result, const char * data, int len);
+
+static void *
+contains_address (result_map_t * result, const void * data, int len);
+static char *
+contains_alias (result_map_t * result, const char * data);
+
+
+static const char *
+is_applicable_name (
+	result_map_t * result,
+	const char * name,
+	char * lookup_name
+);
+
+static const char *
+is_applicable_addr (
+	result_map_t * result,
+	const void * addr,
+	int af,
+	char * addr_str
+);
+
+
+// Error code functions
+
+static nss_status
+set_err (result_map_t * result, nss_status status, int err, int herr);
+
+static nss_status set_err_notfound (result_map_t * result);
+static nss_status set_err_bad_hostname (result_map_t * result);
+static nss_status set_err_buf_too_small (result_map_t * result);
+static nss_status set_err_internal_resource_full (result_map_t * result);
+static nss_status set_err_system (result_map_t * result);
+static nss_status set_err_mdns_failed (result_map_t * result);
+static nss_status set_err_success (result_map_t * result);
+
+
+//----------
+// Global variables
+
+
+//----------
+// NSS functions
+
+nss_status
+_nss_mdns_gethostbyname_r (
+	const char *name,
+	hostent * result_buf,
+	char *buf,
+	size_t buflen,
+	int *errnop,
+	int *h_errnop
+)
+{
+	if (MDNS_VERBOSE)
+		syslog (LOG_DEBUG,
+			"mdns: Called nss_mdns_gethostbyname with %s",
+			name
+		);
+
+	return
+		mdns_gethostbyname2 (
+			name, AF_INET, result_buf, buf, buflen, errnop, h_errnop
+		);
+}
+
+
+nss_status
+_nss_mdns_gethostbyname2_r (
+	const char *name,
+	int af,
+	hostent * result_buf,
+	char *buf,
+	size_t buflen,
+	int *errnop,
+	int *h_errnop
+)
+{
+	if (MDNS_VERBOSE)
+		syslog (LOG_DEBUG,
+			"mdns: Called nss_mdns_gethostbyname2 with %s",
+			name
+		);
+
+	return
+		mdns_gethostbyname2 (
+			name, af, result_buf, buf, buflen, errnop, h_errnop
+		);
+}
+
+
+nss_status
+_nss_mdns_gethostbyaddr_r (
+	const void *addr,
+	socklen_t len,
+	int af,
+	hostent * result_buf,
+	char *buf,
+	size_t buflen,
+	int *errnop,
+	int *h_errnop
+)
+{
+	char addr_str [NI_MAXHOST + 1];
+	result_map_t result;
+	int err_status;
+	
+	if (inet_ntop (af, addr, addr_str, NI_MAXHOST) == NULL)
+	{
+		const char * family = af_to_str (af);
+		if (family == NULL)
+		{
+			family = "Unknown";
+		}
+
+		syslog (LOG_WARNING,
+			"mdns: Couldn't covert address, family %d (%s) in nss_mdns_gethostbyaddr: %s",
+			af,
+			family,
+			strerror (errno)
+		);
+
+		// This address family never applicable to us, so return NOT_FOUND
+
+		*errnop = ENOENT;
+		*h_errnop = HOST_NOT_FOUND;
+		return NSS_STATUS_NOTFOUND;
+	}
+	if (MDNS_VERBOSE)
+	{
+		syslog (LOG_DEBUG,
+			"mdns: Called nss_mdns_gethostbyaddr with %s",
+			addr_str
+		);
+	}
+
+	// Initialise result
+	err_status = init_result (&result, result_buf, buf, buflen);
+	if (err_status)
+	{
+		*errnop = err_status;
+		*h_errnop = NETDB_INTERNAL;
+		return NSS_STATUS_TRYAGAIN;
+	}
+		
+	if (is_applicable_addr (&result, addr, af, addr_str))
+	{
+		nss_status rv;
+
+		rv = mdns_lookup_addr (addr, len, af, addr_str, &result);
+		if (rv == NSS_STATUS_SUCCESS)
+		{
+			return rv;
+		}
+	}
+
+	// Return current error status (defaults to NOT_FOUND)
+	
+	*errnop = result.r_errno;
+	*h_errnop = result.r_h_errno;
+	return result.status;
+}
+
+
+//----------
+// Local functions
+
+nss_status
+mdns_gethostbyname2 (
+	const char *name,
+	int af,
+	hostent * result_buf,
+	char *buf,
+	size_t buflen,
+	int *errnop,
+	int *h_errnop
+)
+{
+	char lookup_name [k_hostname_maxlen + 1];
+	result_map_t result;
+	int err_status;
+	
+	// Initialise result
+	err_status = init_result (&result, result_buf, buf, buflen);
+	if (err_status)
+	{
+		*errnop = err_status;
+		*h_errnop = NETDB_INTERNAL;
+		return NSS_STATUS_TRYAGAIN;
+	}
+		
+	if (is_applicable_name (&result, name, lookup_name))
+	{
+		// Try using mdns
+		nss_status rv;
+
+		if (MDNS_VERBOSE)
+			syslog (LOG_DEBUG,
+				"mdns: Local name: %s",
+				name
+			);
+
+		rv = mdns_lookup_name (name, af, &result);
+		if (rv == NSS_STATUS_SUCCESS)
+		{
+			return rv;
+		}
+	}
+
+	// Return current error status (defaults to NOT_FOUND)
+	
+	*errnop = result.r_errno;
+	*h_errnop = result.r_h_errno;
+	return result.status;
+}
+
+
+/*
+	Lookup a fully qualified hostname using the default record type
+	for the specified address family.
+	
+	Parameters
+		fullname
+			Fully qualified hostname.  If not fully qualified the code will
+			still 'work', but the lookup is unlikely to succeed.
+		af
+			Either AF_INET or AF_INET6.  Other families are not supported.
+		result
+			Initialised 'result' data structure.
+ */
+static nss_status
+mdns_lookup_name (
+	const char * fullname,
+	int af,
+	result_map_t * result
+)
+{
+	// Lookup using mDNS.
+	DNSServiceErrorType errcode;
+	DNSServiceRef sdref;
+	ns_type_t rrtype;
+	nss_status status;
+	
+	if (MDNS_VERBOSE)
+		syslog (LOG_DEBUG,
+			"mdns: Attempting lookup of %s",
+			fullname
+		);
+	
+	switch (af)
+	{
+	  case AF_INET:
+		rrtype = kDNSServiceType_A;
+		result->hostent->h_length = 4;
+			// Length of an A record
+		break;
+	
+	  case AF_INET6:
+		rrtype = kDNSServiceType_AAAA;
+		result->hostent->h_length = 16;
+			// Length of an AAAA record
+		break;
+	
+	  default:
+		syslog (LOG_WARNING,
+			"mdns: Unsupported address family %d",
+			af
+		);
+		return set_err_bad_hostname (result);
+	}
+	result->hostent->h_addrtype = af;
+	
+	errcode =
+		DNSServiceQueryRecord (
+			&sdref,
+			kDNSServiceFlagsForceMulticast,		// force multicast query
+			kDNSServiceInterfaceIndexAny,	// all interfaces
+			fullname,	// full name to query for
+			rrtype,		// resource record type
+			kDNSServiceClass_IN,	// internet class records
+			mdns_lookup_callback,	// callback
+			result		// Context - result buffer
+		);
+	
+	if (errcode)
+	{
+		syslog (LOG_WARNING,
+			"mdns: Failed to initialise lookup, error %d",
+			errcode
+		);
+		return set_err_mdns_failed (result);
+	}
+
+	status = handle_events (sdref, result, fullname);
+	DNSServiceRefDeallocate (sdref);
+	return status;
+}
+
+
+/*
+	Reverse (PTR) lookup for the specified address.
+	
+	Parameters
+		addr
+			Either a struct in_addr or a struct in6_addr
+		addr_len
+			size of the address
+		af
+			Either AF_INET or AF_INET6.  Other families are not supported.
+			Must match addr
+		addr_str
+			Address in format suitable for PTR lookup.
+			AF_INET: a.b.c.d -> d.c.b.a.in-addr.arpa
+			AF_INET6: reverse nibble format, x.x.x...x.ip6.arpa
+		result
+			Initialised 'result' data structure.
+ */
+static nss_status
+mdns_lookup_addr (
+	const void * addr,
+	socklen_t addr_len,
+	int af,
+	const char * addr_str,
+	result_map_t * result
+)
+{
+	DNSServiceErrorType errcode;
+	DNSServiceRef sdref;
+	nss_status status;
+	
+	if (MDNS_VERBOSE)
+		syslog (LOG_DEBUG,
+			"mdns: Attempting lookup of %s",
+			addr_str
+		);
+		
+	result->hostent->h_addrtype = af;
+	result->hostent->h_length = addr_len;
+
+	// Query address becomes "address" in result.
+	if (! add_address_to_buffer (result, addr, addr_len))
+	{
+		return result->status;
+	}
+	
+	result->hostent->h_name [0] = 0;
+	
+	errcode =
+		DNSServiceQueryRecord (
+			&sdref,
+			kDNSServiceFlagsForceMulticast,		// force multicast query
+			kDNSServiceInterfaceIndexAny,	// all interfaces
+			addr_str,	// address string to query for
+			kDNSServiceType_PTR,	// pointer RRs
+			kDNSServiceClass_IN,	// internet class records
+			mdns_lookup_callback,	// callback
+			result		// Context - result buffer
+		);
+	
+	if (errcode)
+	{
+		syslog (LOG_WARNING,
+			"mdns: Failed to initialise mdns lookup, error %d",
+			errcode
+		);
+		return set_err_mdns_failed (result);
+	}
+
+	status = handle_events (sdref, result, addr_str);
+	DNSServiceRefDeallocate (sdref);
+	return status;
+}
+
+
+/*
+	Wait on result of callback, and process it when it arrives.
+	
+	Parameters
+		sdref
+			dns-sd reference
+		result
+			Initialised 'result' data structure.
+		str
+			lookup string, used for status/error reporting.
+ */
+static nss_status
+handle_events (DNSServiceRef sdref, result_map_t * result, const char * str)
+{
+	int dns_sd_fd = DNSServiceRefSockFD(sdref);
+	int nfds = dns_sd_fd + 1;
+	fd_set readfds;
+	struct timeval tv;
+	int select_result;
+
+	while (! result->done)
+	{
+		FD_ZERO(&readfds);
+		FD_SET(dns_sd_fd, &readfds);
+
+		tv = k_select_time;
+		
+		select_result =
+			select (nfds, &readfds, (fd_set*)NULL, (fd_set*)NULL, &tv);
+		if (select_result > 0)
+		{
+			if (FD_ISSET(dns_sd_fd, &readfds))
+			{
+				if (MDNS_VERBOSE)
+					syslog (LOG_DEBUG,
+						"mdns: Reply received for %s",
+						str
+					);
+				DNSServiceProcessResult(sdref);
+			}
+			else
+			{
+				syslog (LOG_WARNING,
+					"mdns: Unexpected return from select on lookup of %s",
+					str
+				);
+			}
+		}
+		else
+		{
+			// Terminate loop due to timer expiry
+			if (MDNS_VERBOSE)
+				syslog (LOG_DEBUG,
+					"mdns: %s not found - timer expired",
+					str
+				);
+			set_err_notfound (result);
+			break;
+		}
+	}
+	
+	return result->status;
+}
+
+
+/*
+	Examine incoming data and add to relevant fields in result structure.
+	This routine is called from DNSServiceProcessResult where appropriate.
+ */
+void
+mdns_lookup_callback
+(
+	DNSServiceRef		sdref,
+	DNSServiceFlags		flags,
+	uint32_t			interface_index,
+	DNSServiceErrorType	error_code,
+	const char			*fullname,	  
+	uint16_t			rrtype,
+	uint16_t			rrclass,
+	uint16_t			rdlen,
+	const void			*rdata,
+	uint32_t			ttl,
+	void				*context
+)
+{
+	// A single record is received
+
+	result_map_t * result = (result_map_t *) context;
+
+	(void)sdref; // Unused
+	(void)interface_index; // Unused
+	(void)ttl; // Unused
+	
+	if (! (flags & kDNSServiceFlagsMoreComing) )
+	{
+		result->done = 1;
+	}
+
+	if (error_code == kDNSServiceErr_NoError)
+	{
+		ns_type_t expected_rr_type =
+			af_to_rr (result->hostent->h_addrtype);
+
+		// Idiot check class
+		if (rrclass != C_IN)
+		{
+			syslog (LOG_WARNING,
+				"mdns: Received bad RR class: expected %d (%s),"
+				" got %d (%s), RR type %d (%s)",
+				C_IN,
+				ns_class_to_str (C_IN),
+				rrclass,
+				ns_class_to_str (rrclass),
+				rrtype,
+				ns_type_to_str (rrtype)
+			);
+			return;
+		}
+		
+		// If a PTR
+		if (rrtype == kDNSServiceType_PTR)
+		{
+			if (callback_body_ptr (fullname, result, rdlen, rdata) < 0)
+				return;
+		}
+		else if (rrtype == expected_rr_type)
+		{
+			if (!
+				add_hostname_or_alias (
+					result,
+					fullname,
+					strlen (fullname)
+				)
+			)
+			{
+				result->done = 1;
+				return;
+					// Abort on error
+			}
+
+			if (! add_address_to_buffer (result, rdata, rdlen) )
+			{
+				result->done = 1;
+				return;
+					// Abort on error
+			}
+		}
+		else
+		{
+			syslog (LOG_WARNING,
+				"mdns: Received bad RR type: expected %d (%s),"
+				" got %d (%s)",
+				expected_rr_type,
+				ns_type_to_str (expected_rr_type),
+				rrtype,
+				ns_type_to_str (rrtype)
+			);
+			return;
+		}
+		
+		if (result->status != NSS_STATUS_SUCCESS)
+			set_err_success (result);
+	}
+	else
+	{
+		// For now, dump message to syslog and continue
+		syslog (LOG_WARNING,
+			"mdns: callback returned error %d",
+			error_code
+		);
+	}
+}
+
+static int
+callback_body_ptr (
+	const char * fullname,
+	result_map_t * result,
+	int rdlen,
+	const void * rdata
+)
+{
+	char result_name [k_hostname_maxlen + 1];
+	int rv;
+	
+	// Fullname should be .in-addr.arpa or equivalent, which we're
+	// not interested in.  Ignore it.
+	
+	rv = dns_rdata_to_name (rdata, rdlen, result_name, k_hostname_maxlen);
+	if (rv < 0)
+	{
+		const char * errmsg;
+		
+		switch (rv)
+		{
+		  case DNS_RDATA_TO_NAME_BAD_FORMAT:
+			errmsg = "mdns: PTR '%s' result badly formatted ('%s...')";
+			break;
+		
+		  case DNS_RDATA_TO_NAME_TOO_LONG:
+			errmsg = "mdns: PTR '%s' result too long ('%s...')";
+			break;
+		
+		  case DNS_RDATA_TO_NAME_PTR:
+			errmsg = "mdns: PTR '%s' result contained pointer ('%s...')";
+			break;
+		
+		  default:
+			errmsg = "mdns: PTR '%s' result conversion failed ('%s...')";
+		}
+
+		syslog (LOG_WARNING,
+			errmsg,
+			fullname,
+			result_name
+		);
+		
+		return -1;
+	}
+	
+	if (MDNS_VERBOSE)
+	{
+		syslog (LOG_DEBUG,
+			"mdns: PTR '%s' resolved to '%s'",
+			fullname,
+			result_name
+		);
+	}
+	
+	// Data should be a hostname
+	if (!
+		add_hostname_or_alias (
+			result,
+			result_name,
+			rv
+		)
+	)
+	{
+		result->done = 1;
+		return -1;
+	}
+	
+	return 0;
+}
+
+
+/*
+	Add an address to the buffer.
+	
+	Parameter
+		result
+			Result structure to write to
+		data
+			Incoming address data buffer
+			Must be 'int' aligned
+		len
+			Length of data buffer (in bytes)
+			Must match data alignment
+	
+	Result
+		Pointer to start of newly written data,
+		or NULL on error.
+		If address already exists in buffer, returns pointer to that instead.
+ */
+static void *
+add_address_to_buffer (result_map_t * result, const void * data, int len)
+{
+	int new_addr;
+	void * start;
+	void * temp;
+	
+	if ((temp = contains_address (result, data, len)))
+	{
+		return temp;
+	}
+	
+	if (result->addrs_count >= k_addrs_max)
+	{
+		// Not enough addr slots
+		set_err_internal_resource_full (result);
+		syslog (LOG_ERR,
+			"mdns: Internal address buffer full; increase size"
+		);
+		return NULL;
+	}
+	
+	// Idiot check
+	if (len != result->hostent->h_length)
+	{
+		syslog (LOG_WARNING,
+			"mdns: Unexpected rdata length for address.  Expected %d, got %d",
+			result->hostent->h_length,
+			len
+		);
+		// XXX And continue for now.
+	}
+
+	new_addr = result->addr_idx + len;
+	
+	if (new_addr > result->alias_idx)
+	{
+		// Not enough room
+		set_err_buf_too_small (result);
+		if (MDNS_VERBOSE)
+			syslog (LOG_DEBUG,
+				"mdns: Ran out of buffer when adding address %d",
+				result->addrs_count + 1
+			);
+		return NULL;
+	}
+
+	start = result->buffer + result->addr_idx;
+	memcpy (start, data, len);
+	result->addr_idx = new_addr;
+	result->header->addrs [result->addrs_count] = start;
+	result->addrs_count ++;
+	result->header->addrs [result->addrs_count] = NULL;
+
+	return start;
+}
+
+
+static void *
+contains_address (result_map_t * result, const void * data, int len)
+{
+	int i;
+	
+	// Idiot check
+	if (len != result->hostent->h_length)
+	{
+		syslog (LOG_WARNING,
+			"mdns: Unexpected rdata length for address.  Expected %d, got %d",
+			result->hostent->h_length,
+			len
+		);
+		// XXX And continue for now.
+	}
+
+	for (i = 0; result->header->addrs [i]; i++)
+	{
+		if (memcmp (result->header->addrs [i], data, len) == 0)
+		{
+			return result->header->addrs [i];
+		}
+	}
+	
+	return NULL;
+}
+
+
+/*
+	Add an alias to the buffer.
+	
+	Parameter
+		result
+			Result structure to write to
+		data
+			Incoming alias (null terminated)
+		len
+			Length of data buffer (in bytes), including trailing null
+	
+	Result
+		Pointer to start of newly written data,
+		or NULL on error
+		If alias already exists in buffer, returns pointer to that instead.
+ */
+static char *
+add_alias_to_buffer (result_map_t * result, const char * data, int len)
+{
+	int new_alias;
+	char * start;
+	char * temp;
+	
+	if ((temp = contains_alias (result, data)))
+	{
+		return temp;
+	}
+	
+	if (result->aliases_count >= k_aliases_max)
+	{
+		// Not enough alias slots
+		set_err_internal_resource_full (result);
+		syslog (LOG_ERR,
+			"mdns: Internal alias buffer full; increase size"
+		);
+		return NULL;
+	}
+
+	new_alias = result->alias_idx - len;
+	
+	if (new_alias < result->addr_idx)
+	{
+		// Not enough room
+		set_err_buf_too_small (result);
+		if (MDNS_VERBOSE)
+			syslog (LOG_DEBUG,
+				"mdns: Ran out of buffer when adding alias %d",
+				result->aliases_count + 1
+			);
+		return NULL;
+	}
+
+	start = result->buffer + new_alias;
+	memcpy (start, data, len);
+	result->alias_idx = new_alias;
+	result->header->aliases [result->aliases_count] = start;
+	result->aliases_count ++;
+	result->header->aliases [result->aliases_count] = NULL;
+
+	return start;
+}
+
+
+static char *
+contains_alias (result_map_t * result, const char * alias)
+{
+	int i;
+	
+	for (i = 0; result->header->aliases [i]; i++)
+	{
+		if (strcmp (result->header->aliases [i], alias) == 0)
+		{
+			return result->header->aliases [i];
+		}
+	}
+	
+	return NULL;
+}
+
+
+/*
+	Add fully qualified hostname to result.
+	
+	Parameter
+		result
+			Result structure to write to
+		fullname
+			Fully qualified hostname
+	
+	Result
+		Pointer to start of hostname buffer,
+		or NULL on error (usually hostname too long)
+ */
+
+static char *
+add_hostname_len (result_map_t * result, const char * fullname, int len)
+{
+	if (len >= k_hostname_maxlen)
+	{
+		set_err_bad_hostname (result);
+		syslog (LOG_WARNING,
+			"mdns: Hostname too long '%.*s': len %d, max %d",
+			len,
+			fullname,
+			len,
+			k_hostname_maxlen
+		);
+		return NULL;
+	}
+	
+	result->hostent->h_name =
+		strcpy (result->header->hostname, fullname);
+	
+	return result->header->hostname;
+}
+
+
+/*
+	Add fully qualified name as hostname or alias.
+	
+	If hostname is not fully qualified this is not an error, but the data
+	returned may be not what the application wanted.
+
+	Parameter
+		result
+			Result structure to write to
+		data
+			Incoming alias (null terminated)
+		len
+			Length of data buffer (in bytes), including trailing null
+	
+	Result
+		Pointer to start of newly written data,
+		or NULL on error
+		If alias or hostname already exists, returns pointer to that instead.
+ */
+static char *
+add_hostname_or_alias (result_map_t * result, const char * data, int len)
+{
+	char * hostname = result->hostent->h_name;
+
+	if (*hostname)
+	{
+		if (strcmp (hostname, data) == 0)
+		{
+			return hostname;
+		}
+		else
+		{
+			return add_alias_to_buffer (result, data, len);
+		}
+	}
+	else
+	{
+		return add_hostname_len (result, data, len);
+	}
+}
+
+
+static int
+init_result (
+	result_map_t * result,
+	hostent * result_buf,
+	char * buf,
+	size_t buflen
+)
+{
+	if (buflen < sizeof (buf_header_t))
+	{
+		return ERANGE;
+	}
+
+	result->hostent = result_buf;
+	result->header = (buf_header_t *) buf;
+	result->header->hostname[0] = 0;
+	result->aliases_count = 0;
+	result->header->aliases[0] = NULL;
+	result->addrs_count = 0;
+	result->header->addrs[0] = NULL;
+	result->buffer = buf + sizeof (buf_header_t);
+	result->addr_idx = 0;
+	result->alias_idx = buflen - sizeof (buf_header_t);
+	result->done = 0;
+	set_err_notfound (result);
+
+	// Point hostent to the right buffers
+	result->hostent->h_name = result->header->hostname;
+	result->hostent->h_aliases = result->header->aliases;
+	result->hostent->h_addr_list = result->header->addrs;
+	
+	return 0;
+}
+
+/*
+	Set the status in the result.
+	
+	Parameters
+		result
+			Result structure to update
+		status
+			New nss_status value
+		err
+			New errno value
+		herr
+			New h_errno value
+	
+	Returns
+		New status value
+ */
+static nss_status
+set_err (result_map_t * result, nss_status status, int err, int herr)
+{
+	result->status = status;
+	result->r_errno = err;
+	result->r_h_errno = herr;
+	
+	return status;
+}
+
+static nss_status
+set_err_notfound (result_map_t * result)
+{
+	return set_err (result, NSS_STATUS_NOTFOUND, ENOENT, HOST_NOT_FOUND);
+}
+
+static nss_status
+set_err_bad_hostname (result_map_t * result)
+{
+	return set_err (result, NSS_STATUS_TRYAGAIN, ENOENT, NO_RECOVERY);
+}
+
+static nss_status
+set_err_buf_too_small (result_map_t * result)
+{
+	return set_err (result, NSS_STATUS_TRYAGAIN, ERANGE, NETDB_INTERNAL);
+}
+
+static nss_status
+set_err_internal_resource_full (result_map_t * result)
+{
+	return set_err (result, NSS_STATUS_RETURN, ERANGE, NO_RECOVERY);
+}
+
+static nss_status
+set_err_system (result_map_t * result)
+{
+	return set_err (result, NSS_STATUS_UNAVAIL, errno, NETDB_INTERNAL);
+}
+
+static nss_status
+set_err_mdns_failed (result_map_t * result)
+{
+	return set_err (result, NSS_STATUS_TRYAGAIN, EAGAIN, TRY_AGAIN);
+}
+
+static nss_status
+set_err_success (result_map_t * result)
+{
+	result->status = NSS_STATUS_SUCCESS;
+	return result->status;
+}
+
+
+/*
+	Test whether name is applicable for mdns to process, and if so copy into
+	lookup_name buffer (if non-NULL).
+	
+	Returns
+		Pointer to name to lookup up, if applicable, or NULL otherwise.
+ */
+static const char *
+is_applicable_name (
+	result_map_t * result,
+	const char * name,
+	char * lookup_name
+)
+{
+	int match = config_is_mdns_suffix (name);
+	if (match > 0)
+	{
+		if (lookup_name)
+		{
+			strncpy (lookup_name, name, k_hostname_maxlen + 1);
+			return lookup_name;
+		}
+		else
+		{
+			return name;
+		}
+	}
+	else
+	{
+		if (match < 0)
+		{
+			set_err_system (result);
+		}
+		return NULL;
+	}
+}
+
+/*
+	Test whether address is applicable for mdns to process, and if so copy into
+	addr_str buffer as an address suitable for ptr lookup.
+	
+	Returns
+		Pointer to name to lookup up, if applicable, or NULL otherwise.
+ */
+static const char *
+is_applicable_addr (
+	result_map_t * result,
+	const void * addr,
+	int af,
+	char * addr_str
+)
+{
+	int match;
+	
+	if (! format_reverse_addr (af, addr, -1, addr_str))
+	{
+		if (MDNS_VERBOSE)
+			syslog (LOG_DEBUG,
+				"mdns: Failed to create reverse address"
+			);
+		return NULL;
+	}
+
+	if (MDNS_VERBOSE)
+		syslog (LOG_DEBUG,
+			"mdns: Reverse address: %s",
+			addr_str
+		);
+
+	match = config_is_mdns_suffix (addr_str);
+	if (match > 0)
+	{
+		return addr_str;
+	}
+	else
+	{
+		if (match < 0)
+		{
+			set_err_system (result);
+		}
+		return NULL;
+	}
+}
+
+//----------
+// Types and Constants
+
+const char * k_conf_file = "/etc/nss_mdns.conf";
+#define CONF_LINE_SIZE 1024
+
+const char k_comment_char = '#';
+
+const char * k_keyword_domain = "domain";
+
+const char * k_default_domains [] =
+	{
+		"local",
+		"254.169.in-addr.arpa",
+		"8.e.f.ip6.int",
+		"8.e.f.ip6.arpa",
+		"9.e.f.ip6.int",
+		"9.e.f.ip6.arpa",
+		"a.e.f.ip6.int",
+		"a.e.f.ip6.arpa",
+		"b.e.f.ip6.int",
+		"b.e.f.ip6.arpa",
+		NULL
+			// Always null terminated
+	};
+
+// Linked list of domains
+typedef struct domain_entry
+{
+	char * domain;
+	struct domain_entry * next;
+} domain_entry_t;
+
+
+// Config
+typedef struct
+{
+	domain_entry_t * domains;
+} config_t;
+
+const config_t k_empty_config =
+	{
+		NULL
+	};
+
+
+// Context - tracks position in config file, used for error reporting
+typedef struct
+{
+	const char * filename;
+	int linenum;
+} config_file_context_t;
+
+
+//----------
+// Local prototypes
+
+static errcode_t
+load_config (config_t * conf);
+
+static errcode_t
+process_config_line (
+	config_t * conf,
+	char * line,
+	config_file_context_t * context
+);
+
+static char *
+get_next_word (char * input, char **next);
+
+static errcode_t
+default_config (config_t * conf);
+
+static errcode_t
+add_domain (config_t * conf, const char * domain);
+
+static int
+contains_domain (const config_t * conf, const char * domain);
+
+static int
+contains_domain_suffix (const config_t * conf, const char * addr);
+
+
+//----------
+// Global variables
+
+static config_t * g_config = NULL;
+	// Configuration info
+
+pthread_mutex_t g_config_mutex =
+#ifdef PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP
+	PTHREAD_ERRORCHECK_MUTEX_INITIALIZER_NP;
+#else
+	PTHREAD_MUTEX_INITIALIZER;
+#endif
+
+
+//----------
+// Configuration functions
+
+
+/*
+	Initialise the configuration from the config file.
+	
+	Returns
+		0 success
+		non-zero error code on failure
+ */
+errcode_t
+init_config ()
+{
+	if (g_config)
+	{
+		/*
+			Safe to test outside mutex.
+			If non-zero, initialisation is complete and g_config can be
+			safely used read-only.  If zero, then we do proper mutex
+			testing before initialisation.
+		 */
+		return 0;
+	}
+	else
+	{
+		int errcode = -1;
+		int presult;
+		config_t * temp_config;
+		
+		// Acquire mutex
+		presult = pthread_mutex_lock (&g_config_mutex);
+		if (presult)
+		{
+			syslog (LOG_ERR,
+				"mdns: Fatal mutex lock error in nss_mdns:init_config, %s:%d: %d: %s",
+				__FILE__, __LINE__, presult, strerror (presult)
+			);
+			return presult;
+		}
+		
+		// Test again now we have mutex, in case initialisation occurred while
+		// we were waiting
+		if (! g_config)
+		{
+			temp_config = (config_t *) malloc (sizeof (config_t));
+			if (temp_config)
+			{
+				// Note: This code will leak memory if initialisation fails
+				// repeatedly.  This should only happen in the case of a memory
+				// error, so I'm not sure if it's a meaningful problem. - AW
+				*temp_config = k_empty_config;
+				errcode = load_config (temp_config);
+	
+				if (! errcode)
+				{
+					g_config = temp_config;
+				}
+			}
+			else
+			{
+				syslog (LOG_ERR,
+					"mdns: Can't allocate memory in nss_mdns:init_config, %s:%d",
+					__FILE__, __LINE__
+				);
+				errcode = errno;
+			}
+		}
+		
+		presult = pthread_mutex_unlock (&g_config_mutex);
+		if (presult)
+		{
+			syslog (LOG_ERR,
+				"mdns: Fatal mutex unlock error in nss_mdns:init_config, %s:%d: %d: %s",
+				__FILE__, __LINE__, presult, strerror (presult)
+			);
+			errcode = presult;
+		}
+
+		return errcode;
+	}
+}
+
+
+int
+config_is_mdns_suffix (const char * name)
+{
+	int errcode = init_config ();
+	if (! errcode)
+	{
+		return contains_domain_suffix (g_config, name);
+	}
+	else
+	{
+		errno = errcode;
+		return -1;
+	}
+}
+
+
+//----------
+// Local functions
+
+static errcode_t
+load_config (config_t * conf)
+{
+	FILE * cf;
+	char line [CONF_LINE_SIZE];
+	config_file_context_t context;
+
+	context.filename = k_conf_file;
+	context.linenum = 0;
+	
+	
+	cf = fopen (context.filename, "r");
+	if (! cf)
+	{
+		syslog (LOG_INFO,
+			"mdns: Couldn't open nss_mdns configuration file %s, using default.",
+			context.filename
+		);
+		return default_config (conf);
+	}
+	
+	while (fgets (line, CONF_LINE_SIZE, cf))
+	{
+		int errcode;
+		context.linenum++;
+		errcode = process_config_line (conf, line, &context);
+		if (errcode)
+		{
+			// Critical error, give up
+			fclose(cf);
+			return errcode;
+		}
+	}
+	
+	fclose (cf);
+	
+	return 0;
+}
+
+
+/*
+	Parse a line of the configuration file.
+	For each keyword recognised, perform appropriate handling.
+	If the keyword is not recognised, print a message to syslog
+	and continue.
+	
+	Returns
+		0 success, or recoverable config file error
+		non-zero serious system error, processing aborted
+ */
+static errcode_t
+process_config_line (
+	config_t * conf,
+	char * line,
+	config_file_context_t * context
+)
+{
+	char * curr = line;
+	char * word;
+	
+	word = get_next_word (curr, &curr);
+	if (! word || word [0] == k_comment_char)
+	{
+		// Nothing interesting on this line
+		return 0;
+	}
+	
+	if (strcmp (word, k_keyword_domain) == 0)
+	{
+		word = get_next_word (curr, &curr);
+		if (word)
+		{
+			int errcode = add_domain (conf, word);
+			if (errcode)
+			{
+				// something badly wrong, bail
+				return errcode;
+			}
+			
+			if (get_next_word (curr, NULL))
+			{
+				syslog (LOG_WARNING,
+					"%s, line %d: ignored extra text found after domain",
+					context->filename,
+					context->linenum
+				);
+			}
+		}
+		else
+		{
+			syslog (LOG_WARNING,
+				"%s, line %d: no domain specified",
+				context->filename,
+				context->linenum
+			);
+		}
+	}
+	else
+	{
+		syslog (LOG_WARNING,
+			"%s, line %d: unknown keyword %s - skipping",
+			context->filename,
+			context->linenum,
+			word
+		);
+	}
+	
+	return 0;
+}
+
+
+/*
+	Get next word (whitespace separated) from input string.
+	A null character is written into the first whitespace character following
+	the word.
+	
+	Parameters
+		input
+			Input string.  This string is modified by get_next_word.
+		next
+			If non-NULL and the result is non-NULL, a pointer to the
+			character following the end of the word (after the null)
+			is written to 'next'.
+			If no word is found, the original value is unchanged.
+			If the word extended to the end of the string, 'next' points
+			to the trailling NULL.
+			It is safe to pass 'str' as 'input' and '&str' as 'next'.
+	Returns
+		Pointer to the first non-whitespace character (and thus word) found.
+		if no word is found, returns NULL.
+ */
+static char *
+get_next_word (char * input, char **next)
+{
+	char * curr = input;
+	char * result;
+	
+	while (isspace (*curr))
+	{
+		curr ++;
+	}
+	
+	if (*curr == 0)
+	{
+		return NULL;
+	}
+	
+	result = curr;
+	while (*curr && ! isspace (*curr))
+	{
+		curr++;
+	}
+	if (*curr)
+	{
+		*curr = 0;
+		if (next)
+		{
+			*next = curr+1;
+		}
+	}
+	else
+	{
+		if (next)
+		{
+			*next = curr;
+		}
+	}
+	
+	return result;
+}
+
+
+static errcode_t
+default_config (config_t * conf)
+{
+	int i;
+	for (i = 0; k_default_domains [i]; i++)
+	{
+		int errcode =
+			add_domain (conf, k_default_domains [i]);
+		if (errcode)
+		{
+			// Something has gone (badly) wrong - let's bail
+			return errcode;
+		}
+	}
+	
+	return 0;
+}
+
+
+static errcode_t
+add_domain (config_t * conf, const char * domain)
+{
+	if (! contains_domain (conf, domain))
+	{
+		domain_entry_t * d =
+			(domain_entry_t *) malloc (sizeof (domain_entry_t));
+		if (! d)
+		{
+			syslog (LOG_ERR,
+				"mdns: Can't allocate memory in nss_mdns:init_config, %s:%d",
+				__FILE__, __LINE__
+			);
+			return ENOMEM;
+		}
+
+		d->domain = strdup (domain);
+		if (! d->domain)
+		{
+			syslog (LOG_ERR,
+				"mdns: Can't allocate memory in nss_mdns:init_config, %s:%d",
+				__FILE__, __LINE__
+			);
+			free (d);
+			return ENOMEM;
+		}
+		d->next = conf->domains;
+		conf->domains = d;
+	}
+	
+	return 0;
+}
+
+
+static int
+contains_domain (const config_t * conf, const char * domain)
+{
+	const domain_entry_t * curr = conf->domains;
+	
+	while (curr != NULL)
+	{
+		if (strcasecmp (curr->domain, domain) == 0)
+		{
+			return 1;
+		}
+		
+		curr = curr->next;
+	}
+	
+	return 0;
+}
+
+
+static int
+contains_domain_suffix (const config_t * conf, const char * addr)
+{
+	const domain_entry_t * curr = conf->domains;
+	
+	while (curr != NULL)
+	{
+		if (cmp_dns_suffix (addr, curr->domain) > 0)
+		{
+			return 1;
+		}
+		
+		curr = curr->next;
+	}
+	
+	return 0;
+}
+
+//----------
+// Types and Constants
+
+static const char * k_local_suffix = "local";
+static const char k_dns_separator = '.';
+
+static const int k_label_maxlen = DNS_LABEL_MAXLEN;
+	// Label entries longer than this are actually pointers.
+
+typedef struct
+{
+	int value;
+	const char * name;
+	const char * comment;
+} table_entry_t;
+
+static const table_entry_t k_table_af [] =
+	{
+		{ AF_UNSPEC, NULL, NULL },
+		{ AF_LOCAL, "LOCAL", NULL },
+		{ AF_UNIX, "UNIX", NULL },
+		{ AF_INET, "INET", NULL },
+		{ AF_INET6, "INET6", NULL }
+	};
+static const int k_table_af_size =
+	sizeof (k_table_af) / sizeof (* k_table_af);
+
+static const char * k_table_ns_class [] =
+	{
+		NULL,
+		"IN"
+	};
+static const int k_table_ns_class_size =
+	sizeof (k_table_ns_class) / sizeof (* k_table_ns_class);
+
+static const char * k_table_ns_type [] =
+	{
+		NULL,
+		"A",
+		"NS",
+		"MD",
+		"MF",
+		"CNAME",
+		"SOA",
+		"MB",
+		"MG",
+		"MR",
+		"NULL",
+		"WKS",
+		"PTR",
+		"HINFO",
+		"MINFO",
+		"MX",
+		"TXT",
+		"RP",
+		"AFSDB",
+		"X25",
+		"ISDN",
+		"RT",
+		"NSAP",
+		NULL,
+		"SIG",
+		"KEY",
+		"PX",
+		"GPOS",
+		"AAAA",
+		"LOC",
+		"NXT",
+		"EID",
+		"NIMLOC",
+		"SRV",
+		"ATMA",
+		"NAPTR",
+		"KX",
+		"CERT",
+		"A6",
+		"DNAME",
+		"SINK",
+		"OPT"
+	};
+static const int k_table_ns_type_size =
+	sizeof (k_table_ns_type) / sizeof (* k_table_ns_type);
+
+
+//----------
+// Local prototypes
+
+static int
+simple_table_index (const char * table [], int size, const char * str);
+
+static int
+table_index_name (const table_entry_t table [], int size, const char * str);
+
+static int
+table_index_value (const table_entry_t table [], int size, int n);
+
+
+//----------
+// Global variables
+
+
+//----------
+// Util functions
+
+int
+count_dots (const char * name)
+{
+	int count = 0;
+	int i;
+	for (i = 0; name[i]; i++)
+	{
+		if (name [i] == k_dns_separator)
+			count++;
+	}
+	
+	return count;
+}
+
+
+int
+islocal (const char * name)
+{
+	return cmp_dns_suffix (name, k_local_suffix) > 0;
+}
+
+
+int
+rr_to_af (ns_type_t rrtype)
+{
+	switch (rrtype)
+	{
+	  case kDNSServiceType_A:
+		return AF_INET;
+	
+	  case kDNSServiceType_AAAA:
+		return AF_INET6;
+	
+	  default:
+		return AF_UNSPEC;
+	}
+}
+
+
+ns_type_t
+af_to_rr (int af)
+{
+	switch (af)
+	{
+	  case AF_INET:
+		return kDNSServiceType_A;
+	
+	  case AF_INET6:
+		return kDNSServiceType_AAAA;
+	
+	  default:
+		//return ns_t_invalid;
+		return 0;
+	}
+}
+
+
+int
+str_to_af (const char * str)
+{
+	int result =
+		table_index_name (k_table_af, k_table_af_size, str);
+	if (result < 0)
+		result = 0;
+
+	return k_table_af [result].value;
+}
+
+
+ns_class_t
+str_to_ns_class (const char * str)
+{
+	return (ns_class_t)
+		simple_table_index (k_table_ns_class, k_table_ns_class_size, str);
+}
+
+
+ns_type_t
+str_to_ns_type (const char * str)
+{
+	return (ns_type_t)
+		simple_table_index (k_table_ns_type, k_table_ns_type_size, str);
+}
+
+
+const char *
+af_to_str (int in)
+{
+	int result =
+		table_index_value (k_table_af, k_table_af_size, in);
+	if (result < 0)
+		result = 0;
+
+	return k_table_af [result].name;
+}
+
+
+const char *
+ns_class_to_str (ns_class_t in)
+{
+	if (in < k_table_ns_class_size)
+		return k_table_ns_class [in];
+	else
+		return NULL;
+}
+
+
+const char *
+ns_type_to_str (ns_type_t in)
+{
+	if (in < k_table_ns_type_size)
+		return k_table_ns_type [in];
+	else
+		return NULL;
+}
+
+
+char *
+format_reverse_addr_in (
+	const struct in_addr * addr,
+	int prefixlen,
+	char * buf
+)
+{
+	char * curr = buf;
+	int i;
+	
+	const uint8_t * in_addr_a = (uint8_t *) addr;
+	
+	if (prefixlen > 32)
+		return NULL;
+	if (prefixlen < 0)
+		prefixlen = 32;
+
+	i = (prefixlen + 7) / 8;
+		// divide prefixlen into bytes, rounding up
+	
+	while (i > 0)
+	{
+		i--;
+		curr += sprintf (curr, "%d.", in_addr_a [i]);
+	}
+	sprintf (curr, "in-addr.arpa");
+	
+	return buf;
+}
+
+
+char *
+format_reverse_addr_in6 (
+	const struct in6_addr * addr,
+	int prefixlen,
+	char * buf
+)
+{
+	char * curr = buf;
+	int i;
+
+	const uint8_t * in_addr_a = (uint8_t *) addr;
+
+	if (prefixlen > 128)
+		return NULL;
+	if (prefixlen < 0)
+		prefixlen = 128;
+	
+	i = (prefixlen + 3) / 4;
+		// divide prefixlen into nibbles, rounding up
+
+	// Special handling for first
+	if (i % 2)
+	{
+		curr += sprintf (curr, "%d.", (in_addr_a [i/2] >> 4) & 0x0F);
+	}
+	i >>= 1;
+		// Convert i to bytes (divide by 2)
+	
+	while (i > 0)
+	{
+		uint8_t val;
+		
+		i--;
+		val = in_addr_a [i];
+		curr += sprintf (curr, "%x.%x.", val & 0x0F, (val >> 4) & 0x0F);
+	}
+	sprintf (curr, "ip6.arpa");
+	
+	return buf;
+}
+
+
+char *
+format_reverse_addr (
+	int af,
+	const void * addr,
+	int prefixlen,
+	char * buf
+)
+{
+	switch (af)
+	{
+	  case AF_INET:
+		return
+			format_reverse_addr_in (
+				(struct in_addr *) addr, prefixlen, buf
+			);
+		break;
+	
+	  case AF_INET6:
+		return
+			format_reverse_addr_in6 (
+				(struct in6_addr *) addr, prefixlen, buf
+			);
+		break;
+	
+	  default:
+		return NULL;
+	}
+}
+
+
+int
+cmp_dns_suffix (const char * name, const char * domain)
+{
+	const char * nametail;
+	const char * domaintail;
+
+	// Idiot checks
+	if (*name == 0 || *name == k_dns_separator)
+	{
+		// Name can't be empty or start with separator
+		return CMP_DNS_SUFFIX_BAD_NAME;
+	}
+	
+	if (*domain == 0)
+	{
+		return CMP_DNS_SUFFIX_SUCCESS;
+			// trivially true
+	}
+	
+	if (*domain == k_dns_separator)
+	{
+		// drop leading separator from domain
+		domain++;
+		if (*domain == k_dns_separator)
+		{
+			return CMP_DNS_SUFFIX_BAD_DOMAIN;
+		}
+	}
+
+	// Find ends of strings
+	for (nametail = name; *nametail; nametail++)
+		;
+	for (domaintail = domain; *domaintail; domaintail++)
+		;
+	
+	// Shuffle back to last real character, and drop any trailing '.'
+	// while we're at it.
+	nametail --;
+	if (*nametail == k_dns_separator)
+	{
+		nametail --;
+		if (*nametail == k_dns_separator)
+		{
+			return CMP_DNS_SUFFIX_BAD_NAME;
+		}
+	}
+	domaintail --;
+	if (*domaintail == k_dns_separator)
+	{
+		domaintail --;
+		if (*domaintail == k_dns_separator)
+		{
+			return CMP_DNS_SUFFIX_BAD_DOMAIN;
+		}
+	}
+	
+	// Compare.
+	while (
+		nametail >= name
+		&& domaintail >= domain
+		&& tolower(*nametail) == tolower(*domaintail))
+	{
+		nametail--;
+		domaintail--;
+	}
+	
+	/* A successful finish will be one of the following:
+		(leading and trailing . ignored)
+		
+		name  :  domain2.domain1
+		domain:  domain2.domain1
+		        ^
+		
+		name  : domain3.domain2.domain1
+		domain:         domain2.domain1
+		               ^
+	 */
+	if (
+		domaintail < domain
+		&& (nametail < name || *nametail == k_dns_separator)
+	)
+	{
+		return CMP_DNS_SUFFIX_SUCCESS;
+	}
+	else
+	{
+		return CMP_DNS_SUFFIX_FAILURE;
+	}
+}
+
+
+int
+dns_rdata_to_name (const char * rdata, int rdlen, char * name, int name_len)
+{
+	int i = 0;
+		// Index into 'name'
+	const char * rdata_curr = rdata;
+	
+	if (rdlen == 0) return DNS_RDATA_TO_NAME_BAD_FORMAT;
+	
+	/*
+		In RDATA, a DNS name is stored as a series of labels.
+		Each label consists of a length octet (max value 63)
+		followed by the data for that label.
+		The series is terminated with a length 0 octet.
+		A length octet beginning with bits 11 is a pointer to
+		somewhere else in the payload, but we don't support these
+		since we don't have access to the entire payload.
+	
+		See RFC1034 section 3.1 and RFC1035 section 3.1.
+	 */
+	while (1)
+	{
+		int term_len = *rdata_curr;
+		rdata_curr++;
+
+		if (term_len == 0)
+		{
+			break;
+				// 0 length record terminates label
+		}
+		else if (term_len > k_label_maxlen)
+		{
+			name [i] = 0;
+			return DNS_RDATA_TO_NAME_PTR;
+		}
+		else if (rdata_curr + term_len > rdata + rdlen)
+		{
+			name [i] = 0;
+			return DNS_RDATA_TO_NAME_BAD_FORMAT;
+		}
+		
+		if (name_len < i + term_len + 1)
+			// +1 is separator
+		{
+			name [i] = 0;
+			return DNS_RDATA_TO_NAME_TOO_LONG;
+		}
+		
+		memcpy (name + i, rdata_curr, term_len);
+		
+		i += term_len;
+		rdata_curr += term_len;
+		
+		name [i] = k_dns_separator;
+		i++;
+	}
+	
+	name [i] = 0;
+	return i;
+}
+
+
+//----------
+// Local functions
+
+/*
+	Find the index of an string entry in a table.  A case insenitive match
+	is performed.  If no entry is found, 0 is returned.
+	
+	Parameters
+		table
+			Lookup table
+			Table entries may be NULL.  NULL entries will never match.
+		size
+			number of entries in table
+		str
+			lookup string
+
+	Result
+		index of first matching entry, or 0 if no matches
+ */
+static int
+simple_table_index (const char * table [], int size, const char * str)
+{
+	int i;
+	for (i = 0; i < size; i++)
+	{
+		if (
+			table [i]
+			&& (strcasecmp (table [i], str) == 0)
+		)
+		{
+			return i;
+		}
+	}
+	
+	return 0;
+}
+
+
+/*
+	Find the index of a name in a table.
+	
+	Parameters
+		table
+			array of table_entry_t records.  The name field is compared
+			(ignoring case) to the input string.
+		size
+			number of entries in table
+		str
+			lookup string
+
+	Result
+		index of first matching entry, or -1 if no matches
+ */
+static int
+table_index_name (const table_entry_t table [], int size, const char * str)
+{
+	int i;
+	for (i = 0; i < size; i++)
+	{
+		if (
+			table [i].name
+			&& (strcasecmp (table [i].name, str) == 0)
+		)
+		{
+			return i;
+		}
+	}
+	
+	return -1;
+}
+
+
+/*
+	Find the index of a value a table.
+	
+	Parameters
+		table
+			array of table_entry_t records.  The value field is compared to
+			the input value
+		size
+			number of entries in table
+		n
+			lookup value
+
+	Result
+		index of first matching entry, or -1 if no matches
+ */
+static int
+table_index_value (const table_entry_t table [], int size, int n)
+{
+	int i;
+	for (i = 0; i < size; i++)
+	{
+		if (table [i].value == n)
+		{
+			return i;
+		}
+	}
+	
+	return -1;
+}
diff --git a/mdnsresponder/mDNSPosix/nss_mdns.conf b/mdnsresponder/mDNSPosix/nss_mdns.conf
new file mode 100755
index 0000000..bdcdad7
--- /dev/null
+++ b/mdnsresponder/mDNSPosix/nss_mdns.conf
@@ -0,0 +1,13 @@
+# Defaut configuration file for nss_mdns
+
+# Applicable domains
+domain local
+domain 254.169.in-addr.arpa
+domain 8.e.f.ip6.int
+domain 9.e.f.ip6.int
+domain a.e.f.ip6.int
+domain b.e.f.ip6.int
+domain 8.e.f.ip6.arpa
+domain 9.e.f.ip6.arpa
+domain a.e.f.ip6.arpa
+domain b.e.f.ip6.arpa
diff --git a/mdnsresponder/mDNSPosix/nss_mdns.conf.5 b/mdnsresponder/mDNSPosix/nss_mdns.conf.5
new file mode 100755
index 0000000..7dbefa2
--- /dev/null
+++ b/mdnsresponder/mDNSPosix/nss_mdns.conf.5
@@ -0,0 +1,135 @@
+.\"
+.\" See section LICENSE for license information.
+.\"
+.Dd June 15, 2004
+.Dt NSS_MDNS.CONF 5
+.Os
+.Sh NAME
+.Nm nss_mdns.conf
+.Nd configuration file for
+.Xr libnss_mdns 8 .
+.Sh DESCRIPTION
+This file describes the domains that
+.Xr libnss_mdns 8
+is to support.  If a lookup domain is not in this list, then
+.Li NSS_STATUS_NOTFOUND
+will be returned to libc and processing will continue according to
+.Xr nsswitch.conf 5 .
+.Ss Configuration file format
+Lines containing only whitespace or lines whose first non-whitespace character is
+.Ql #
+are ignored.  No single line may be greater than 1023 characters plus end-of-line.
+.Pp
+.D1 Ic domain Ar x.y.z
+.Pp
+Enable use of
+.Xr libnss_mdns 8
+to lookup DNS entries in the
+.Ql x.y.z
+domain.  Leading and trailing dots are dropped.
+.Pp
+Reverse (PTR) lookups are enabled using their DNS names.  IPv6 names use
+.Qq nibble format .
+.Pp
+.Dl domain 254.169.in-addr.arpa
+.Dl domain 0.8.e.f.ip6.arpa
+.Ss Default configuration
+If the configuration file cannot be found then the following is assumed.
+.Bd -literal -offset indent
+domain local
+domain 0.8.e.f.ip6.int
+domain 0.8.e.f.ip6.arpa
+domain 254.169.in-addr.arpa
+.Ed
+.Sh SEE ALSO
+.\" Cross-references should be ordered by section (low to high), then in
+.\"     alphabetical order.
+.Xr nsswitch.conf 5 ,
+.Xr libnss_mdns 8
+.\" .Sh STANDARDS
+.Sh HISTORY
+.Xr libnss_mdns 8
+was originally written for
+.An NICTA Bq http://www.nicta.com.au/ .
+.Sh AUTHORS
+.An "Andrew White" Bq Andrew.White@nicta.com.au
+.Sh LICENSE
+This software is licensed under the NICTA Public Source License version 1.0
+.Ss NICTA Public Software Licence
+Version 1.0
+.Pp
+Copyright 2004 National ICT Australia Ltd
+.Pp
+All rights reserved.
+.Pp
+By this licence, National ICT Australia Ltd (NICTA) grants permission,
+free of charge, to any person who obtains a copy of this software
+and any associated documentation files ("the Software") to use and
+deal with the Software in source code and binary forms without
+restriction, with or without modification, and to permit persons
+to whom the Software is furnished to do so, provided that the
+following conditions are met:
+.Bl -bullet
+.It
+Redistributions of source code must retain the above copyright
+notice, this list of conditions and the following disclaimers.
+.It
+Redistributions in binary form must reproduce the above copyright
+notice, this list of conditions and the following disclaimers in
+the documentation and/or other materials provided with the
+distribution.
+.It
+The name of NICTA may not be used to endorse or promote products
+derived from this Software without specific prior written permission.
+.El
+.Pp
+EXCEPT AS EXPRESSLY STATED IN THIS LICENCE AND TO THE FULL EXTENT
+PERMITTED BY APPLICABLE LAW, THE SOFTWARE IS PROVIDED "AS-IS" AND
+NICTA MAKES NO REPRESENTATIONS, WARRANTIES OR CONDITIONS OF ANY
+KIND, EXPRESS OR IMPLIED, INCLUDING, WITHOUT LIMITATION, ANY
+REPRESENTATIONS, WARRANTIES OR CONDITIONS REGARDING THE CONTENTS
+OR ACCURACY OF THE SOFTWARE, OR OF TITLE, MERCHANTABILITY, FITNESS
+FOR A PARTICULAR PURPOSE, NONINFRINGEMENT, THE ABSENCE OF LATENT
+OR OTHER DEFECTS, OR THE PRESENCE OR ABSENCE OF ERRORS, WHETHER OR
+NOT DISCOVERABLE.
+.Pp
+TO THE FULL EXTENT PERMITTED BY APPLICABLE LAW, IN NO EVENT WILL
+NICTA BE LIABLE ON ANY LEGAL THEORY (INCLUDING, WITHOUT LIMITATION,
+NEGLIGENCE) FOR ANY LOSS OR DAMAGE WHATSOEVER, INCLUDING (WITHOUT
+LIMITATION) LOSS OF PRODUCTION OR OPERATION TIME, LOSS, DAMAGE OR
+CORRUPTION OF DATA OR RECORDS; OR LOSS OF ANTICIPATED SAVINGS,
+OPPORTUNITY, REVENUE, PROFIT OR GOODWILL, OR OTHER ECONOMIC LOSS;
+OR ANY SPECIAL, INCIDENTAL, INDIRECT, CONSEQUENTIAL, PUNITIVE OR
+EXEMPLARY DAMAGES ARISING OUT OF OR IN CONNECTION WITH THIS LICENCE,
+THE SOFTWARE OR THE USE OF THE SOFTWARE, EVEN IF NICTA HAS BEEN
+ADVISED OF THE POSSIBILITY OF SUCH DAMAGES.
+.Pp
+If applicable legislation implies warranties or conditions, or
+imposes obligations or liability on NICTA in respect of the Software
+that cannot be wholly or partly excluded, restricted or modified,
+NICTA's liability is limited, to the full extent permitted by the
+applicable legislation, at its option, to:
+.Pp
+.Bl -tag -width "a." -compact
+.It a.
+in the case of goods, any one or more of the following:
+.Bl -tag -width "iii." -compact
+.It i.
+the replacement of the goods or the supply of equivalent goods;
+.It ii.
+the repair of the goods;
+.It iii.
+the payment of the cost of replacing the goods or of acquiring
+equivalent goods;
+.It iv.
+the payment of the cost of having the goods repaired; or
+.El
+.It b.
+in the case of services:
+.Bl -tag -width "iii." -compact
+.It i.
+the supplying of the services again; or 
+.It ii.
+the payment of the cost of having the services supplied again.
+.El
+.El
diff --git a/mdnsresponder/mDNSPosix/objects/prod/Client.c.o b/mdnsresponder/mDNSPosix/objects/prod/Client.c.o
new file mode 100644
index 0000000..8e248e3
--- /dev/null
+++ b/mdnsresponder/mDNSPosix/objects/prod/Client.c.o
Binary files differ
diff --git a/mdnsresponder/mDNSPosix/objects/prod/DNSCommon.c.o b/mdnsresponder/mDNSPosix/objects/prod/DNSCommon.c.o
new file mode 100644
index 0000000..5bd0683
--- /dev/null
+++ b/mdnsresponder/mDNSPosix/objects/prod/DNSCommon.c.o
Binary files differ
diff --git a/mdnsresponder/mDNSPosix/objects/prod/DNSDigest.c.o b/mdnsresponder/mDNSPosix/objects/prod/DNSDigest.c.o
new file mode 100644
index 0000000..8a64ed8
--- /dev/null
+++ b/mdnsresponder/mDNSPosix/objects/prod/DNSDigest.c.o
Binary files differ
diff --git a/mdnsresponder/mDNSPosix/objects/prod/ExampleClientApp.c.o b/mdnsresponder/mDNSPosix/objects/prod/ExampleClientApp.c.o
new file mode 100644
index 0000000..522529c
--- /dev/null
+++ b/mdnsresponder/mDNSPosix/objects/prod/ExampleClientApp.c.o
Binary files differ
diff --git a/mdnsresponder/mDNSPosix/objects/prod/GenLinkedList.c.o b/mdnsresponder/mDNSPosix/objects/prod/GenLinkedList.c.o
new file mode 100644
index 0000000..fb830c3
--- /dev/null
+++ b/mdnsresponder/mDNSPosix/objects/prod/GenLinkedList.c.o
Binary files differ
diff --git a/mdnsresponder/mDNSPosix/objects/prod/Identify.c.o b/mdnsresponder/mDNSPosix/objects/prod/Identify.c.o
new file mode 100644
index 0000000..4df1fca
--- /dev/null
+++ b/mdnsresponder/mDNSPosix/objects/prod/Identify.c.o
Binary files differ
diff --git a/mdnsresponder/mDNSPosix/objects/prod/NetMonitor.c.o b/mdnsresponder/mDNSPosix/objects/prod/NetMonitor.c.o
new file mode 100644
index 0000000..fe10ba0
--- /dev/null
+++ b/mdnsresponder/mDNSPosix/objects/prod/NetMonitor.c.o
Binary files differ
diff --git a/mdnsresponder/mDNSPosix/objects/prod/PlatformCommon.c.o b/mdnsresponder/mDNSPosix/objects/prod/PlatformCommon.c.o
new file mode 100644
index 0000000..759dc07
--- /dev/null
+++ b/mdnsresponder/mDNSPosix/objects/prod/PlatformCommon.c.o
Binary files differ
diff --git a/mdnsresponder/mDNSPosix/objects/prod/PosixDaemon.c.o b/mdnsresponder/mDNSPosix/objects/prod/PosixDaemon.c.o
new file mode 100644
index 0000000..a652667
--- /dev/null
+++ b/mdnsresponder/mDNSPosix/objects/prod/PosixDaemon.c.o
Binary files differ
diff --git a/mdnsresponder/mDNSPosix/objects/prod/ProxyResponder.c.o b/mdnsresponder/mDNSPosix/objects/prod/ProxyResponder.c.o
new file mode 100644
index 0000000..3ad8fc8
--- /dev/null
+++ b/mdnsresponder/mDNSPosix/objects/prod/ProxyResponder.c.o
Binary files differ
diff --git a/mdnsresponder/mDNSPosix/objects/prod/Responder.c.o b/mdnsresponder/mDNSPosix/objects/prod/Responder.c.o
new file mode 100644
index 0000000..2d98141
--- /dev/null
+++ b/mdnsresponder/mDNSPosix/objects/prod/Responder.c.o
Binary files differ
diff --git a/mdnsresponder/mDNSPosix/objects/prod/dnsextd_parser.c b/mdnsresponder/mDNSPosix/objects/prod/dnsextd_parser.c
new file mode 100644
index 0000000..4933828
--- /dev/null
+++ b/mdnsresponder/mDNSPosix/objects/prod/dnsextd_parser.c
@@ -0,0 +1,2029 @@
+/* A Bison parser, made by GNU Bison 3.0.2.  */
+
+/* Bison implementation for Yacc-like parsers in C
+
+   Copyright (C) 1984, 1989-1990, 2000-2013 Free Software Foundation, Inc.
+
+   This program 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.
+
+   This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* As a special exception, you may create a larger work that contains
+   part or all of the Bison parser skeleton and distribute that work
+   under terms of your choice, so long as that work isn't itself a
+   parser generator using the skeleton or a modified version thereof
+   as a parser skeleton.  Alternatively, if you modify or redistribute
+   the parser skeleton itself, you may (at your option) remove this
+   special exception, which will cause the skeleton and the resulting
+   Bison output files to be licensed under the GNU General Public
+   License without this special exception.
+
+   This special exception was added by the Free Software Foundation in
+   version 2.2 of Bison.  */
+
+/* C LALR(1) parser skeleton written by Richard Stallman, by
+   simplifying the original so-called "semantic" parser.  */
+
+/* All symbols defined below should begin with yy or YY, to avoid
+   infringing on user name space.  This should be done even for local
+   variables, as they might otherwise be expanded by user macros.
+   There are some unavoidable exceptions within include files to
+   define necessary library symbols; they are noted "INFRINGES ON
+   USER NAME SPACE" below.  */
+
+/* Identify Bison output.  */
+#define YYBISON 1
+
+/* Bison version.  */
+#define YYBISON_VERSION "3.0.2"
+
+/* Skeleton name.  */
+#define YYSKELETON_NAME "yacc.c"
+
+/* Pure parsers.  */
+#define YYPURE 0
+
+/* Push parsers.  */
+#define YYPUSH 0
+
+/* Pull parsers.  */
+#define YYPULL 1
+
+
+
+
+/* Copy the first part of user declarations.  */
+#line 18 "../mDNSShared/dnsextd_parser.y" /* yacc.c:339  */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "mDNSEmbeddedAPI.h"
+#include "DebugServices.h"
+#include "dnsextd.h"
+
+void yyerror( const char* error );
+int  yylex(void);
+
+
+typedef struct StringListElem
+{
+	char					*	string;
+	struct StringListElem	*	next;
+} StringListElem;
+
+
+typedef struct OptionsInfo
+{
+	char	server_address[ 256 ];
+	int		server_port;
+	char	source_address[ 256 ];
+	int		source_port;
+	int		private_port;
+	int		llq_port;
+} OptionsInfo;
+
+
+typedef struct ZoneInfo
+{
+	char	name[ 256 ];
+	char	certificate_name[ 256 ];
+	char	allow_clients_file[ 256 ];
+	char	allow_clients[ 256 ];
+	char	key[ 256 ];
+} ZoneInfo;
+
+
+typedef struct KeySpec
+{
+	char 				name[ 256 ];
+	char				algorithm[ 256 ];
+	char				secret[ 256 ];
+	struct KeySpec	*	next;
+} KeySpec;
+
+
+typedef struct ZoneSpec
+{
+	char				name[ 256 ];
+	DNSZoneSpecType		type;
+	StringListElem	*	allowUpdate;
+	StringListElem	*	allowQuery;
+	char				key[ 256 ];
+	struct ZoneSpec	*	next;
+} ZoneSpec;
+
+
+static StringListElem	*	g_stringList = NULL;
+static KeySpec			*	g_keys;
+static ZoneSpec			*	g_zones;
+static ZoneSpec				g_zoneSpec;
+static const char		*	g_filename;
+
+#define YYPARSE_PARAM  context
+
+void
+SetupOptions
+	(
+	OptionsInfo	*	info,
+	void		*	context
+	);
+
+
+#line 143 "objects/prod/dnsextd_parser.c" /* yacc.c:339  */
+
+# ifndef YY_NULLPTR
+#  if defined __cplusplus && 201103L <= __cplusplus
+#   define YY_NULLPTR nullptr
+#  else
+#   define YY_NULLPTR 0
+#  endif
+# endif
+
+/* Enabling verbose error messages.  */
+#ifdef YYERROR_VERBOSE
+# undef YYERROR_VERBOSE
+# define YYERROR_VERBOSE 1
+#else
+# define YYERROR_VERBOSE 0
+#endif
+
+/* In a future release of Bison, this section will be replaced
+   by #include "dnsextd_parser.h".  */
+#ifndef YY_YY_OBJECTS_PROD_DNSEXTD_PARSER_H_INCLUDED
+# define YY_YY_OBJECTS_PROD_DNSEXTD_PARSER_H_INCLUDED
+/* Debug traces.  */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+#if YYDEBUG
+extern int yydebug;
+#endif
+
+/* Token type.  */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+  enum yytokentype
+  {
+    OPTIONS = 258,
+    LISTEN_ON = 259,
+    NAMESERVER = 260,
+    PORT = 261,
+    ADDRESS = 262,
+    LLQ = 263,
+    PUBLIC = 264,
+    PRIVATE = 265,
+    ALLOWUPDATE = 266,
+    ALLOWQUERY = 267,
+    KEY = 268,
+    ALGORITHM = 269,
+    SECRET = 270,
+    ISSUER = 271,
+    SERIAL = 272,
+    ZONE = 273,
+    TYPE = 274,
+    ALLOW = 275,
+    OBRACE = 276,
+    EBRACE = 277,
+    SEMICOLON = 278,
+    IN = 279,
+    DOTTED_DECIMAL_ADDRESS = 280,
+    WILDCARD = 281,
+    DOMAINNAME = 282,
+    HOSTNAME = 283,
+    QUOTEDSTRING = 284,
+    NUMBER = 285
+  };
+#endif
+
+/* Value type.  */
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE YYSTYPE;
+union YYSTYPE
+{
+#line 96 "../mDNSShared/dnsextd_parser.y" /* yacc.c:355  */
+
+	int			number;
+	char	*	string;
+
+#line 219 "objects/prod/dnsextd_parser.c" /* yacc.c:355  */
+};
+# define YYSTYPE_IS_TRIVIAL 1
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+
+extern YYSTYPE yylval;
+
+int yyparse (void);
+
+#endif /* !YY_YY_OBJECTS_PROD_DNSEXTD_PARSER_H_INCLUDED  */
+
+/* Copy the second part of user declarations.  */
+
+#line 234 "objects/prod/dnsextd_parser.c" /* yacc.c:358  */
+
+#ifdef short
+# undef short
+#endif
+
+#ifdef YYTYPE_UINT8
+typedef YYTYPE_UINT8 yytype_uint8;
+#else
+typedef unsigned char yytype_uint8;
+#endif
+
+#ifdef YYTYPE_INT8
+typedef YYTYPE_INT8 yytype_int8;
+#else
+typedef signed char yytype_int8;
+#endif
+
+#ifdef YYTYPE_UINT16
+typedef YYTYPE_UINT16 yytype_uint16;
+#else
+typedef unsigned short int yytype_uint16;
+#endif
+
+#ifdef YYTYPE_INT16
+typedef YYTYPE_INT16 yytype_int16;
+#else
+typedef short int yytype_int16;
+#endif
+
+#ifndef YYSIZE_T
+# ifdef __SIZE_TYPE__
+#  define YYSIZE_T __SIZE_TYPE__
+# elif defined size_t
+#  define YYSIZE_T size_t
+# elif ! defined YYSIZE_T
+#  include <stddef.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYSIZE_T size_t
+# else
+#  define YYSIZE_T unsigned int
+# endif
+#endif
+
+#define YYSIZE_MAXIMUM ((YYSIZE_T) -1)
+
+#ifndef YY_
+# if defined YYENABLE_NLS && YYENABLE_NLS
+#  if ENABLE_NLS
+#   include <libintl.h> /* INFRINGES ON USER NAME SPACE */
+#   define YY_(Msgid) dgettext ("bison-runtime", Msgid)
+#  endif
+# endif
+# ifndef YY_
+#  define YY_(Msgid) Msgid
+# endif
+#endif
+
+#ifndef YY_ATTRIBUTE
+# if (defined __GNUC__                                               \
+      && (2 < __GNUC__ || (__GNUC__ == 2 && 96 <= __GNUC_MINOR__)))  \
+     || defined __SUNPRO_C && 0x5110 <= __SUNPRO_C
+#  define YY_ATTRIBUTE(Spec) __attribute__(Spec)
+# else
+#  define YY_ATTRIBUTE(Spec) /* empty */
+# endif
+#endif
+
+#ifndef YY_ATTRIBUTE_PURE
+# define YY_ATTRIBUTE_PURE   YY_ATTRIBUTE ((__pure__))
+#endif
+
+#ifndef YY_ATTRIBUTE_UNUSED
+# define YY_ATTRIBUTE_UNUSED YY_ATTRIBUTE ((__unused__))
+#endif
+
+#if !defined _Noreturn \
+     && (!defined __STDC_VERSION__ || __STDC_VERSION__ < 201112)
+# if defined _MSC_VER && 1200 <= _MSC_VER
+#  define _Noreturn __declspec (noreturn)
+# else
+#  define _Noreturn YY_ATTRIBUTE ((__noreturn__))
+# endif
+#endif
+
+/* Suppress unused-variable warnings by "using" E.  */
+#if ! defined lint || defined __GNUC__
+# define YYUSE(E) ((void) (E))
+#else
+# define YYUSE(E) /* empty */
+#endif
+
+#if defined __GNUC__ && 407 <= __GNUC__ * 100 + __GNUC_MINOR__
+/* Suppress an incorrect diagnostic about yylval being uninitialized.  */
+# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN \
+    _Pragma ("GCC diagnostic push") \
+    _Pragma ("GCC diagnostic ignored \"-Wuninitialized\"")\
+    _Pragma ("GCC diagnostic ignored \"-Wmaybe-uninitialized\"")
+# define YY_IGNORE_MAYBE_UNINITIALIZED_END \
+    _Pragma ("GCC diagnostic pop")
+#else
+# define YY_INITIAL_VALUE(Value) Value
+#endif
+#ifndef YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+# define YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+# define YY_IGNORE_MAYBE_UNINITIALIZED_END
+#endif
+#ifndef YY_INITIAL_VALUE
+# define YY_INITIAL_VALUE(Value) /* Nothing. */
+#endif
+
+
+#if ! defined yyoverflow || YYERROR_VERBOSE
+
+/* The parser invokes alloca or malloc; define the necessary symbols.  */
+
+# ifdef YYSTACK_USE_ALLOCA
+#  if YYSTACK_USE_ALLOCA
+#   ifdef __GNUC__
+#    define YYSTACK_ALLOC __builtin_alloca
+#   elif defined __BUILTIN_VA_ARG_INCR
+#    include <alloca.h> /* INFRINGES ON USER NAME SPACE */
+#   elif defined _AIX
+#    define YYSTACK_ALLOC __alloca
+#   elif defined _MSC_VER
+#    include <malloc.h> /* INFRINGES ON USER NAME SPACE */
+#    define alloca _alloca
+#   else
+#    define YYSTACK_ALLOC alloca
+#    if ! defined _ALLOCA_H && ! defined EXIT_SUCCESS
+#     include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+      /* Use EXIT_SUCCESS as a witness for stdlib.h.  */
+#     ifndef EXIT_SUCCESS
+#      define EXIT_SUCCESS 0
+#     endif
+#    endif
+#   endif
+#  endif
+# endif
+
+# ifdef YYSTACK_ALLOC
+   /* Pacify GCC's 'empty if-body' warning.  */
+#  define YYSTACK_FREE(Ptr) do { /* empty */; } while (0)
+#  ifndef YYSTACK_ALLOC_MAXIMUM
+    /* The OS might guarantee only one guard page at the bottom of the stack,
+       and a page size can be as small as 4096 bytes.  So we cannot safely
+       invoke alloca (N) if N exceeds 4096.  Use a slightly smaller number
+       to allow for a few compiler-allocated temporary stack slots.  */
+#   define YYSTACK_ALLOC_MAXIMUM 4032 /* reasonable circa 2006 */
+#  endif
+# else
+#  define YYSTACK_ALLOC YYMALLOC
+#  define YYSTACK_FREE YYFREE
+#  ifndef YYSTACK_ALLOC_MAXIMUM
+#   define YYSTACK_ALLOC_MAXIMUM YYSIZE_MAXIMUM
+#  endif
+#  if (defined __cplusplus && ! defined EXIT_SUCCESS \
+       && ! ((defined YYMALLOC || defined malloc) \
+             && (defined YYFREE || defined free)))
+#   include <stdlib.h> /* INFRINGES ON USER NAME SPACE */
+#   ifndef EXIT_SUCCESS
+#    define EXIT_SUCCESS 0
+#   endif
+#  endif
+#  ifndef YYMALLOC
+#   define YYMALLOC malloc
+#   if ! defined malloc && ! defined EXIT_SUCCESS
+void *malloc (YYSIZE_T); /* INFRINGES ON USER NAME SPACE */
+#   endif
+#  endif
+#  ifndef YYFREE
+#   define YYFREE free
+#   if ! defined free && ! defined EXIT_SUCCESS
+void free (void *); /* INFRINGES ON USER NAME SPACE */
+#   endif
+#  endif
+# endif
+#endif /* ! defined yyoverflow || YYERROR_VERBOSE */
+
+
+#if (! defined yyoverflow \
+     && (! defined __cplusplus \
+         || (defined YYSTYPE_IS_TRIVIAL && YYSTYPE_IS_TRIVIAL)))
+
+/* A type that is properly aligned for any stack member.  */
+union yyalloc
+{
+  yytype_int16 yyss_alloc;
+  YYSTYPE yyvs_alloc;
+};
+
+/* The size of the maximum gap between one aligned stack and the next.  */
+# define YYSTACK_GAP_MAXIMUM (sizeof (union yyalloc) - 1)
+
+/* The size of an array large to enough to hold all stacks, each with
+   N elements.  */
+# define YYSTACK_BYTES(N) \
+     ((N) * (sizeof (yytype_int16) + sizeof (YYSTYPE)) \
+      + YYSTACK_GAP_MAXIMUM)
+
+# define YYCOPY_NEEDED 1
+
+/* Relocate STACK from its old location to the new one.  The
+   local variables YYSIZE and YYSTACKSIZE give the old and new number of
+   elements in the stack, and YYPTR gives the new location of the
+   stack.  Advance YYPTR to a properly aligned location for the next
+   stack.  */
+# define YYSTACK_RELOCATE(Stack_alloc, Stack)                           \
+    do                                                                  \
+      {                                                                 \
+        YYSIZE_T yynewbytes;                                            \
+        YYCOPY (&yyptr->Stack_alloc, Stack, yysize);                    \
+        Stack = &yyptr->Stack_alloc;                                    \
+        yynewbytes = yystacksize * sizeof (*Stack) + YYSTACK_GAP_MAXIMUM; \
+        yyptr += yynewbytes / sizeof (*yyptr);                          \
+      }                                                                 \
+    while (0)
+
+#endif
+
+#if defined YYCOPY_NEEDED && YYCOPY_NEEDED
+/* Copy COUNT objects from SRC to DST.  The source and destination do
+   not overlap.  */
+# ifndef YYCOPY
+#  if defined __GNUC__ && 1 < __GNUC__
+#   define YYCOPY(Dst, Src, Count) \
+      __builtin_memcpy (Dst, Src, (Count) * sizeof (*(Src)))
+#  else
+#   define YYCOPY(Dst, Src, Count)              \
+      do                                        \
+        {                                       \
+          YYSIZE_T yyi;                         \
+          for (yyi = 0; yyi < (Count); yyi++)   \
+            (Dst)[yyi] = (Src)[yyi];            \
+        }                                       \
+      while (0)
+#  endif
+# endif
+#endif /* !YYCOPY_NEEDED */
+
+/* YYFINAL -- State number of the termination state.  */
+#define YYFINAL  2
+/* YYLAST -- Last index in YYTABLE.  */
+#define YYLAST   63
+
+/* YYNTOKENS -- Number of terminals.  */
+#define YYNTOKENS  31
+/* YYNNTS -- Number of nonterminals.  */
+#define YYNNTS  22
+/* YYNRULES -- Number of rules.  */
+#define YYNRULES  43
+/* YYNSTATES -- Number of states.  */
+#define YYNSTATES  79
+
+/* YYTRANSLATE[YYX] -- Symbol number corresponding to YYX as returned
+   by yylex, with out-of-bounds checking.  */
+#define YYUNDEFTOK  2
+#define YYMAXUTOK   285
+
+#define YYTRANSLATE(YYX)                                                \
+  ((unsigned int) (YYX) <= YYMAXUTOK ? yytranslate[YYX] : YYUNDEFTOK)
+
+/* YYTRANSLATE[TOKEN-NUM] -- Symbol number corresponding to TOKEN-NUM
+   as returned by yylex, without out-of-bounds checking.  */
+static const yytype_uint8 yytranslate[] =
+{
+       0,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     2,     2,     2,     2,
+       2,     2,     2,     2,     2,     2,     1,     2,     3,     4,
+       5,     6,     7,     8,     9,    10,    11,    12,    13,    14,
+      15,    16,    17,    18,    19,    20,    21,    22,    23,    24,
+      25,    26,    27,    28,    29,    30
+};
+
+#if YYDEBUG
+  /* YYRLINE[YYN] -- Source line where rule number YYN was defined.  */
+static const yytype_uint16 yyrline[] =
+{
+       0,   135,   135,   137,   142,   144,   146,   151,   158,   161,
+     163,   168,   170,   174,   178,   182,   186,   191,   198,   219,
+     241,   265,   267,   269,   273,   278,   283,   289,   297,   301,
+     303,   309,   316,   320,   322,   328,   349,   351,   353,   357,
+     360,   362,   366,   371
+};
+#endif
+
+#if YYDEBUG || YYERROR_VERBOSE || 0
+/* YYTNAME[SYMBOL-NUM] -- String name of the symbol SYMBOL-NUM.
+   First, the terminals, then, starting at YYNTOKENS, nonterminals.  */
+static const char *const yytname[] =
+{
+  "$end", "error", "$undefined", "OPTIONS", "LISTEN_ON", "NAMESERVER",
+  "PORT", "ADDRESS", "LLQ", "PUBLIC", "PRIVATE", "ALLOWUPDATE",
+  "ALLOWQUERY", "KEY", "ALGORITHM", "SECRET", "ISSUER", "SERIAL", "ZONE",
+  "TYPE", "ALLOW", "OBRACE", "EBRACE", "SEMICOLON", "IN",
+  "DOTTED_DECIMAL_ADDRESS", "WILDCARD", "DOMAINNAME", "HOSTNAME",
+  "QUOTEDSTRING", "NUMBER", "$accept", "commands", "command",
+  "options_set", "optionscontent", "optionsstatements", "optionsstatement",
+  "key_set", "zone_set", "zonecontent", "zonestatements", "zonestatement",
+  "addresscontent", "addressstatements", "addressstatement", "keycontent",
+  "keystatements", "keystatement", "networkaddress", "block", "statements",
+  "statement", YY_NULLPTR
+};
+#endif
+
+# ifdef YYPRINT
+/* YYTOKNUM[NUM] -- (External) token number corresponding to the
+   (internal) symbol number NUM (which must be that of a token).  */
+static const yytype_uint16 yytoknum[] =
+{
+       0,   256,   257,   258,   259,   260,   261,   262,   263,   264,
+     265,   266,   267,   268,   269,   270,   271,   272,   273,   274,
+     275,   276,   277,   278,   279,   280,   281,   282,   283,   284,
+     285
+};
+# endif
+
+#define YYPACT_NINF -19
+
+#define yypact_value_is_default(Yystate) \
+  (!!((Yystate) == (-19)))
+
+#define YYTABLE_NINF -1
+
+#define yytable_value_is_error(Yytable_value) \
+  0
+
+  /* YYPACT[STATE-NUM] -- Index in YYTABLE of the portion describing
+     STATE-NUM.  */
+static const yytype_int8 yypact[] =
+{
+     -19,     2,   -19,   -11,   -18,     6,    13,   -19,   -19,   -19,
+     -19,   -19,     8,    -7,   -19,    -4,    22,   -19,    17,   -19,
+       1,    32,    34,    35,   -19,    19,    -8,    14,    -3,   -19,
+      15,   -19,   -19,     0,    16,    18,   -19,   -19,   -19,   -19,
+     -19,    21,    26,    26,    23,   -19,    27,    28,     5,   -19,
+     -19,   -19,    45,   -19,   -19,    12,    30,   -19,   -19,   -19,
+     -19,   -19,   -19,   -19,   -19,   -19,    31,    25,    33,   -19,
+     -10,   -19,   -19,   -19,    36,   -19,    37,   -19,   -19
+};
+
+  /* YYDEFACT[STATE-NUM] -- Default reduction number in state STATE-NUM.
+     Performed when YYTABLE does not specify something else to do.  Zero
+     means the default is an error.  */
+static const yytype_uint8 yydefact[] =
+{
+       2,     0,     1,     0,     0,     0,     0,     4,     6,     5,
+       9,     7,     0,     0,     3,    40,     0,    22,     0,    19,
+       0,     0,     0,     0,     8,     0,    11,     0,     0,    20,
+       0,    29,    12,     0,     0,     0,    10,    22,    43,    42,
+      41,     0,     0,     0,     0,    21,     0,     0,     0,    36,
+      38,    37,    14,    17,    16,     0,     0,    33,    26,    27,
+      24,    25,    23,    13,    28,    31,     0,     0,     0,    18,
+       0,    30,    15,    39,     0,    32,     0,    35,    34
+};
+
+  /* YYPGOTO[NTERM-NUM].  */
+static const yytype_int8 yypgoto[] =
+{
+     -19,   -19,   -19,   -19,   -19,   -19,   -19,   -19,   -19,    39,
+      24,   -19,    11,   -19,   -19,    10,   -19,   -19,   -19,   -19,
+     -19,   -19
+};
+
+  /* YYDEFGOTO[NTERM-NUM].  */
+static const yytype_int8 yydefgoto[] =
+{
+      -1,     1,     6,     7,    11,    15,    25,     8,     9,    19,
+      28,    46,    32,    48,    66,    58,    70,    76,    52,    39,
+      26,    40
+};
+
+  /* YYTABLE[YYPACT[STATE-NUM]] -- What to do in state STATE-NUM.  If
+     positive, shift that token.  If negative, reduce the rule whose
+     number is the opposite.  If YYTABLE_NINF, syntax error.  */
+static const yytype_uint8 yytable[] =
+{
+      20,    21,     2,    74,    22,     3,    23,    30,    42,    43,
+      10,    12,    75,    37,    17,     4,    44,    18,    24,    45,
+       5,    38,    31,    42,    43,    49,    50,    64,    51,    16,
+      65,    44,    60,    61,    68,    13,    14,    27,    17,    33,
+      34,    35,    36,    41,    56,    47,    53,    57,    54,    31,
+      62,    67,    69,    59,    71,    72,    73,    29,    63,     0,
+      78,    55,     0,    77
+};
+
+static const yytype_int8 yycheck[] =
+{
+       4,     5,     0,    13,     8,     3,    10,     6,    11,    12,
+      21,    29,    22,    21,    21,    13,    19,    24,    22,    22,
+      18,    29,    21,    11,    12,    25,    26,    22,    28,    21,
+      25,    19,     9,    10,    22,    29,    23,    15,    21,     7,
+       6,     6,    23,    29,    23,    30,    30,    21,    30,    21,
+      23,     6,    22,    43,    23,    30,    23,    18,    47,    -1,
+      23,    37,    -1,    27
+};
+
+  /* YYSTOS[STATE-NUM] -- The (internal number of the) accessing
+     symbol of state STATE-NUM.  */
+static const yytype_uint8 yystos[] =
+{
+       0,    32,     0,     3,    13,    18,    33,    34,    38,    39,
+      21,    35,    29,    29,    23,    36,    21,    21,    24,    40,
+       4,     5,     8,    10,    22,    37,    51,    15,    41,    40,
+       6,    21,    43,     7,     6,     6,    23,    21,    29,    50,
+      52,    29,    11,    12,    19,    22,    42,    30,    44,    25,
+      26,    28,    49,    30,    30,    41,    23,    21,    46,    46,
+       9,    10,    23,    43,    22,    25,    45,     6,    22,    22,
+      47,    23,    30,    23,    13,    22,    48,    27,    23
+};
+
+  /* YYR1[YYN] -- Symbol number of symbol that rule YYN derives.  */
+static const yytype_uint8 yyr1[] =
+{
+       0,    31,    32,    32,    33,    33,    33,    34,    35,    36,
+      36,    37,    37,    37,    37,    37,    37,    37,    38,    39,
+      39,    40,    41,    41,    42,    42,    42,    42,    43,    44,
+      44,    45,    46,    47,    47,    48,    49,    49,    49,    50,
+      51,    51,    52,    52
+};
+
+  /* YYR2[YYN] -- Number of symbols on the right hand side of rule YYN.  */
+static const yytype_uint8 yyr2[] =
+{
+       0,     2,     0,     3,     1,     1,     1,     2,     3,     0,
+       3,     1,     2,     4,     3,     5,     3,     3,     7,     3,
+       4,     3,     0,     3,     2,     2,     2,     2,     3,     0,
+       3,     1,     3,     0,     3,     2,     1,     1,     1,     4,
+       0,     2,     1,     1
+};
+
+
+#define yyerrok         (yyerrstatus = 0)
+#define yyclearin       (yychar = YYEMPTY)
+#define YYEMPTY         (-2)
+#define YYEOF           0
+
+#define YYACCEPT        goto yyacceptlab
+#define YYABORT         goto yyabortlab
+#define YYERROR         goto yyerrorlab
+
+
+#define YYRECOVERING()  (!!yyerrstatus)
+
+#define YYBACKUP(Token, Value)                                  \
+do                                                              \
+  if (yychar == YYEMPTY)                                        \
+    {                                                           \
+      yychar = (Token);                                         \
+      yylval = (Value);                                         \
+      YYPOPSTACK (yylen);                                       \
+      yystate = *yyssp;                                         \
+      goto yybackup;                                            \
+    }                                                           \
+  else                                                          \
+    {                                                           \
+      yyerror (YY_("syntax error: cannot back up")); \
+      YYERROR;                                                  \
+    }                                                           \
+while (0)
+
+/* Error token number */
+#define YYTERROR        1
+#define YYERRCODE       256
+
+
+
+/* Enable debugging if requested.  */
+#if YYDEBUG
+
+# ifndef YYFPRINTF
+#  include <stdio.h> /* INFRINGES ON USER NAME SPACE */
+#  define YYFPRINTF fprintf
+# endif
+
+# define YYDPRINTF(Args)                        \
+do {                                            \
+  if (yydebug)                                  \
+    YYFPRINTF Args;                             \
+} while (0)
+
+/* This macro is provided for backward compatibility. */
+#ifndef YY_LOCATION_PRINT
+# define YY_LOCATION_PRINT(File, Loc) ((void) 0)
+#endif
+
+
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)                    \
+do {                                                                      \
+  if (yydebug)                                                            \
+    {                                                                     \
+      YYFPRINTF (stderr, "%s ", Title);                                   \
+      yy_symbol_print (stderr,                                            \
+                  Type, Value); \
+      YYFPRINTF (stderr, "\n");                                           \
+    }                                                                     \
+} while (0)
+
+
+/*----------------------------------------.
+| Print this symbol's value on YYOUTPUT.  |
+`----------------------------------------*/
+
+static void
+yy_symbol_value_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+{
+  FILE *yyo = yyoutput;
+  YYUSE (yyo);
+  if (!yyvaluep)
+    return;
+# ifdef YYPRINT
+  if (yytype < YYNTOKENS)
+    YYPRINT (yyoutput, yytoknum[yytype], *yyvaluep);
+# endif
+  YYUSE (yytype);
+}
+
+
+/*--------------------------------.
+| Print this symbol on YYOUTPUT.  |
+`--------------------------------*/
+
+static void
+yy_symbol_print (FILE *yyoutput, int yytype, YYSTYPE const * const yyvaluep)
+{
+  YYFPRINTF (yyoutput, "%s %s (",
+             yytype < YYNTOKENS ? "token" : "nterm", yytname[yytype]);
+
+  yy_symbol_value_print (yyoutput, yytype, yyvaluep);
+  YYFPRINTF (yyoutput, ")");
+}
+
+/*------------------------------------------------------------------.
+| yy_stack_print -- Print the state stack from its BOTTOM up to its |
+| TOP (included).                                                   |
+`------------------------------------------------------------------*/
+
+static void
+yy_stack_print (yytype_int16 *yybottom, yytype_int16 *yytop)
+{
+  YYFPRINTF (stderr, "Stack now");
+  for (; yybottom <= yytop; yybottom++)
+    {
+      int yybot = *yybottom;
+      YYFPRINTF (stderr, " %d", yybot);
+    }
+  YYFPRINTF (stderr, "\n");
+}
+
+# define YY_STACK_PRINT(Bottom, Top)                            \
+do {                                                            \
+  if (yydebug)                                                  \
+    yy_stack_print ((Bottom), (Top));                           \
+} while (0)
+
+
+/*------------------------------------------------.
+| Report that the YYRULE is going to be reduced.  |
+`------------------------------------------------*/
+
+static void
+yy_reduce_print (yytype_int16 *yyssp, YYSTYPE *yyvsp, int yyrule)
+{
+  unsigned long int yylno = yyrline[yyrule];
+  int yynrhs = yyr2[yyrule];
+  int yyi;
+  YYFPRINTF (stderr, "Reducing stack by rule %d (line %lu):\n",
+             yyrule - 1, yylno);
+  /* The symbols being reduced.  */
+  for (yyi = 0; yyi < yynrhs; yyi++)
+    {
+      YYFPRINTF (stderr, "   $%d = ", yyi + 1);
+      yy_symbol_print (stderr,
+                       yystos[yyssp[yyi + 1 - yynrhs]],
+                       &(yyvsp[(yyi + 1) - (yynrhs)])
+                                              );
+      YYFPRINTF (stderr, "\n");
+    }
+}
+
+# define YY_REDUCE_PRINT(Rule)          \
+do {                                    \
+  if (yydebug)                          \
+    yy_reduce_print (yyssp, yyvsp, Rule); \
+} while (0)
+
+/* Nonzero means print parse trace.  It is left uninitialized so that
+   multiple parsers can coexist.  */
+int yydebug;
+#else /* !YYDEBUG */
+# define YYDPRINTF(Args)
+# define YY_SYMBOL_PRINT(Title, Type, Value, Location)
+# define YY_STACK_PRINT(Bottom, Top)
+# define YY_REDUCE_PRINT(Rule)
+#endif /* !YYDEBUG */
+
+
+/* YYINITDEPTH -- initial size of the parser's stacks.  */
+#ifndef YYINITDEPTH
+# define YYINITDEPTH 200
+#endif
+
+/* YYMAXDEPTH -- maximum size the stacks can grow to (effective only
+   if the built-in stack extension method is used).
+
+   Do not make this value too large; the results are undefined if
+   YYSTACK_ALLOC_MAXIMUM < YYSTACK_BYTES (YYMAXDEPTH)
+   evaluated with infinite-precision integer arithmetic.  */
+
+#ifndef YYMAXDEPTH
+# define YYMAXDEPTH 10000
+#endif
+
+
+#if YYERROR_VERBOSE
+
+# ifndef yystrlen
+#  if defined __GLIBC__ && defined _STRING_H
+#   define yystrlen strlen
+#  else
+/* Return the length of YYSTR.  */
+static YYSIZE_T
+yystrlen (const char *yystr)
+{
+  YYSIZE_T yylen;
+  for (yylen = 0; yystr[yylen]; yylen++)
+    continue;
+  return yylen;
+}
+#  endif
+# endif
+
+# ifndef yystpcpy
+#  if defined __GLIBC__ && defined _STRING_H && defined _GNU_SOURCE
+#   define yystpcpy stpcpy
+#  else
+/* Copy YYSRC to YYDEST, returning the address of the terminating '\0' in
+   YYDEST.  */
+static char *
+yystpcpy (char *yydest, const char *yysrc)
+{
+  char *yyd = yydest;
+  const char *yys = yysrc;
+
+  while ((*yyd++ = *yys++) != '\0')
+    continue;
+
+  return yyd - 1;
+}
+#  endif
+# endif
+
+# ifndef yytnamerr
+/* Copy to YYRES the contents of YYSTR after stripping away unnecessary
+   quotes and backslashes, so that it's suitable for yyerror.  The
+   heuristic is that double-quoting is unnecessary unless the string
+   contains an apostrophe, a comma, or backslash (other than
+   backslash-backslash).  YYSTR is taken from yytname.  If YYRES is
+   null, do not copy; instead, return the length of what the result
+   would have been.  */
+static YYSIZE_T
+yytnamerr (char *yyres, const char *yystr)
+{
+  if (*yystr == '"')
+    {
+      YYSIZE_T yyn = 0;
+      char const *yyp = yystr;
+
+      for (;;)
+        switch (*++yyp)
+          {
+          case '\'':
+          case ',':
+            goto do_not_strip_quotes;
+
+          case '\\':
+            if (*++yyp != '\\')
+              goto do_not_strip_quotes;
+            /* Fall through.  */
+          default:
+            if (yyres)
+              yyres[yyn] = *yyp;
+            yyn++;
+            break;
+
+          case '"':
+            if (yyres)
+              yyres[yyn] = '\0';
+            return yyn;
+          }
+    do_not_strip_quotes: ;
+    }
+
+  if (! yyres)
+    return yystrlen (yystr);
+
+  return yystpcpy (yyres, yystr) - yyres;
+}
+# endif
+
+/* Copy into *YYMSG, which is of size *YYMSG_ALLOC, an error message
+   about the unexpected token YYTOKEN for the state stack whose top is
+   YYSSP.
+
+   Return 0 if *YYMSG was successfully written.  Return 1 if *YYMSG is
+   not large enough to hold the message.  In that case, also set
+   *YYMSG_ALLOC to the required number of bytes.  Return 2 if the
+   required number of bytes is too large to store.  */
+static int
+yysyntax_error (YYSIZE_T *yymsg_alloc, char **yymsg,
+                yytype_int16 *yyssp, int yytoken)
+{
+  YYSIZE_T yysize0 = yytnamerr (YY_NULLPTR, yytname[yytoken]);
+  YYSIZE_T yysize = yysize0;
+  enum { YYERROR_VERBOSE_ARGS_MAXIMUM = 5 };
+  /* Internationalized format string. */
+  const char *yyformat = YY_NULLPTR;
+  /* Arguments of yyformat. */
+  char const *yyarg[YYERROR_VERBOSE_ARGS_MAXIMUM];
+  /* Number of reported tokens (one for the "unexpected", one per
+     "expected"). */
+  int yycount = 0;
+
+  /* There are many possibilities here to consider:
+     - If this state is a consistent state with a default action, then
+       the only way this function was invoked is if the default action
+       is an error action.  In that case, don't check for expected
+       tokens because there are none.
+     - The only way there can be no lookahead present (in yychar) is if
+       this state is a consistent state with a default action.  Thus,
+       detecting the absence of a lookahead is sufficient to determine
+       that there is no unexpected or expected token to report.  In that
+       case, just report a simple "syntax error".
+     - Don't assume there isn't a lookahead just because this state is a
+       consistent state with a default action.  There might have been a
+       previous inconsistent state, consistent state with a non-default
+       action, or user semantic action that manipulated yychar.
+     - Of course, the expected token list depends on states to have
+       correct lookahead information, and it depends on the parser not
+       to perform extra reductions after fetching a lookahead from the
+       scanner and before detecting a syntax error.  Thus, state merging
+       (from LALR or IELR) and default reductions corrupt the expected
+       token list.  However, the list is correct for canonical LR with
+       one exception: it will still contain any token that will not be
+       accepted due to an error action in a later state.
+  */
+  if (yytoken != YYEMPTY)
+    {
+      int yyn = yypact[*yyssp];
+      yyarg[yycount++] = yytname[yytoken];
+      if (!yypact_value_is_default (yyn))
+        {
+          /* Start YYX at -YYN if negative to avoid negative indexes in
+             YYCHECK.  In other words, skip the first -YYN actions for
+             this state because they are default actions.  */
+          int yyxbegin = yyn < 0 ? -yyn : 0;
+          /* Stay within bounds of both yycheck and yytname.  */
+          int yychecklim = YYLAST - yyn + 1;
+          int yyxend = yychecklim < YYNTOKENS ? yychecklim : YYNTOKENS;
+          int yyx;
+
+          for (yyx = yyxbegin; yyx < yyxend; ++yyx)
+            if (yycheck[yyx + yyn] == yyx && yyx != YYTERROR
+                && !yytable_value_is_error (yytable[yyx + yyn]))
+              {
+                if (yycount == YYERROR_VERBOSE_ARGS_MAXIMUM)
+                  {
+                    yycount = 1;
+                    yysize = yysize0;
+                    break;
+                  }
+                yyarg[yycount++] = yytname[yyx];
+                {
+                  YYSIZE_T yysize1 = yysize + yytnamerr (YY_NULLPTR, yytname[yyx]);
+                  if (! (yysize <= yysize1
+                         && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+                    return 2;
+                  yysize = yysize1;
+                }
+              }
+        }
+    }
+
+  switch (yycount)
+    {
+# define YYCASE_(N, S)                      \
+      case N:                               \
+        yyformat = S;                       \
+      break
+      YYCASE_(0, YY_("syntax error"));
+      YYCASE_(1, YY_("syntax error, unexpected %s"));
+      YYCASE_(2, YY_("syntax error, unexpected %s, expecting %s"));
+      YYCASE_(3, YY_("syntax error, unexpected %s, expecting %s or %s"));
+      YYCASE_(4, YY_("syntax error, unexpected %s, expecting %s or %s or %s"));
+      YYCASE_(5, YY_("syntax error, unexpected %s, expecting %s or %s or %s or %s"));
+# undef YYCASE_
+    }
+
+  {
+    YYSIZE_T yysize1 = yysize + yystrlen (yyformat);
+    if (! (yysize <= yysize1 && yysize1 <= YYSTACK_ALLOC_MAXIMUM))
+      return 2;
+    yysize = yysize1;
+  }
+
+  if (*yymsg_alloc < yysize)
+    {
+      *yymsg_alloc = 2 * yysize;
+      if (! (yysize <= *yymsg_alloc
+             && *yymsg_alloc <= YYSTACK_ALLOC_MAXIMUM))
+        *yymsg_alloc = YYSTACK_ALLOC_MAXIMUM;
+      return 1;
+    }
+
+  /* Avoid sprintf, as that infringes on the user's name space.
+     Don't have undefined behavior even if the translation
+     produced a string with the wrong number of "%s"s.  */
+  {
+    char *yyp = *yymsg;
+    int yyi = 0;
+    while ((*yyp = *yyformat) != '\0')
+      if (*yyp == '%' && yyformat[1] == 's' && yyi < yycount)
+        {
+          yyp += yytnamerr (yyp, yyarg[yyi++]);
+          yyformat += 2;
+        }
+      else
+        {
+          yyp++;
+          yyformat++;
+        }
+  }
+  return 0;
+}
+#endif /* YYERROR_VERBOSE */
+
+/*-----------------------------------------------.
+| Release the memory associated to this symbol.  |
+`-----------------------------------------------*/
+
+static void
+yydestruct (const char *yymsg, int yytype, YYSTYPE *yyvaluep)
+{
+  YYUSE (yyvaluep);
+  if (!yymsg)
+    yymsg = "Deleting";
+  YY_SYMBOL_PRINT (yymsg, yytype, yyvaluep, yylocationp);
+
+  YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+  YYUSE (yytype);
+  YY_IGNORE_MAYBE_UNINITIALIZED_END
+}
+
+
+
+
+/* The lookahead symbol.  */
+int yychar;
+
+/* The semantic value of the lookahead symbol.  */
+YYSTYPE yylval;
+/* Number of syntax errors so far.  */
+int yynerrs;
+
+
+/*----------.
+| yyparse.  |
+`----------*/
+
+int
+yyparse (void)
+{
+    int yystate;
+    /* Number of tokens to shift before error messages enabled.  */
+    int yyerrstatus;
+
+    /* The stacks and their tools:
+       'yyss': related to states.
+       'yyvs': related to semantic values.
+
+       Refer to the stacks through separate pointers, to allow yyoverflow
+       to reallocate them elsewhere.  */
+
+    /* The state stack.  */
+    yytype_int16 yyssa[YYINITDEPTH];
+    yytype_int16 *yyss;
+    yytype_int16 *yyssp;
+
+    /* The semantic value stack.  */
+    YYSTYPE yyvsa[YYINITDEPTH];
+    YYSTYPE *yyvs;
+    YYSTYPE *yyvsp;
+
+    YYSIZE_T yystacksize;
+
+  int yyn;
+  int yyresult;
+  /* Lookahead token as an internal (translated) token number.  */
+  int yytoken = 0;
+  /* The variables used to return semantic value and location from the
+     action routines.  */
+  YYSTYPE yyval;
+
+#if YYERROR_VERBOSE
+  /* Buffer for error messages, and its allocated size.  */
+  char yymsgbuf[128];
+  char *yymsg = yymsgbuf;
+  YYSIZE_T yymsg_alloc = sizeof yymsgbuf;
+#endif
+
+#define YYPOPSTACK(N)   (yyvsp -= (N), yyssp -= (N))
+
+  /* The number of symbols on the RHS of the reduced rule.
+     Keep to zero when no symbol should be popped.  */
+  int yylen = 0;
+
+  yyssp = yyss = yyssa;
+  yyvsp = yyvs = yyvsa;
+  yystacksize = YYINITDEPTH;
+
+  YYDPRINTF ((stderr, "Starting parse\n"));
+
+  yystate = 0;
+  yyerrstatus = 0;
+  yynerrs = 0;
+  yychar = YYEMPTY; /* Cause a token to be read.  */
+  goto yysetstate;
+
+/*------------------------------------------------------------.
+| yynewstate -- Push a new state, which is found in yystate.  |
+`------------------------------------------------------------*/
+ yynewstate:
+  /* In all cases, when you get here, the value and location stacks
+     have just been pushed.  So pushing a state here evens the stacks.  */
+  yyssp++;
+
+ yysetstate:
+  *yyssp = yystate;
+
+  if (yyss + yystacksize - 1 <= yyssp)
+    {
+      /* Get the current used size of the three stacks, in elements.  */
+      YYSIZE_T yysize = yyssp - yyss + 1;
+
+#ifdef yyoverflow
+      {
+        /* Give user a chance to reallocate the stack.  Use copies of
+           these so that the &'s don't force the real ones into
+           memory.  */
+        YYSTYPE *yyvs1 = yyvs;
+        yytype_int16 *yyss1 = yyss;
+
+        /* Each stack pointer address is followed by the size of the
+           data in use in that stack, in bytes.  This used to be a
+           conditional around just the two extra args, but that might
+           be undefined if yyoverflow is a macro.  */
+        yyoverflow (YY_("memory exhausted"),
+                    &yyss1, yysize * sizeof (*yyssp),
+                    &yyvs1, yysize * sizeof (*yyvsp),
+                    &yystacksize);
+
+        yyss = yyss1;
+        yyvs = yyvs1;
+      }
+#else /* no yyoverflow */
+# ifndef YYSTACK_RELOCATE
+      goto yyexhaustedlab;
+# else
+      /* Extend the stack our own way.  */
+      if (YYMAXDEPTH <= yystacksize)
+        goto yyexhaustedlab;
+      yystacksize *= 2;
+      if (YYMAXDEPTH < yystacksize)
+        yystacksize = YYMAXDEPTH;
+
+      {
+        yytype_int16 *yyss1 = yyss;
+        union yyalloc *yyptr =
+          (union yyalloc *) YYSTACK_ALLOC (YYSTACK_BYTES (yystacksize));
+        if (! yyptr)
+          goto yyexhaustedlab;
+        YYSTACK_RELOCATE (yyss_alloc, yyss);
+        YYSTACK_RELOCATE (yyvs_alloc, yyvs);
+#  undef YYSTACK_RELOCATE
+        if (yyss1 != yyssa)
+          YYSTACK_FREE (yyss1);
+      }
+# endif
+#endif /* no yyoverflow */
+
+      yyssp = yyss + yysize - 1;
+      yyvsp = yyvs + yysize - 1;
+
+      YYDPRINTF ((stderr, "Stack size increased to %lu\n",
+                  (unsigned long int) yystacksize));
+
+      if (yyss + yystacksize - 1 <= yyssp)
+        YYABORT;
+    }
+
+  YYDPRINTF ((stderr, "Entering state %d\n", yystate));
+
+  if (yystate == YYFINAL)
+    YYACCEPT;
+
+  goto yybackup;
+
+/*-----------.
+| yybackup.  |
+`-----------*/
+yybackup:
+
+  /* Do appropriate processing given the current state.  Read a
+     lookahead token if we need one and don't already have one.  */
+
+  /* First try to decide what to do without reference to lookahead token.  */
+  yyn = yypact[yystate];
+  if (yypact_value_is_default (yyn))
+    goto yydefault;
+
+  /* Not known => get a lookahead token if don't already have one.  */
+
+  /* YYCHAR is either YYEMPTY or YYEOF or a valid lookahead symbol.  */
+  if (yychar == YYEMPTY)
+    {
+      YYDPRINTF ((stderr, "Reading a token: "));
+      yychar = yylex ();
+    }
+
+  if (yychar <= YYEOF)
+    {
+      yychar = yytoken = YYEOF;
+      YYDPRINTF ((stderr, "Now at end of input.\n"));
+    }
+  else
+    {
+      yytoken = YYTRANSLATE (yychar);
+      YY_SYMBOL_PRINT ("Next token is", yytoken, &yylval, &yylloc);
+    }
+
+  /* If the proper action on seeing token YYTOKEN is to reduce or to
+     detect an error, take that action.  */
+  yyn += yytoken;
+  if (yyn < 0 || YYLAST < yyn || yycheck[yyn] != yytoken)
+    goto yydefault;
+  yyn = yytable[yyn];
+  if (yyn <= 0)
+    {
+      if (yytable_value_is_error (yyn))
+        goto yyerrlab;
+      yyn = -yyn;
+      goto yyreduce;
+    }
+
+  /* Count tokens shifted since error; after three, turn off error
+     status.  */
+  if (yyerrstatus)
+    yyerrstatus--;
+
+  /* Shift the lookahead token.  */
+  YY_SYMBOL_PRINT ("Shifting", yytoken, &yylval, &yylloc);
+
+  /* Discard the shifted token.  */
+  yychar = YYEMPTY;
+
+  yystate = yyn;
+  YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+  *++yyvsp = yylval;
+  YY_IGNORE_MAYBE_UNINITIALIZED_END
+
+  goto yynewstate;
+
+
+/*-----------------------------------------------------------.
+| yydefault -- do the default action for the current state.  |
+`-----------------------------------------------------------*/
+yydefault:
+  yyn = yydefact[yystate];
+  if (yyn == 0)
+    goto yyerrlab;
+  goto yyreduce;
+
+
+/*-----------------------------.
+| yyreduce -- Do a reduction.  |
+`-----------------------------*/
+yyreduce:
+  /* yyn is the number of a rule to reduce with.  */
+  yylen = yyr2[yyn];
+
+  /* If YYLEN is nonzero, implement the default value of the action:
+     '$$ = $1'.
+
+     Otherwise, the following line sets YYVAL to garbage.
+     This behavior is undocumented and Bison
+     users should not rely upon it.  Assigning to YYVAL
+     unconditionally makes the parser a bit smaller, and it avoids a
+     GCC warning that YYVAL may be used uninitialized.  */
+  yyval = yyvsp[1-yylen];
+
+
+  YY_REDUCE_PRINT (yyn);
+  switch (yyn)
+    {
+        case 7:
+#line 152 "../mDNSShared/dnsextd_parser.y" /* yacc.c:1646  */
+    {
+			// SetupOptions( &g_optionsInfo, context );
+		}
+#line 1365 "objects/prod/dnsextd_parser.c" /* yacc.c:1646  */
+    break;
+
+  case 12:
+#line 171 "../mDNSShared/dnsextd_parser.y" /* yacc.c:1646  */
+    {
+		}
+#line 1372 "objects/prod/dnsextd_parser.c" /* yacc.c:1646  */
+    break;
+
+  case 13:
+#line 175 "../mDNSShared/dnsextd_parser.y" /* yacc.c:1646  */
+    {
+		}
+#line 1379 "objects/prod/dnsextd_parser.c" /* yacc.c:1646  */
+    break;
+
+  case 14:
+#line 179 "../mDNSShared/dnsextd_parser.y" /* yacc.c:1646  */
+    {
+		}
+#line 1386 "objects/prod/dnsextd_parser.c" /* yacc.c:1646  */
+    break;
+
+  case 15:
+#line 183 "../mDNSShared/dnsextd_parser.y" /* yacc.c:1646  */
+    {
+		}
+#line 1393 "objects/prod/dnsextd_parser.c" /* yacc.c:1646  */
+    break;
+
+  case 16:
+#line 187 "../mDNSShared/dnsextd_parser.y" /* yacc.c:1646  */
+    {
+			( ( DaemonInfo* ) context )->private_port = mDNSOpaque16fromIntVal( (yyvsp[0].number) );
+		}
+#line 1401 "objects/prod/dnsextd_parser.c" /* yacc.c:1646  */
+    break;
+
+  case 17:
+#line 192 "../mDNSShared/dnsextd_parser.y" /* yacc.c:1646  */
+    {
+			( ( DaemonInfo* ) context )->llq_port = mDNSOpaque16fromIntVal( (yyvsp[0].number) );
+		}
+#line 1409 "objects/prod/dnsextd_parser.c" /* yacc.c:1646  */
+    break;
+
+  case 18:
+#line 199 "../mDNSShared/dnsextd_parser.y" /* yacc.c:1646  */
+    {
+			KeySpec	* keySpec;
+
+			keySpec = ( KeySpec* ) malloc( sizeof( KeySpec ) );
+
+			if ( !keySpec )
+				{
+				LogMsg("ERROR: memory allocation failure");
+				YYABORT;
+				}
+
+			strncpy( keySpec->name, (yyvsp[-5].string), sizeof( keySpec->name ) );
+			strncpy( keySpec->secret, (yyvsp[-2].string), sizeof( keySpec->secret ) );
+
+			keySpec->next	= g_keys;
+			g_keys			= keySpec;
+        }
+#line 1431 "objects/prod/dnsextd_parser.c" /* yacc.c:1646  */
+    break;
+
+  case 19:
+#line 220 "../mDNSShared/dnsextd_parser.y" /* yacc.c:1646  */
+    {
+			ZoneSpec * zoneSpec;
+
+			zoneSpec = ( ZoneSpec* ) malloc( sizeof( ZoneSpec ) );
+
+			if ( !zoneSpec )
+				{
+				LogMsg("ERROR: memory allocation failure");
+				YYABORT;
+				}
+
+			strncpy( zoneSpec->name, (yyvsp[-1].string), sizeof( zoneSpec->name ) );
+			zoneSpec->type = g_zoneSpec.type;
+			strcpy( zoneSpec->key, g_zoneSpec.key );
+			zoneSpec->allowUpdate = g_zoneSpec.allowUpdate;
+			zoneSpec->allowQuery = g_zoneSpec.allowQuery;
+
+			zoneSpec->next = g_zones;
+			g_zones = zoneSpec;
+		}
+#line 1456 "objects/prod/dnsextd_parser.c" /* yacc.c:1646  */
+    break;
+
+  case 20:
+#line 242 "../mDNSShared/dnsextd_parser.y" /* yacc.c:1646  */
+    {
+			ZoneSpec * zoneSpec;
+
+			zoneSpec = ( ZoneSpec* ) malloc( sizeof( ZoneSpec ) );
+
+			if ( !zoneSpec )
+				{
+				LogMsg("ERROR: memory allocation failure");
+				YYABORT;
+				}
+
+			strncpy( zoneSpec->name, (yyvsp[-2].string), sizeof( zoneSpec->name ) );
+			zoneSpec->type = g_zoneSpec.type;
+			strcpy( zoneSpec->key, g_zoneSpec.key );
+			zoneSpec->allowUpdate = g_zoneSpec.allowUpdate;
+			zoneSpec->allowQuery = g_zoneSpec.allowQuery;
+
+			zoneSpec->next = g_zones;
+			g_zones = zoneSpec;
+		}
+#line 1481 "objects/prod/dnsextd_parser.c" /* yacc.c:1646  */
+    break;
+
+  case 24:
+#line 274 "../mDNSShared/dnsextd_parser.y" /* yacc.c:1646  */
+    {
+			g_zoneSpec.type = kDNSZonePublic;
+		}
+#line 1489 "objects/prod/dnsextd_parser.c" /* yacc.c:1646  */
+    break;
+
+  case 25:
+#line 279 "../mDNSShared/dnsextd_parser.y" /* yacc.c:1646  */
+    {
+			g_zoneSpec.type = kDNSZonePrivate;
+		}
+#line 1497 "objects/prod/dnsextd_parser.c" /* yacc.c:1646  */
+    break;
+
+  case 26:
+#line 284 "../mDNSShared/dnsextd_parser.y" /* yacc.c:1646  */
+    {
+			g_zoneSpec.allowUpdate = g_stringList;
+			g_stringList = NULL;
+		}
+#line 1506 "objects/prod/dnsextd_parser.c" /* yacc.c:1646  */
+    break;
+
+  case 27:
+#line 290 "../mDNSShared/dnsextd_parser.y" /* yacc.c:1646  */
+    {
+			g_zoneSpec.allowQuery = g_stringList;
+			g_stringList = NULL;
+		}
+#line 1515 "objects/prod/dnsextd_parser.c" /* yacc.c:1646  */
+    break;
+
+  case 28:
+#line 298 "../mDNSShared/dnsextd_parser.y" /* yacc.c:1646  */
+    {
+		}
+#line 1522 "objects/prod/dnsextd_parser.c" /* yacc.c:1646  */
+    break;
+
+  case 30:
+#line 304 "../mDNSShared/dnsextd_parser.y" /* yacc.c:1646  */
+    {
+		}
+#line 1529 "objects/prod/dnsextd_parser.c" /* yacc.c:1646  */
+    break;
+
+  case 31:
+#line 310 "../mDNSShared/dnsextd_parser.y" /* yacc.c:1646  */
+    {
+		}
+#line 1536 "objects/prod/dnsextd_parser.c" /* yacc.c:1646  */
+    break;
+
+  case 32:
+#line 317 "../mDNSShared/dnsextd_parser.y" /* yacc.c:1646  */
+    {
+		}
+#line 1543 "objects/prod/dnsextd_parser.c" /* yacc.c:1646  */
+    break;
+
+  case 34:
+#line 323 "../mDNSShared/dnsextd_parser.y" /* yacc.c:1646  */
+    {
+		}
+#line 1550 "objects/prod/dnsextd_parser.c" /* yacc.c:1646  */
+    break;
+
+  case 35:
+#line 329 "../mDNSShared/dnsextd_parser.y" /* yacc.c:1646  */
+    {
+			StringListElem * elem;
+
+			elem = ( StringListElem* ) malloc( sizeof( StringListElem ) );
+
+			if ( !elem )
+				{
+				LogMsg("ERROR: memory allocation failure");
+				YYABORT;
+				}
+
+			elem->string = (yyvsp[0].string);
+
+			elem->next		= g_stringList;
+			g_stringList	= elem;
+		}
+#line 1571 "objects/prod/dnsextd_parser.c" /* yacc.c:1646  */
+    break;
+
+  case 42:
+#line 367 "../mDNSShared/dnsextd_parser.y" /* yacc.c:1646  */
+    {
+			(yyval.string) = NULL;
+		}
+#line 1579 "objects/prod/dnsextd_parser.c" /* yacc.c:1646  */
+    break;
+
+  case 43:
+#line 372 "../mDNSShared/dnsextd_parser.y" /* yacc.c:1646  */
+    {
+			(yyval.string) = (yyvsp[0].string);
+		}
+#line 1587 "objects/prod/dnsextd_parser.c" /* yacc.c:1646  */
+    break;
+
+
+#line 1591 "objects/prod/dnsextd_parser.c" /* yacc.c:1646  */
+      default: break;
+    }
+  /* User semantic actions sometimes alter yychar, and that requires
+     that yytoken be updated with the new translation.  We take the
+     approach of translating immediately before every use of yytoken.
+     One alternative is translating here after every semantic action,
+     but that translation would be missed if the semantic action invokes
+     YYABORT, YYACCEPT, or YYERROR immediately after altering yychar or
+     if it invokes YYBACKUP.  In the case of YYABORT or YYACCEPT, an
+     incorrect destructor might then be invoked immediately.  In the
+     case of YYERROR or YYBACKUP, subsequent parser actions might lead
+     to an incorrect destructor call or verbose syntax error message
+     before the lookahead is translated.  */
+  YY_SYMBOL_PRINT ("-> $$ =", yyr1[yyn], &yyval, &yyloc);
+
+  YYPOPSTACK (yylen);
+  yylen = 0;
+  YY_STACK_PRINT (yyss, yyssp);
+
+  *++yyvsp = yyval;
+
+  /* Now 'shift' the result of the reduction.  Determine what state
+     that goes to, based on the state we popped back to and the rule
+     number reduced by.  */
+
+  yyn = yyr1[yyn];
+
+  yystate = yypgoto[yyn - YYNTOKENS] + *yyssp;
+  if (0 <= yystate && yystate <= YYLAST && yycheck[yystate] == *yyssp)
+    yystate = yytable[yystate];
+  else
+    yystate = yydefgoto[yyn - YYNTOKENS];
+
+  goto yynewstate;
+
+
+/*--------------------------------------.
+| yyerrlab -- here on detecting error.  |
+`--------------------------------------*/
+yyerrlab:
+  /* Make sure we have latest lookahead translation.  See comments at
+     user semantic actions for why this is necessary.  */
+  yytoken = yychar == YYEMPTY ? YYEMPTY : YYTRANSLATE (yychar);
+
+  /* If not already recovering from an error, report this error.  */
+  if (!yyerrstatus)
+    {
+      ++yynerrs;
+#if ! YYERROR_VERBOSE
+      yyerror (YY_("syntax error"));
+#else
+# define YYSYNTAX_ERROR yysyntax_error (&yymsg_alloc, &yymsg, \
+                                        yyssp, yytoken)
+      {
+        char const *yymsgp = YY_("syntax error");
+        int yysyntax_error_status;
+        yysyntax_error_status = YYSYNTAX_ERROR;
+        if (yysyntax_error_status == 0)
+          yymsgp = yymsg;
+        else if (yysyntax_error_status == 1)
+          {
+            if (yymsg != yymsgbuf)
+              YYSTACK_FREE (yymsg);
+            yymsg = (char *) YYSTACK_ALLOC (yymsg_alloc);
+            if (!yymsg)
+              {
+                yymsg = yymsgbuf;
+                yymsg_alloc = sizeof yymsgbuf;
+                yysyntax_error_status = 2;
+              }
+            else
+              {
+                yysyntax_error_status = YYSYNTAX_ERROR;
+                yymsgp = yymsg;
+              }
+          }
+        yyerror (yymsgp);
+        if (yysyntax_error_status == 2)
+          goto yyexhaustedlab;
+      }
+# undef YYSYNTAX_ERROR
+#endif
+    }
+
+
+
+  if (yyerrstatus == 3)
+    {
+      /* If just tried and failed to reuse lookahead token after an
+         error, discard it.  */
+
+      if (yychar <= YYEOF)
+        {
+          /* Return failure if at end of input.  */
+          if (yychar == YYEOF)
+            YYABORT;
+        }
+      else
+        {
+          yydestruct ("Error: discarding",
+                      yytoken, &yylval);
+          yychar = YYEMPTY;
+        }
+    }
+
+  /* Else will try to reuse lookahead token after shifting the error
+     token.  */
+  goto yyerrlab1;
+
+
+/*---------------------------------------------------.
+| yyerrorlab -- error raised explicitly by YYERROR.  |
+`---------------------------------------------------*/
+yyerrorlab:
+
+  /* Pacify compilers like GCC when the user code never invokes
+     YYERROR and the label yyerrorlab therefore never appears in user
+     code.  */
+  if (/*CONSTCOND*/ 0)
+     goto yyerrorlab;
+
+  /* Do not reclaim the symbols of the rule whose action triggered
+     this YYERROR.  */
+  YYPOPSTACK (yylen);
+  yylen = 0;
+  YY_STACK_PRINT (yyss, yyssp);
+  yystate = *yyssp;
+  goto yyerrlab1;
+
+
+/*-------------------------------------------------------------.
+| yyerrlab1 -- common code for both syntax error and YYERROR.  |
+`-------------------------------------------------------------*/
+yyerrlab1:
+  yyerrstatus = 3;      /* Each real token shifted decrements this.  */
+
+  for (;;)
+    {
+      yyn = yypact[yystate];
+      if (!yypact_value_is_default (yyn))
+        {
+          yyn += YYTERROR;
+          if (0 <= yyn && yyn <= YYLAST && yycheck[yyn] == YYTERROR)
+            {
+              yyn = yytable[yyn];
+              if (0 < yyn)
+                break;
+            }
+        }
+
+      /* Pop the current state because it cannot handle the error token.  */
+      if (yyssp == yyss)
+        YYABORT;
+
+
+      yydestruct ("Error: popping",
+                  yystos[yystate], yyvsp);
+      YYPOPSTACK (1);
+      yystate = *yyssp;
+      YY_STACK_PRINT (yyss, yyssp);
+    }
+
+  YY_IGNORE_MAYBE_UNINITIALIZED_BEGIN
+  *++yyvsp = yylval;
+  YY_IGNORE_MAYBE_UNINITIALIZED_END
+
+
+  /* Shift the error token.  */
+  YY_SYMBOL_PRINT ("Shifting", yystos[yyn], yyvsp, yylsp);
+
+  yystate = yyn;
+  goto yynewstate;
+
+
+/*-------------------------------------.
+| yyacceptlab -- YYACCEPT comes here.  |
+`-------------------------------------*/
+yyacceptlab:
+  yyresult = 0;
+  goto yyreturn;
+
+/*-----------------------------------.
+| yyabortlab -- YYABORT comes here.  |
+`-----------------------------------*/
+yyabortlab:
+  yyresult = 1;
+  goto yyreturn;
+
+#if !defined yyoverflow || YYERROR_VERBOSE
+/*-------------------------------------------------.
+| yyexhaustedlab -- memory exhaustion comes here.  |
+`-------------------------------------------------*/
+yyexhaustedlab:
+  yyerror (YY_("memory exhausted"));
+  yyresult = 2;
+  /* Fall through.  */
+#endif
+
+yyreturn:
+  if (yychar != YYEMPTY)
+    {
+      /* Make sure we have latest lookahead translation.  See comments at
+         user semantic actions for why this is necessary.  */
+      yytoken = YYTRANSLATE (yychar);
+      yydestruct ("Cleanup: discarding lookahead",
+                  yytoken, &yylval);
+    }
+  /* Do not reclaim the symbols of the rule whose action triggered
+     this YYABORT or YYACCEPT.  */
+  YYPOPSTACK (yylen);
+  YY_STACK_PRINT (yyss, yyssp);
+  while (yyssp != yyss)
+    {
+      yydestruct ("Cleanup: popping",
+                  yystos[*yyssp], yyvsp);
+      YYPOPSTACK (1);
+    }
+#ifndef yyoverflow
+  if (yyss != yyssa)
+    YYSTACK_FREE (yyss);
+#endif
+#if YYERROR_VERBOSE
+  if (yymsg != yymsgbuf)
+    YYSTACK_FREE (yymsg);
+#endif
+  return yyresult;
+}
+#line 375 "../mDNSShared/dnsextd_parser.y" /* yacc.c:1906  */
+
+
+int yywrap(void);
+
+extern int yylineno;
+
+void yyerror( const char *str )
+{
+        fprintf( stderr,"%s:%d: error: %s\n", g_filename, yylineno, str );
+}
+ 
+int yywrap()
+{
+        return 1;
+} 
+
+
+int
+ParseConfig
+	(
+	DaemonInfo	*	d,
+	const char	*	file
+	)
+	{
+	extern FILE		*	yyin;
+	DNSZone			*	zone;
+	DomainAuthInfo	*	key;
+	KeySpec			*	keySpec;
+	ZoneSpec		*	zoneSpec;
+	int					err = 0;
+
+	g_filename = file;
+
+	// Tear down the current zone specifiers
+
+	zone = d->zones;
+
+	while ( zone )
+		{
+		DNSZone * next = zone->next;
+
+		key = zone->updateKeys;
+
+		while ( key )
+			{
+			DomainAuthInfo * nextKey = key->next;
+
+			free( key );
+
+			key = nextKey;
+			}
+
+		key = zone->queryKeys;
+
+		while ( key )
+			{
+			DomainAuthInfo * nextKey = key->next;
+
+			free( key );
+
+			key = nextKey;
+			}
+
+		free( zone );
+
+		zone = next;
+		}
+
+	d->zones = NULL;
+	
+	yyin = fopen( file, "r" );
+	require_action( yyin, exit, err = 0 );
+
+	err = yyparse( ( void* ) d );
+	require_action( !err, exit, err = 1 );
+
+	for ( zoneSpec = g_zones; zoneSpec; zoneSpec = zoneSpec->next )
+		{
+		StringListElem  *   elem;
+		mDNSu8			*	ok;
+
+		zone = ( DNSZone* ) malloc( sizeof( DNSZone ) );
+		require_action( zone, exit, err = 1 );
+		memset( zone, 0, sizeof( DNSZone ) );
+
+		zone->next	= d->zones;
+		d->zones	= zone;
+
+		// Fill in the domainname
+
+		ok = MakeDomainNameFromDNSNameString( &zone->name, zoneSpec->name );
+		require_action( ok, exit, err = 1 );
+
+		// Fill in the type
+
+		zone->type = zoneSpec->type;
+
+		// Fill in the allow-update keys
+
+		for ( elem = zoneSpec->allowUpdate; elem; elem = elem->next )
+			{
+			mDNSBool found = mDNSfalse;
+
+			for ( keySpec = g_keys; keySpec; keySpec = keySpec->next )
+				{
+				if ( strcmp( elem->string, keySpec->name ) == 0 )
+					{
+					DomainAuthInfo	*	authInfo = malloc( sizeof( DomainAuthInfo ) );
+					mDNSs32				keylen;
+					require_action( authInfo, exit, err = 1 );
+					memset( authInfo, 0, sizeof( DomainAuthInfo ) );
+
+					ok = MakeDomainNameFromDNSNameString( &authInfo->keyname, keySpec->name );
+					if (!ok) { free(authInfo); err = 1; goto exit; }
+
+					keylen = DNSDigest_ConstructHMACKeyfromBase64( authInfo, keySpec->secret );
+					if (keylen < 0) { free(authInfo); err = 1; goto exit; }
+
+					authInfo->next = zone->updateKeys;
+					zone->updateKeys = authInfo;
+
+					found = mDNStrue;
+
+					break;
+					}
+				}
+
+			// Log this
+			require_action( found, exit, err = 1 );
+			}
+
+		// Fill in the allow-query keys
+
+		for ( elem = zoneSpec->allowQuery; elem; elem = elem->next )
+			{
+			mDNSBool found = mDNSfalse;
+
+			for ( keySpec = g_keys; keySpec; keySpec = keySpec->next )
+				{
+				if ( strcmp( elem->string, keySpec->name ) == 0 )
+					{
+					DomainAuthInfo	*	authInfo = malloc( sizeof( DomainAuthInfo ) );
+					mDNSs32				keylen;
+					require_action( authInfo, exit, err = 1 );
+					memset( authInfo, 0, sizeof( DomainAuthInfo ) );
+
+					ok = MakeDomainNameFromDNSNameString( &authInfo->keyname, keySpec->name );
+					if (!ok) { free(authInfo); err = 1; goto exit; }
+
+					keylen = DNSDigest_ConstructHMACKeyfromBase64( authInfo, keySpec->secret );
+					if (keylen < 0) { free(authInfo); err = 1; goto exit; }
+
+					authInfo->next = zone->queryKeys;
+					zone->queryKeys = authInfo;
+
+					found = mDNStrue;
+
+					break;
+					}
+				}
+
+			// Log this
+			require_action( found, exit, err = 1 );
+			}
+		}
+
+exit:
+
+	return err;
+	}
+
+
+void
+SetupOptions
+	(
+	OptionsInfo	*	info,
+	void		*	context
+	)
+	{
+	DaemonInfo * d = ( DaemonInfo* ) context;
+
+	if ( strlen( info->source_address ) )
+		{
+		inet_pton( AF_INET, info->source_address, &d->addr.sin_addr );
+		}
+
+	if ( info->source_port )
+		{
+		d->addr.sin_port = htons( ( mDNSu16 ) info->source_port );
+		}
+				
+	if ( strlen( info->server_address ) )
+		{
+		inet_pton( AF_INET, info->server_address, &d->ns_addr.sin_addr );
+		}
+
+	if ( info->server_port )
+		{
+		d->ns_addr.sin_port = htons( ( mDNSu16 ) info->server_port );
+		}
+
+	if ( info->private_port )
+		{
+		d->private_port = mDNSOpaque16fromIntVal( info->private_port );
+		}
+
+	if ( info->llq_port )
+		{
+		d->llq_port = mDNSOpaque16fromIntVal( info->llq_port );
+		}
+	}
diff --git a/mdnsresponder/mDNSPosix/objects/prod/dnsextd_parser.h b/mdnsresponder/mDNSPosix/objects/prod/dnsextd_parser.h
new file mode 100644
index 0000000..439d9b9
--- /dev/null
+++ b/mdnsresponder/mDNSPosix/objects/prod/dnsextd_parser.h
@@ -0,0 +1,100 @@
+/* A Bison parser, made by GNU Bison 3.0.2.  */
+
+/* Bison interface for Yacc-like parsers in C
+
+   Copyright (C) 1984, 1989-1990, 2000-2013 Free Software Foundation, Inc.
+
+   This program 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.
+
+   This program 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 this program.  If not, see <http://www.gnu.org/licenses/>.  */
+
+/* As a special exception, you may create a larger work that contains
+   part or all of the Bison parser skeleton and distribute that work
+   under terms of your choice, so long as that work isn't itself a
+   parser generator using the skeleton or a modified version thereof
+   as a parser skeleton.  Alternatively, if you modify or redistribute
+   the parser skeleton itself, you may (at your option) remove this
+   special exception, which will cause the skeleton and the resulting
+   Bison output files to be licensed under the GNU General Public
+   License without this special exception.
+
+   This special exception was added by the Free Software Foundation in
+   version 2.2 of Bison.  */
+
+#ifndef YY_YY_OBJECTS_PROD_DNSEXTD_PARSER_H_INCLUDED
+# define YY_YY_OBJECTS_PROD_DNSEXTD_PARSER_H_INCLUDED
+/* Debug traces.  */
+#ifndef YYDEBUG
+# define YYDEBUG 0
+#endif
+#if YYDEBUG
+extern int yydebug;
+#endif
+
+/* Token type.  */
+#ifndef YYTOKENTYPE
+# define YYTOKENTYPE
+  enum yytokentype
+  {
+    OPTIONS = 258,
+    LISTEN_ON = 259,
+    NAMESERVER = 260,
+    PORT = 261,
+    ADDRESS = 262,
+    LLQ = 263,
+    PUBLIC = 264,
+    PRIVATE = 265,
+    ALLOWUPDATE = 266,
+    ALLOWQUERY = 267,
+    KEY = 268,
+    ALGORITHM = 269,
+    SECRET = 270,
+    ISSUER = 271,
+    SERIAL = 272,
+    ZONE = 273,
+    TYPE = 274,
+    ALLOW = 275,
+    OBRACE = 276,
+    EBRACE = 277,
+    SEMICOLON = 278,
+    IN = 279,
+    DOTTED_DECIMAL_ADDRESS = 280,
+    WILDCARD = 281,
+    DOMAINNAME = 282,
+    HOSTNAME = 283,
+    QUOTEDSTRING = 284,
+    NUMBER = 285
+  };
+#endif
+
+/* Value type.  */
+#if ! defined YYSTYPE && ! defined YYSTYPE_IS_DECLARED
+typedef union YYSTYPE YYSTYPE;
+union YYSTYPE
+{
+#line 96 "../mDNSShared/dnsextd_parser.y" /* yacc.c:1909  */
+
+	int			number;
+	char	*	string;
+
+#line 90 "objects/prod/dnsextd_parser.h" /* yacc.c:1909  */
+};
+# define YYSTYPE_IS_TRIVIAL 1
+# define YYSTYPE_IS_DECLARED 1
+#endif
+
+
+extern YYSTYPE yylval;
+
+int yyparse (void);
+
+#endif /* !YY_YY_OBJECTS_PROD_DNSEXTD_PARSER_H_INCLUDED  */
diff --git a/mdnsresponder/mDNSPosix/objects/prod/dnssd_clientlib.c.so.o b/mdnsresponder/mDNSPosix/objects/prod/dnssd_clientlib.c.so.o
new file mode 100644
index 0000000..2e3e933
--- /dev/null
+++ b/mdnsresponder/mDNSPosix/objects/prod/dnssd_clientlib.c.so.o
Binary files differ
diff --git a/mdnsresponder/mDNSPosix/objects/prod/dnssd_clientstub.c.so.o b/mdnsresponder/mDNSPosix/objects/prod/dnssd_clientstub.c.so.o
new file mode 100644
index 0000000..3dcb331
--- /dev/null
+++ b/mdnsresponder/mDNSPosix/objects/prod/dnssd_clientstub.c.so.o
Binary files differ
diff --git a/mdnsresponder/mDNSPosix/objects/prod/dnssd_ipc.c.o b/mdnsresponder/mDNSPosix/objects/prod/dnssd_ipc.c.o
new file mode 100644
index 0000000..42e8bdf
--- /dev/null
+++ b/mdnsresponder/mDNSPosix/objects/prod/dnssd_ipc.c.o
Binary files differ
diff --git a/mdnsresponder/mDNSPosix/objects/prod/dnssd_ipc.c.so.o b/mdnsresponder/mDNSPosix/objects/prod/dnssd_ipc.c.so.o
new file mode 100644
index 0000000..2693f9d
--- /dev/null
+++ b/mdnsresponder/mDNSPosix/objects/prod/dnssd_ipc.c.so.o
Binary files differ
diff --git a/mdnsresponder/mDNSPosix/objects/prod/mDNS.c.o b/mdnsresponder/mDNSPosix/objects/prod/mDNS.c.o
new file mode 100644
index 0000000..9e515dc
--- /dev/null
+++ b/mdnsresponder/mDNSPosix/objects/prod/mDNS.c.o
Binary files differ
diff --git a/mdnsresponder/mDNSPosix/objects/prod/mDNSDebug.c.o b/mdnsresponder/mDNSPosix/objects/prod/mDNSDebug.c.o
new file mode 100644
index 0000000..ae75075
--- /dev/null
+++ b/mdnsresponder/mDNSPosix/objects/prod/mDNSDebug.c.o
Binary files differ
diff --git a/mdnsresponder/mDNSPosix/objects/prod/mDNSPosix.c.o b/mdnsresponder/mDNSPosix/objects/prod/mDNSPosix.c.o
new file mode 100644
index 0000000..11db9d3
--- /dev/null
+++ b/mdnsresponder/mDNSPosix/objects/prod/mDNSPosix.c.o
Binary files differ
diff --git a/mdnsresponder/mDNSPosix/objects/prod/mDNSUNP.c.o b/mdnsresponder/mDNSPosix/objects/prod/mDNSUNP.c.o
new file mode 100644
index 0000000..45fa830
--- /dev/null
+++ b/mdnsresponder/mDNSPosix/objects/prod/mDNSUNP.c.o
Binary files differ
diff --git a/mdnsresponder/mDNSPosix/objects/prod/uDNS.c.o b/mdnsresponder/mDNSPosix/objects/prod/uDNS.c.o
new file mode 100644
index 0000000..689165f
--- /dev/null
+++ b/mdnsresponder/mDNSPosix/objects/prod/uDNS.c.o
Binary files differ
diff --git a/mdnsresponder/mDNSPosix/objects/prod/uds_daemon.c.o b/mdnsresponder/mDNSPosix/objects/prod/uds_daemon.c.o
new file mode 100644
index 0000000..c714032
--- /dev/null
+++ b/mdnsresponder/mDNSPosix/objects/prod/uds_daemon.c.o
Binary files differ
diff --git a/mdnsresponder/mDNSPosix/parselog.py b/mdnsresponder/mDNSPosix/parselog.py
new file mode 100755
index 0000000..f39d0f2
--- /dev/null
+++ b/mdnsresponder/mDNSPosix/parselog.py
@@ -0,0 +1,247 @@
+#!/usr/bin/python
+# Emacs settings: -*- tab-width: 4 -*-
+#
+# Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved.
+#
+# Licensed 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.
+#
+# parselog.py, written and contributed by Kevin Marks
+#
+# Requires OS X 10.3 Panther or later, for Python and Core Graphics Python APIs
+# Invoke from the command line with "parselog.py fname" where fname is a log file made by mDNSNetMonitor
+#
+# Caveats:
+# It expects plain ASCII, and doesn't handle spaces in record names very well right now
+# There's a procedure you can follow to 'sanitize' an mDNSNetMonitor log file to make it more paletable to parselog.py:
+# 1. Run mDNSNetMonitor in a terminal window.
+#    When you have enough traffic, type Ctrl-C and save the content of the terminal window to disk.
+#    Alternatively, you can use "mDNSNetMonitor > logfile" to write the text directly to a file.
+#    You now have a UTF-8 text file.
+# 2. Open the UTF-8 text file using BBEdit or some other text editor.
+#    (These instructions are for BBEdit, which I highly recommend you use when doing this.)
+# 3. Make sure BBEdit correctly interprets the file as UTF-8.
+#    Either set your "Text Files Opening" preference to "UTF-8 no BOM", and drop the file onto BBEdit,
+#    or manually open the File using "File -> Open" and make sure the "Read As" setting is set to "UTF-8 no BOM"
+#    Check in the document pulldown menu in the window toolbar to make sure that it says "Encoding: UTF-8 no BOM"
+# 4. Use "Tools -> Convert to ASCII" to replace all special characters with their seven-bit ascii equivalents.
+#    (e.g. curly quotes are converted to straight quotes)
+# 5. Do a grep search and replace. (Cmd-F; make sure Grep checkbox is turned on.)
+#    Enter this search text     : ^(.................\(................\S*) (.* -> .*)$
+#    Enter this replacement text: \1-\2
+#    Click "Replace All"
+#    Press Cmd-Opt-= repeatedly until there are no more instances to be replaced.
+#    You now have text file with all spaces in names changed to hyphens
+# 6. Save the new file. You can save it as "UTF-8 no BOM", or as "Mac Roman". It really doesn't matter which --
+#    the file now contains only seven-bit ascii, so it's all the same no matter how you save it.
+# 7. Run "parselog.py fname"
+# 8. Open the resulting fname.pdf file with a PDF viewer like Preview on OS X
+#
+# Key to what you see:
+# Time is on the horizontal axis
+# Individual machines are shown on the vertical axis
+# Filled red    circle: Normal query              Hollow red    circle: Query requesting unicast reply
+# Filled orange circle: Probe (service starting)  Hollow orange circle: First probe (requesting unicast reply)
+# Filled green  circle: Normal answer             Hollow green  circle: Goodbye message (record going away)
+#                                                 Hollow blue   circle: Legacy query (from old client)
+
+from CoreGraphics import *
+import math   # for pi
+
+import string
+import sys, os
+import re
+
+def parselog(inFile):
+	f = open(inFile)
+	hunt = 'getTime'
+	ipList = {}
+	querySource = {}
+	plotPoints = []
+	maxTime=0
+	minTime = 36*60*60
+	spaceExp = re.compile(r'\s+')
+	print "Reading " + inFile
+	while 1:
+		lines = f.readlines(100000)
+		if not lines:
+			break
+		for line in lines:
+			if (hunt == 'skip'):
+				if (line == '\n' or line == '\r' or line ==''): 
+					hunt = 'getTime'
+#				else:
+#					msg = ("skipped" , line)
+#					print msg
+			elif (hunt == 'getTime'):
+				if (line == "^C\n" ):
+					break
+				time = line.split(' ')[0].split(':')
+				if (len(time)<3):
+					#print "bad time, skipping",time
+					hunt = 'skip'
+				else:
+					hunt = 'getIP'
+				#print (("getTime:%s" % (line)), time)
+			elif (hunt == 'getIP'):
+				ip = line.split(' ',1)
+				ip = ip[0]
+				secs=0
+				for t in time:
+					secs = secs*60 +float(t)
+				if (secs>maxTime):
+					maxTime=secs
+				if (secs<minTime):
+					minTime=secs
+				if (not ip in ipList):
+					ipList[ip] = [len(ipList), "", ""]
+				#print (("getIP:%s" % (line)), time, secs)
+				hunt = 'getQA'
+			elif (hunt == 'getQA'):
+				qaList = spaceExp.split(line)
+				# qaList[0] Source Address
+				# qaList[1] Operation type (PU/PM/QU/QM/AN etc.)
+				# qaList[2] Record type (PTR/SRV/TXT etc.)
+				# For QU/QM/LQ:
+				# qaList[3] RR name
+				# For PU/PM/AN/AN+/AD/AD+/KA:
+				# qaList[3] TTL
+				# qaList[4] RR name
+				# qaList[5...] "->" symbol and following rdata
+				#print qaList
+				if (qaList[0] == ip):
+					if (qaList[1] == '(QU)' or qaList[1] == '(LQ)' or qaList[1] == '(PU)'):
+						plotPoints.append([secs, ipList[ip][0], (qaList[1])[1:-1]])
+					elif (qaList[1] == '(QM)'):
+						plotPoints.append([secs, ipList[ip][0], (qaList[1])[1:-1]])
+						querySource[qaList[3]] = len(plotPoints)-1
+					elif (qaList[1] == '(PM)'):
+						plotPoints.append([secs, ipList[ip][0], (qaList[1])[1:-1]])
+						querySource[qaList[4]] = len(plotPoints)-1
+					elif (qaList[1] == '(AN)' or qaList[1] == '(AN+)' or qaList[1] == '(DE)'):
+						plotPoints.append([secs, ipList[ip][0], (qaList[1])[1:-1]])
+						try:
+							theQuery = querySource[qaList[4]]
+							theDelta = secs - plotPoints[theQuery][0]
+							if (theDelta < 1.0):
+								plotPoints[-1].append(querySource[qaList[4]])
+							#print "Answer AN+ %s points to %d" % (qaList[4],querySource[qaList[4]])
+						except:
+							#print "Couldn't find any preceeding question for", qaList
+							pass
+					elif (qaList[1] != '(KA)' and qaList[1] != '(AD)' and qaList[1] != '(AD+)'):
+						print "Operation unknown", qaList
+
+					if (qaList[1] == '(AN)' or qaList[1] == '(AN+)' or qaList[1] == '(AD)' or qaList[1] == '(AD+)'):
+						if (qaList[2] == 'HINFO'):
+							ipList[ip][1] = qaList[4]
+							ipList[ip][2] = string.join(qaList[6:])
+							#print ipList[ip][1]
+						elif (qaList[2] == 'AAAA'):
+							if (ipList[ip][1] == ""):
+								ipList[ip][1] = qaList[4]
+								ipList[ip][2] = "Panther"
+						elif (qaList[2] == 'Addr'):
+							if (ipList[ip][1] == ""):
+								ipList[ip][1] = qaList[4]
+								ipList[ip][2] = "Jaguar"
+				else:
+					if (line == '\n'): 
+						hunt = 'getTime'
+					else:
+						hunt = 'skip'
+	f.close()
+	#print plotPoints
+	#print querySource
+	#width=20.0*(maxTime-minTime)
+	if (maxTime < minTime + 10.0):
+		maxTime = minTime + 10.0
+	typesize = 12
+	width=20.0*(maxTime-minTime)
+	pageHeight=(len(ipList)+1) * typesize
+	scale = width/(maxTime-minTime)
+	leftMargin = typesize * 60
+	bottomMargin = typesize
+	pageRect = CGRectMake (-leftMargin, -bottomMargin,  leftMargin + width, bottomMargin + pageHeight)   #  landscape
+	outFile = "%s.pdf" % (".".join(inFile.split('.')[:-1]))
+	c = CGPDFContextCreateWithFilename (outFile, pageRect)
+	print "Writing " + outFile
+	ourColourSpace = c.getColorSpace()
+	# QM/QU red    solid/hollow
+	# PM/PU orange solid/hollow
+	# LQ    blue         hollow
+	# AN/DA green  solid/hollow
+	#colourLookup = {"L":(0.0,0.0,.75), "Q":(.75,0.0,0.0), "P":(.75,0.5,0.0), "A":(0.0,0.75,0.0), "D":(0.0,0.75,0.0), "?":(.25,0.25,0.25)}
+	colourLookup = {"L":(0.0,0.0,1.0), "Q":(1.0,0.0,0.0), "P":(1.0,0.8,0.0), "A":(0.0,1.0,0.0), "D":(0.0,1.0,0.0), "?":(1.0,1.0,1.0)}
+	c.beginPage (pageRect)
+	c.setRGBFillColor(.75,0.0,0.0,1.0)
+	c.setRGBStrokeColor(.25,0.75,0.25,1.0)
+	c.setLineWidth(0.25)
+	for point in plotPoints:
+		#c.addArc((point[0]-minTime)*scale,point[1]*typesize+6,5,0,2*math.pi,1)
+		c.addArc((point[0]-minTime)*scale,point[1]*typesize+6,typesize/4,0,2*math.pi,1)
+		theColour = colourLookup[(point[2])[0]]
+		if (((point[2])[0]) != "L") and (((point[2])[0]) != "Q") and (((point[2])[0]) != "P") and (((point[2])[0]) != "A") and (((point[2])[0]) != "D"):
+			print "Unknown", point
+		if ((point[2])[-1] == "M" or (point[2])[0]== "A"):
+			c.setRGBFillColor(theColour[0],theColour[1],theColour[2],.5)
+			c.fillPath()
+		else:
+			c.setRGBStrokeColor(theColour[0],theColour[1],theColour[2],.5)
+			c.setLineWidth(1.0)
+			c.strokePath()
+		c.setRGBStrokeColor(.25,0.75,0.25,1.0)
+		c.setLineWidth(0.25)
+		for index in point[3:]:
+			c.beginPath()
+			c.moveToPoint((point[0]-minTime)*scale,point[1]*typesize+6)
+			c.addLineToPoint(((plotPoints[index])[0]-minTime)*scale,(plotPoints[index])[1]*typesize+6)
+			c.closePath()
+			c.strokePath()
+	c.setRGBFillColor (0,0,0, 1)
+	c.setTextDrawingMode (kCGTextFill)
+	c.setTextMatrix (CGAffineTransformIdentity)
+	c.selectFont ('Gill Sans', typesize, kCGEncodingMacRoman)
+	c.setRGBStrokeColor(0.25,0.0,0.0,1.0)
+	c.setLineWidth(0.1)
+	for ip,[height,hname,hinfo] in ipList.items():
+		c.beginPath()
+		c.moveToPoint(pageRect.origin.x,height*typesize+6)
+		c.addLineToPoint(width,height*typesize+6)
+		c.closePath()
+		c.strokePath()
+		c.showTextAtPoint(pageRect.origin.x + 2,               height*typesize + 2, ip,    len(ip))
+		c.showTextAtPoint(pageRect.origin.x + 2 + typesize*8,  height*typesize + 2, hname, len(hname))
+		c.showTextAtPoint(pageRect.origin.x + 2 + typesize*25, height*typesize + 2, hinfo, len(hinfo))
+	for time in range (int(minTime),int(maxTime)+1):
+		c.beginPath()
+		c.moveToPoint((time-minTime)*scale,pageRect.origin.y)
+		c.addLineToPoint((time-minTime)*scale,pageHeight)
+		c.closePath()
+		if (int(time) % 10 == 0):
+			theHours = time/3600
+			theMinutes = time/60 % 60
+			theSeconds = time % 60
+			theTimeString = '%d:%02d:%02d' % (theHours, theMinutes, theSeconds)
+			# Should measure string width, but don't know how to do that
+			theStringWidth = typesize * 3.5
+			c.showTextAtPoint((time-minTime)*scale - theStringWidth/2, pageRect.origin.y + 2, theTimeString, len(theTimeString))
+			c.setLineWidth(0.3)
+		else:
+			c.setLineWidth(0.1)
+		c.strokePath()
+	c.endPage()
+	c.finish()
+
+			
+for arg in sys.argv[1:]:
+	parselog(arg)
diff --git a/mdnsresponder/mDNSResponder.sln b/mdnsresponder/mDNSResponder.sln
new file mode 100755
index 0000000..ed96501
--- /dev/null
+++ b/mdnsresponder/mDNSResponder.sln
@@ -0,0 +1,400 @@
+Microsoft Visual Studio Solution File, Format Version 9.00

+# Visual Studio 2005

+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DLL", "mDNSWindows\DLL\dnssd.vcproj", "{AB581101-18F0-46F6-B56A-83A6B1EA657E}"

+EndProject

+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mDNSResponder", "mDNSWindows\SystemService\Service.vcproj", "{C1D98254-BA27-4427-A3BE-A68CA2CC5F69}"

+EndProject

+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "NSPTool", "mDNSWindows\NSPTool\NSPTool.vcproj", "{208B3A9F-1CA0-4D1D-9D6C-C61616F94705}"

+EndProject

+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mdnsNSP", "mDNSWindows\mdnsNSP\mdnsNSP.vcproj", "{F4F15529-F0EB-402F-8662-73C5797EE557}"

+	ProjectSection(ProjectDependencies) = postProject

+		{AB581101-18F0-46F6-B56A-83A6B1EA657E} = {AB581101-18F0-46F6-B56A-83A6B1EA657E}

+	EndProjectSection

+EndProject

+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ExplorerPlugin", "Clients\ExplorerPlugin\ExplorerPlugin.vcproj", "{BB8AC1B5-6587-4163-BDC6-788B157705CA}"

+	ProjectSection(ProjectDependencies) = postProject

+		{3A2B6325-3053-4236-84BD-AA9BE2E323E5} = {3A2B6325-3053-4236-84BD-AA9BE2E323E5}

+	EndProjectSection

+EndProject

+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PrinterSetupWizard", "Clients\PrinterSetupWizard\PrinterSetupWizard.vcproj", "{B1D2CDA2-CC8F-45D5-A694-2EE45B0308CF}"

+	ProjectSection(ProjectDependencies) = postProject

+		{3A2B6325-3053-4236-84BD-AA9BE2E323E5} = {3A2B6325-3053-4236-84BD-AA9BE2E323E5}

+	EndProjectSection

+EndProject

+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PrinterSetupWizardLocRes", "Clients\PrinterSetupWizard\PrinterSetupWizardLocRes.vcproj", "{967F5375-0176-43D3-ADA3-22EE25551C37}"

+	ProjectSection(ProjectDependencies) = postProject

+		{AB581101-18F0-46F6-B56A-83A6B1EA657E} = {AB581101-18F0-46F6-B56A-83A6B1EA657E}

+	EndProjectSection

+EndProject

+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "PrinterSetupWizardRes", "Clients\PrinterSetupWizard\PrinterSetupWizardRes.vcproj", "{CFCCB176-6CAA-472B-B0A2-90511C8E2E52}"

+	ProjectSection(ProjectDependencies) = postProject

+		{AB581101-18F0-46F6-B56A-83A6B1EA657E} = {AB581101-18F0-46F6-B56A-83A6B1EA657E}

+	EndProjectSection

+EndProject

+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ExplorerPluginLocRes", "Clients\ExplorerPlugin\ExplorerPluginLocRes.vcproj", "{1643427B-F226-4AD6-B413-97DA64D5C6B4}"

+	ProjectSection(ProjectDependencies) = postProject

+		{AB581101-18F0-46F6-B56A-83A6B1EA657E} = {AB581101-18F0-46F6-B56A-83A6B1EA657E}

+	EndProjectSection

+EndProject

+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ExplorerPluginRes", "Clients\ExplorerPlugin\ExplorerPluginRes.vcproj", "{871B1492-B4A4-4B57-9237-FA798484D7D7}"

+	ProjectSection(ProjectDependencies) = postProject

+		{AB581101-18F0-46F6-B56A-83A6B1EA657E} = {AB581101-18F0-46F6-B56A-83A6B1EA657E}

+	EndProjectSection

+EndProject

+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "dns-sd", "Clients\DNS-SD.VisualStudio\dns-sd.vcproj", "{AA230639-E115-4A44-AA5A-44A61235BA50}"

+EndProject

+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Java", "mDNSWindows\Java\Java.vcproj", "{9CE2568A-3170-41C6-9F20-A0188A9EC114}"

+	ProjectSection(ProjectDependencies) = postProject

+		{AB581101-18F0-46F6-B56A-83A6B1EA657E} = {AB581101-18F0-46F6-B56A-83A6B1EA657E}

+	EndProjectSection

+EndProject

+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "JavaSamples", "Clients\Java\JavaSamples.vcproj", "{A987A0C1-344F-475C-869C-F082EB11EEBA}"

+	ProjectSection(ProjectDependencies) = postProject

+		{9CE2568A-3170-41C6-9F20-A0188A9EC114} = {9CE2568A-3170-41C6-9F20-A0188A9EC114}

+	EndProjectSection

+EndProject

+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DLLStub", "mDNSWindows\DLLStub\DLLStub.vcproj", "{3A2B6325-3053-4236-84BD-AA9BE2E323E5}"

+	ProjectSection(ProjectDependencies) = postProject

+		{AB581101-18F0-46F6-B56A-83A6B1EA657E} = {AB581101-18F0-46F6-B56A-83A6B1EA657E}

+	EndProjectSection

+EndProject

+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "DLLX", "mDNSWindows\DLLX\DLLX.vcproj", "{78FBFCC5-2873-4AE2-9114-A08082F71124}"

+EndProject

+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "DNSServiceBrowser.NET", "Clients\DNSServiceBrowser.NET\DNSServiceBrowser.NET.csproj", "{DE8DB97E-37A3-43ED-9A5E-CCC5F6DE9CB4}"

+EndProject

+Project("{F184B08F-C81C-45F6-A57F-5ABD9991F28F}") = "DNSServiceBrowser.VB", "Clients\DNSServiceBrowser.VB\DNSServiceBrowser.VB.vbproj", "{FB79E297-5703-435C-A829-51AA51CD71C2}"

+EndProject

+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "mDNSNetMonitor", "Clients\mDNSNetMonitor.VisualStudio\mDNSNetMonitor.vcproj", "{AF35C285-528D-46A1-8A0E-47B0733DC718}"

+	ProjectSection(ProjectDependencies) = postProject

+		{C1D98254-BA27-4427-A3BE-A68CA2CC5F69} = {C1D98254-BA27-4427-A3BE-A68CA2CC5F69}

+	EndProjectSection

+EndProject

+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ControlPanelLocRes", "mDNSWindows\ControlPanel\ControlPanelLocRes.vcproj", "{4490229E-025A-478F-A2CF-51154DA83E39}"

+EndProject

+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ControlPanelRes", "mDNSWindows\ControlPanel\ControlPanelRes.vcproj", "{5254AA9C-3D2E-4539-86D9-5EB0F4151215}"

+EndProject

+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "ControlPanel", "mDNSWindows\ControlPanel\ControlPanel.vcproj", "{0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}"

+	ProjectSection(ProjectDependencies) = postProject

+		{3A2B6325-3053-4236-84BD-AA9BE2E323E5} = {3A2B6325-3053-4236-84BD-AA9BE2E323E5}

+	EndProjectSection

+EndProject

+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "FirefoxExtension", "Clients\FirefoxExtension\FirefoxExtension.vcproj", "{7826EA27-D4CC-4FAA-AD23-DF813823227B}"

+	ProjectSection(ProjectDependencies) = postProject

+		{3A2B6325-3053-4236-84BD-AA9BE2E323E5} = {3A2B6325-3053-4236-84BD-AA9BE2E323E5}

+	EndProjectSection

+EndProject

+Global

+	GlobalSection(SolutionConfigurationPlatforms) = preSolution

+		Debug|Any CPU = Debug|Any CPU

+		Debug|Mixed Platforms = Debug|Mixed Platforms

+		Debug|Win32 = Debug|Win32

+		Debug|x64 = Debug|x64

+		Release|Any CPU = Release|Any CPU

+		Release|Mixed Platforms = Release|Mixed Platforms

+		Release|Win32 = Release|Win32

+		Release|x64 = Release|x64

+	EndGlobalSection

+	GlobalSection(ProjectConfigurationPlatforms) = postSolution

+		{AB581101-18F0-46F6-B56A-83A6B1EA657E}.Debug|Any CPU.ActiveCfg = Debug|x64

+		{AB581101-18F0-46F6-B56A-83A6B1EA657E}.Debug|Mixed Platforms.ActiveCfg = Debug|x64

+		{AB581101-18F0-46F6-B56A-83A6B1EA657E}.Debug|Mixed Platforms.Build.0 = Debug|x64

+		{AB581101-18F0-46F6-B56A-83A6B1EA657E}.Debug|Win32.ActiveCfg = Debug|Win32

+		{AB581101-18F0-46F6-B56A-83A6B1EA657E}.Debug|Win32.Build.0 = Debug|Win32

+		{AB581101-18F0-46F6-B56A-83A6B1EA657E}.Debug|x64.ActiveCfg = Debug|x64

+		{AB581101-18F0-46F6-B56A-83A6B1EA657E}.Debug|x64.Build.0 = Debug|x64

+		{AB581101-18F0-46F6-B56A-83A6B1EA657E}.Release|Any CPU.ActiveCfg = Release|x64

+		{AB581101-18F0-46F6-B56A-83A6B1EA657E}.Release|Mixed Platforms.ActiveCfg = Release|x64

+		{AB581101-18F0-46F6-B56A-83A6B1EA657E}.Release|Mixed Platforms.Build.0 = Release|x64

+		{AB581101-18F0-46F6-B56A-83A6B1EA657E}.Release|Win32.ActiveCfg = Release|Win32

+		{AB581101-18F0-46F6-B56A-83A6B1EA657E}.Release|Win32.Build.0 = Release|Win32

+		{AB581101-18F0-46F6-B56A-83A6B1EA657E}.Release|x64.ActiveCfg = Release|x64

+		{AB581101-18F0-46F6-B56A-83A6B1EA657E}.Release|x64.Build.0 = Release|x64

+		{C1D98254-BA27-4427-A3BE-A68CA2CC5F69}.Debug|Any CPU.ActiveCfg = Debug|x64

+		{C1D98254-BA27-4427-A3BE-A68CA2CC5F69}.Debug|Mixed Platforms.ActiveCfg = Debug|x64

+		{C1D98254-BA27-4427-A3BE-A68CA2CC5F69}.Debug|Mixed Platforms.Build.0 = Debug|x64

+		{C1D98254-BA27-4427-A3BE-A68CA2CC5F69}.Debug|Win32.ActiveCfg = Debug|Win32

+		{C1D98254-BA27-4427-A3BE-A68CA2CC5F69}.Debug|Win32.Build.0 = Debug|Win32

+		{C1D98254-BA27-4427-A3BE-A68CA2CC5F69}.Debug|x64.ActiveCfg = Debug|x64

+		{C1D98254-BA27-4427-A3BE-A68CA2CC5F69}.Debug|x64.Build.0 = Debug|x64

+		{C1D98254-BA27-4427-A3BE-A68CA2CC5F69}.Release|Any CPU.ActiveCfg = Release|x64

+		{C1D98254-BA27-4427-A3BE-A68CA2CC5F69}.Release|Mixed Platforms.ActiveCfg = Release|x64

+		{C1D98254-BA27-4427-A3BE-A68CA2CC5F69}.Release|Mixed Platforms.Build.0 = Release|x64

+		{C1D98254-BA27-4427-A3BE-A68CA2CC5F69}.Release|Win32.ActiveCfg = Release|Win32

+		{C1D98254-BA27-4427-A3BE-A68CA2CC5F69}.Release|Win32.Build.0 = Release|Win32

+		{C1D98254-BA27-4427-A3BE-A68CA2CC5F69}.Release|x64.ActiveCfg = Release|x64

+		{C1D98254-BA27-4427-A3BE-A68CA2CC5F69}.Release|x64.Build.0 = Release|x64

+		{208B3A9F-1CA0-4D1D-9D6C-C61616F94705}.Debug|Any CPU.ActiveCfg = Debug|x64

+		{208B3A9F-1CA0-4D1D-9D6C-C61616F94705}.Debug|Mixed Platforms.ActiveCfg = Debug|x64

+		{208B3A9F-1CA0-4D1D-9D6C-C61616F94705}.Debug|Mixed Platforms.Build.0 = Debug|x64

+		{208B3A9F-1CA0-4D1D-9D6C-C61616F94705}.Debug|Win32.ActiveCfg = Debug|Win32

+		{208B3A9F-1CA0-4D1D-9D6C-C61616F94705}.Debug|Win32.Build.0 = Debug|Win32

+		{208B3A9F-1CA0-4D1D-9D6C-C61616F94705}.Debug|x64.ActiveCfg = Debug|x64

+		{208B3A9F-1CA0-4D1D-9D6C-C61616F94705}.Debug|x64.Build.0 = Debug|x64

+		{208B3A9F-1CA0-4D1D-9D6C-C61616F94705}.Release|Any CPU.ActiveCfg = Release|x64

+		{208B3A9F-1CA0-4D1D-9D6C-C61616F94705}.Release|Mixed Platforms.ActiveCfg = Release|x64

+		{208B3A9F-1CA0-4D1D-9D6C-C61616F94705}.Release|Mixed Platforms.Build.0 = Release|x64

+		{208B3A9F-1CA0-4D1D-9D6C-C61616F94705}.Release|Win32.ActiveCfg = Release|Win32

+		{208B3A9F-1CA0-4D1D-9D6C-C61616F94705}.Release|Win32.Build.0 = Release|Win32

+		{208B3A9F-1CA0-4D1D-9D6C-C61616F94705}.Release|x64.ActiveCfg = Release|x64

+		{208B3A9F-1CA0-4D1D-9D6C-C61616F94705}.Release|x64.Build.0 = Release|x64

+		{F4F15529-F0EB-402F-8662-73C5797EE557}.Debug|Any CPU.ActiveCfg = Debug|x64

+		{F4F15529-F0EB-402F-8662-73C5797EE557}.Debug|Mixed Platforms.ActiveCfg = Debug|x64

+		{F4F15529-F0EB-402F-8662-73C5797EE557}.Debug|Mixed Platforms.Build.0 = Debug|x64

+		{F4F15529-F0EB-402F-8662-73C5797EE557}.Debug|Win32.ActiveCfg = Debug|Win32

+		{F4F15529-F0EB-402F-8662-73C5797EE557}.Debug|Win32.Build.0 = Debug|Win32

+		{F4F15529-F0EB-402F-8662-73C5797EE557}.Debug|x64.ActiveCfg = Debug|x64

+		{F4F15529-F0EB-402F-8662-73C5797EE557}.Debug|x64.Build.0 = Debug|x64

+		{F4F15529-F0EB-402F-8662-73C5797EE557}.Release|Any CPU.ActiveCfg = Release|x64

+		{F4F15529-F0EB-402F-8662-73C5797EE557}.Release|Mixed Platforms.ActiveCfg = Release|x64

+		{F4F15529-F0EB-402F-8662-73C5797EE557}.Release|Mixed Platforms.Build.0 = Release|x64

+		{F4F15529-F0EB-402F-8662-73C5797EE557}.Release|Win32.ActiveCfg = Release|Win32

+		{F4F15529-F0EB-402F-8662-73C5797EE557}.Release|Win32.Build.0 = Release|Win32

+		{F4F15529-F0EB-402F-8662-73C5797EE557}.Release|x64.ActiveCfg = Release|x64

+		{F4F15529-F0EB-402F-8662-73C5797EE557}.Release|x64.Build.0 = Release|x64

+		{BB8AC1B5-6587-4163-BDC6-788B157705CA}.Debug|Any CPU.ActiveCfg = Debug|x64

+		{BB8AC1B5-6587-4163-BDC6-788B157705CA}.Debug|Mixed Platforms.ActiveCfg = Debug|x64

+		{BB8AC1B5-6587-4163-BDC6-788B157705CA}.Debug|Mixed Platforms.Build.0 = Debug|x64

+		{BB8AC1B5-6587-4163-BDC6-788B157705CA}.Debug|Win32.ActiveCfg = Debug|Win32

+		{BB8AC1B5-6587-4163-BDC6-788B157705CA}.Debug|Win32.Build.0 = Debug|Win32

+		{BB8AC1B5-6587-4163-BDC6-788B157705CA}.Debug|x64.ActiveCfg = Debug|x64

+		{BB8AC1B5-6587-4163-BDC6-788B157705CA}.Debug|x64.Build.0 = Debug|x64

+		{BB8AC1B5-6587-4163-BDC6-788B157705CA}.Release|Any CPU.ActiveCfg = Release|x64

+		{BB8AC1B5-6587-4163-BDC6-788B157705CA}.Release|Mixed Platforms.ActiveCfg = Release|x64

+		{BB8AC1B5-6587-4163-BDC6-788B157705CA}.Release|Mixed Platforms.Build.0 = Release|x64

+		{BB8AC1B5-6587-4163-BDC6-788B157705CA}.Release|Win32.ActiveCfg = Release|Win32

+		{BB8AC1B5-6587-4163-BDC6-788B157705CA}.Release|Win32.Build.0 = Release|Win32

+		{BB8AC1B5-6587-4163-BDC6-788B157705CA}.Release|x64.ActiveCfg = Release|x64

+		{BB8AC1B5-6587-4163-BDC6-788B157705CA}.Release|x64.Build.0 = Release|x64

+		{B1D2CDA2-CC8F-45D5-A694-2EE45B0308CF}.Debug|Any CPU.ActiveCfg = Debug|x64

+		{B1D2CDA2-CC8F-45D5-A694-2EE45B0308CF}.Debug|Mixed Platforms.ActiveCfg = Debug|x64

+		{B1D2CDA2-CC8F-45D5-A694-2EE45B0308CF}.Debug|Mixed Platforms.Build.0 = Debug|x64

+		{B1D2CDA2-CC8F-45D5-A694-2EE45B0308CF}.Debug|Win32.ActiveCfg = Debug|Win32

+		{B1D2CDA2-CC8F-45D5-A694-2EE45B0308CF}.Debug|Win32.Build.0 = Debug|Win32

+		{B1D2CDA2-CC8F-45D5-A694-2EE45B0308CF}.Debug|x64.ActiveCfg = Debug|x64

+		{B1D2CDA2-CC8F-45D5-A694-2EE45B0308CF}.Debug|x64.Build.0 = Debug|x64

+		{B1D2CDA2-CC8F-45D5-A694-2EE45B0308CF}.Release|Any CPU.ActiveCfg = Release|x64

+		{B1D2CDA2-CC8F-45D5-A694-2EE45B0308CF}.Release|Mixed Platforms.ActiveCfg = Release|x64

+		{B1D2CDA2-CC8F-45D5-A694-2EE45B0308CF}.Release|Mixed Platforms.Build.0 = Release|x64

+		{B1D2CDA2-CC8F-45D5-A694-2EE45B0308CF}.Release|Win32.ActiveCfg = Release|Win32

+		{B1D2CDA2-CC8F-45D5-A694-2EE45B0308CF}.Release|Win32.Build.0 = Release|Win32

+		{B1D2CDA2-CC8F-45D5-A694-2EE45B0308CF}.Release|x64.ActiveCfg = Release|x64

+		{B1D2CDA2-CC8F-45D5-A694-2EE45B0308CF}.Release|x64.Build.0 = Release|x64

+		{967F5375-0176-43D3-ADA3-22EE25551C37}.Debug|Any CPU.ActiveCfg = Debug|x64

+		{967F5375-0176-43D3-ADA3-22EE25551C37}.Debug|Mixed Platforms.ActiveCfg = Debug|x64

+		{967F5375-0176-43D3-ADA3-22EE25551C37}.Debug|Mixed Platforms.Build.0 = Debug|x64

+		{967F5375-0176-43D3-ADA3-22EE25551C37}.Debug|Win32.ActiveCfg = Debug|Win32

+		{967F5375-0176-43D3-ADA3-22EE25551C37}.Debug|Win32.Build.0 = Debug|Win32

+		{967F5375-0176-43D3-ADA3-22EE25551C37}.Debug|x64.ActiveCfg = Debug|x64

+		{967F5375-0176-43D3-ADA3-22EE25551C37}.Debug|x64.Build.0 = Debug|x64

+		{967F5375-0176-43D3-ADA3-22EE25551C37}.Release|Any CPU.ActiveCfg = Release|x64

+		{967F5375-0176-43D3-ADA3-22EE25551C37}.Release|Mixed Platforms.ActiveCfg = Release|x64

+		{967F5375-0176-43D3-ADA3-22EE25551C37}.Release|Mixed Platforms.Build.0 = Release|x64

+		{967F5375-0176-43D3-ADA3-22EE25551C37}.Release|Win32.ActiveCfg = Release|Win32

+		{967F5375-0176-43D3-ADA3-22EE25551C37}.Release|Win32.Build.0 = Release|Win32

+		{967F5375-0176-43D3-ADA3-22EE25551C37}.Release|x64.ActiveCfg = Release|x64

+		{967F5375-0176-43D3-ADA3-22EE25551C37}.Release|x64.Build.0 = Release|x64

+		{CFCCB176-6CAA-472B-B0A2-90511C8E2E52}.Debug|Any CPU.ActiveCfg = Debug|x64

+		{CFCCB176-6CAA-472B-B0A2-90511C8E2E52}.Debug|Mixed Platforms.ActiveCfg = Debug|x64

+		{CFCCB176-6CAA-472B-B0A2-90511C8E2E52}.Debug|Mixed Platforms.Build.0 = Debug|x64

+		{CFCCB176-6CAA-472B-B0A2-90511C8E2E52}.Debug|Win32.ActiveCfg = Debug|Win32

+		{CFCCB176-6CAA-472B-B0A2-90511C8E2E52}.Debug|Win32.Build.0 = Debug|Win32

+		{CFCCB176-6CAA-472B-B0A2-90511C8E2E52}.Debug|x64.ActiveCfg = Debug|x64

+		{CFCCB176-6CAA-472B-B0A2-90511C8E2E52}.Debug|x64.Build.0 = Debug|x64

+		{CFCCB176-6CAA-472B-B0A2-90511C8E2E52}.Release|Any CPU.ActiveCfg = Release|x64

+		{CFCCB176-6CAA-472B-B0A2-90511C8E2E52}.Release|Mixed Platforms.ActiveCfg = Release|x64

+		{CFCCB176-6CAA-472B-B0A2-90511C8E2E52}.Release|Mixed Platforms.Build.0 = Release|x64

+		{CFCCB176-6CAA-472B-B0A2-90511C8E2E52}.Release|Win32.ActiveCfg = Release|Win32

+		{CFCCB176-6CAA-472B-B0A2-90511C8E2E52}.Release|Win32.Build.0 = Release|Win32

+		{CFCCB176-6CAA-472B-B0A2-90511C8E2E52}.Release|x64.ActiveCfg = Release|x64

+		{CFCCB176-6CAA-472B-B0A2-90511C8E2E52}.Release|x64.Build.0 = Release|x64

+		{1643427B-F226-4AD6-B413-97DA64D5C6B4}.Debug|Any CPU.ActiveCfg = Debug|x64

+		{1643427B-F226-4AD6-B413-97DA64D5C6B4}.Debug|Mixed Platforms.ActiveCfg = Debug|x64

+		{1643427B-F226-4AD6-B413-97DA64D5C6B4}.Debug|Mixed Platforms.Build.0 = Debug|x64

+		{1643427B-F226-4AD6-B413-97DA64D5C6B4}.Debug|Win32.ActiveCfg = Debug|Win32

+		{1643427B-F226-4AD6-B413-97DA64D5C6B4}.Debug|Win32.Build.0 = Debug|Win32

+		{1643427B-F226-4AD6-B413-97DA64D5C6B4}.Debug|x64.ActiveCfg = Debug|x64

+		{1643427B-F226-4AD6-B413-97DA64D5C6B4}.Debug|x64.Build.0 = Debug|x64

+		{1643427B-F226-4AD6-B413-97DA64D5C6B4}.Release|Any CPU.ActiveCfg = Release|x64

+		{1643427B-F226-4AD6-B413-97DA64D5C6B4}.Release|Mixed Platforms.ActiveCfg = Release|x64

+		{1643427B-F226-4AD6-B413-97DA64D5C6B4}.Release|Mixed Platforms.Build.0 = Release|x64

+		{1643427B-F226-4AD6-B413-97DA64D5C6B4}.Release|Win32.ActiveCfg = Release|Win32

+		{1643427B-F226-4AD6-B413-97DA64D5C6B4}.Release|Win32.Build.0 = Release|Win32

+		{1643427B-F226-4AD6-B413-97DA64D5C6B4}.Release|x64.ActiveCfg = Release|x64

+		{1643427B-F226-4AD6-B413-97DA64D5C6B4}.Release|x64.Build.0 = Release|x64

+		{871B1492-B4A4-4B57-9237-FA798484D7D7}.Debug|Any CPU.ActiveCfg = Debug|x64

+		{871B1492-B4A4-4B57-9237-FA798484D7D7}.Debug|Mixed Platforms.ActiveCfg = Debug|x64

+		{871B1492-B4A4-4B57-9237-FA798484D7D7}.Debug|Mixed Platforms.Build.0 = Debug|x64

+		{871B1492-B4A4-4B57-9237-FA798484D7D7}.Debug|Win32.ActiveCfg = Debug|Win32

+		{871B1492-B4A4-4B57-9237-FA798484D7D7}.Debug|Win32.Build.0 = Debug|Win32

+		{871B1492-B4A4-4B57-9237-FA798484D7D7}.Debug|x64.ActiveCfg = Debug|x64

+		{871B1492-B4A4-4B57-9237-FA798484D7D7}.Debug|x64.Build.0 = Debug|x64

+		{871B1492-B4A4-4B57-9237-FA798484D7D7}.Release|Any CPU.ActiveCfg = Release|x64

+		{871B1492-B4A4-4B57-9237-FA798484D7D7}.Release|Mixed Platforms.ActiveCfg = Release|x64

+		{871B1492-B4A4-4B57-9237-FA798484D7D7}.Release|Mixed Platforms.Build.0 = Release|x64

+		{871B1492-B4A4-4B57-9237-FA798484D7D7}.Release|Win32.ActiveCfg = Release|Win32

+		{871B1492-B4A4-4B57-9237-FA798484D7D7}.Release|Win32.Build.0 = Release|Win32

+		{871B1492-B4A4-4B57-9237-FA798484D7D7}.Release|x64.ActiveCfg = Release|x64

+		{871B1492-B4A4-4B57-9237-FA798484D7D7}.Release|x64.Build.0 = Release|x64

+		{AA230639-E115-4A44-AA5A-44A61235BA50}.Debug|Any CPU.ActiveCfg = Debug|x64

+		{AA230639-E115-4A44-AA5A-44A61235BA50}.Debug|Mixed Platforms.ActiveCfg = Debug|x64

+		{AA230639-E115-4A44-AA5A-44A61235BA50}.Debug|Mixed Platforms.Build.0 = Debug|x64

+		{AA230639-E115-4A44-AA5A-44A61235BA50}.Debug|Win32.ActiveCfg = Debug|Win32

+		{AA230639-E115-4A44-AA5A-44A61235BA50}.Debug|Win32.Build.0 = Debug|Win32

+		{AA230639-E115-4A44-AA5A-44A61235BA50}.Debug|x64.ActiveCfg = Debug|x64

+		{AA230639-E115-4A44-AA5A-44A61235BA50}.Debug|x64.Build.0 = Debug|x64

+		{AA230639-E115-4A44-AA5A-44A61235BA50}.Release|Any CPU.ActiveCfg = Release|x64

+		{AA230639-E115-4A44-AA5A-44A61235BA50}.Release|Mixed Platforms.ActiveCfg = Release|x64

+		{AA230639-E115-4A44-AA5A-44A61235BA50}.Release|Mixed Platforms.Build.0 = Release|x64

+		{AA230639-E115-4A44-AA5A-44A61235BA50}.Release|Win32.ActiveCfg = Release|Win32

+		{AA230639-E115-4A44-AA5A-44A61235BA50}.Release|Win32.Build.0 = Release|Win32

+		{AA230639-E115-4A44-AA5A-44A61235BA50}.Release|x64.ActiveCfg = Release|x64

+		{AA230639-E115-4A44-AA5A-44A61235BA50}.Release|x64.Build.0 = Release|x64

+		{9CE2568A-3170-41C6-9F20-A0188A9EC114}.Debug|Any CPU.ActiveCfg = Debug|x64

+		{9CE2568A-3170-41C6-9F20-A0188A9EC114}.Debug|Mixed Platforms.ActiveCfg = Debug|x64

+		{9CE2568A-3170-41C6-9F20-A0188A9EC114}.Debug|Mixed Platforms.Build.0 = Debug|x64

+		{9CE2568A-3170-41C6-9F20-A0188A9EC114}.Debug|Win32.ActiveCfg = Debug|Win32

+		{9CE2568A-3170-41C6-9F20-A0188A9EC114}.Debug|Win32.Build.0 = Debug|Win32

+		{9CE2568A-3170-41C6-9F20-A0188A9EC114}.Debug|x64.ActiveCfg = Debug|x64

+		{9CE2568A-3170-41C6-9F20-A0188A9EC114}.Debug|x64.Build.0 = Debug|x64

+		{9CE2568A-3170-41C6-9F20-A0188A9EC114}.Release|Any CPU.ActiveCfg = Release|x64

+		{9CE2568A-3170-41C6-9F20-A0188A9EC114}.Release|Mixed Platforms.ActiveCfg = Release|x64

+		{9CE2568A-3170-41C6-9F20-A0188A9EC114}.Release|Mixed Platforms.Build.0 = Release|x64

+		{9CE2568A-3170-41C6-9F20-A0188A9EC114}.Release|Win32.ActiveCfg = Release|Win32

+		{9CE2568A-3170-41C6-9F20-A0188A9EC114}.Release|Win32.Build.0 = Release|Win32

+		{9CE2568A-3170-41C6-9F20-A0188A9EC114}.Release|x64.ActiveCfg = Release|x64

+		{9CE2568A-3170-41C6-9F20-A0188A9EC114}.Release|x64.Build.0 = Release|x64

+		{A987A0C1-344F-475C-869C-F082EB11EEBA}.Debug|Any CPU.ActiveCfg = Debug|x64

+		{A987A0C1-344F-475C-869C-F082EB11EEBA}.Debug|Mixed Platforms.ActiveCfg = Debug|x64

+		{A987A0C1-344F-475C-869C-F082EB11EEBA}.Debug|Mixed Platforms.Build.0 = Debug|x64

+		{A987A0C1-344F-475C-869C-F082EB11EEBA}.Debug|Win32.ActiveCfg = Debug|Win32

+		{A987A0C1-344F-475C-869C-F082EB11EEBA}.Debug|Win32.Build.0 = Debug|Win32

+		{A987A0C1-344F-475C-869C-F082EB11EEBA}.Debug|x64.ActiveCfg = Debug|x64

+		{A987A0C1-344F-475C-869C-F082EB11EEBA}.Release|Any CPU.ActiveCfg = Release|x64

+		{A987A0C1-344F-475C-869C-F082EB11EEBA}.Release|Mixed Platforms.ActiveCfg = Release|x64

+		{A987A0C1-344F-475C-869C-F082EB11EEBA}.Release|Mixed Platforms.Build.0 = Release|x64

+		{A987A0C1-344F-475C-869C-F082EB11EEBA}.Release|Win32.ActiveCfg = Release|Win32

+		{A987A0C1-344F-475C-869C-F082EB11EEBA}.Release|Win32.Build.0 = Release|Win32

+		{A987A0C1-344F-475C-869C-F082EB11EEBA}.Release|x64.ActiveCfg = Release|x64

+		{3A2B6325-3053-4236-84BD-AA9BE2E323E5}.Debug|Any CPU.ActiveCfg = Debug|x64

+		{3A2B6325-3053-4236-84BD-AA9BE2E323E5}.Debug|Mixed Platforms.ActiveCfg = Debug|x64

+		{3A2B6325-3053-4236-84BD-AA9BE2E323E5}.Debug|Mixed Platforms.Build.0 = Debug|x64

+		{3A2B6325-3053-4236-84BD-AA9BE2E323E5}.Debug|Win32.ActiveCfg = Debug|Win32

+		{3A2B6325-3053-4236-84BD-AA9BE2E323E5}.Debug|Win32.Build.0 = Debug|Win32

+		{3A2B6325-3053-4236-84BD-AA9BE2E323E5}.Debug|x64.ActiveCfg = Debug|x64

+		{3A2B6325-3053-4236-84BD-AA9BE2E323E5}.Debug|x64.Build.0 = Debug|x64

+		{3A2B6325-3053-4236-84BD-AA9BE2E323E5}.Release|Any CPU.ActiveCfg = Release|x64

+		{3A2B6325-3053-4236-84BD-AA9BE2E323E5}.Release|Mixed Platforms.ActiveCfg = Release|x64

+		{3A2B6325-3053-4236-84BD-AA9BE2E323E5}.Release|Mixed Platforms.Build.0 = Release|x64

+		{3A2B6325-3053-4236-84BD-AA9BE2E323E5}.Release|Win32.ActiveCfg = Release|Win32

+		{3A2B6325-3053-4236-84BD-AA9BE2E323E5}.Release|Win32.Build.0 = Release|Win32

+		{3A2B6325-3053-4236-84BD-AA9BE2E323E5}.Release|x64.ActiveCfg = Release|x64

+		{3A2B6325-3053-4236-84BD-AA9BE2E323E5}.Release|x64.Build.0 = Release|x64

+		{78FBFCC5-2873-4AE2-9114-A08082F71124}.Debug|Any CPU.ActiveCfg = Debug|x64

+		{78FBFCC5-2873-4AE2-9114-A08082F71124}.Debug|Mixed Platforms.ActiveCfg = Debug|x64

+		{78FBFCC5-2873-4AE2-9114-A08082F71124}.Debug|Mixed Platforms.Build.0 = Debug|x64

+		{78FBFCC5-2873-4AE2-9114-A08082F71124}.Debug|Win32.ActiveCfg = Debug|Win32

+		{78FBFCC5-2873-4AE2-9114-A08082F71124}.Debug|Win32.Build.0 = Debug|Win32

+		{78FBFCC5-2873-4AE2-9114-A08082F71124}.Debug|x64.ActiveCfg = Debug|x64

+		{78FBFCC5-2873-4AE2-9114-A08082F71124}.Debug|x64.Build.0 = Debug|x64

+		{78FBFCC5-2873-4AE2-9114-A08082F71124}.Release|Any CPU.ActiveCfg = Release|x64

+		{78FBFCC5-2873-4AE2-9114-A08082F71124}.Release|Mixed Platforms.ActiveCfg = Release|x64

+		{78FBFCC5-2873-4AE2-9114-A08082F71124}.Release|Mixed Platforms.Build.0 = Release|x64

+		{78FBFCC5-2873-4AE2-9114-A08082F71124}.Release|Win32.ActiveCfg = Release|Win32

+		{78FBFCC5-2873-4AE2-9114-A08082F71124}.Release|Win32.Build.0 = Release|Win32

+		{78FBFCC5-2873-4AE2-9114-A08082F71124}.Release|x64.ActiveCfg = Release|x64

+		{78FBFCC5-2873-4AE2-9114-A08082F71124}.Release|x64.Build.0 = Release|x64

+		{DE8DB97E-37A3-43ED-9A5E-CCC5F6DE9CB4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU

+		{DE8DB97E-37A3-43ED-9A5E-CCC5F6DE9CB4}.Debug|Any CPU.Build.0 = Debug|Any CPU

+		{DE8DB97E-37A3-43ED-9A5E-CCC5F6DE9CB4}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU

+		{DE8DB97E-37A3-43ED-9A5E-CCC5F6DE9CB4}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU

+		{DE8DB97E-37A3-43ED-9A5E-CCC5F6DE9CB4}.Debug|Win32.ActiveCfg = Debug|Any CPU

+		{DE8DB97E-37A3-43ED-9A5E-CCC5F6DE9CB4}.Debug|x64.ActiveCfg = Debug|Any CPU

+		{DE8DB97E-37A3-43ED-9A5E-CCC5F6DE9CB4}.Release|Any CPU.ActiveCfg = Release|Any CPU

+		{DE8DB97E-37A3-43ED-9A5E-CCC5F6DE9CB4}.Release|Any CPU.Build.0 = Release|Any CPU

+		{DE8DB97E-37A3-43ED-9A5E-CCC5F6DE9CB4}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU

+		{DE8DB97E-37A3-43ED-9A5E-CCC5F6DE9CB4}.Release|Mixed Platforms.Build.0 = Release|Any CPU

+		{DE8DB97E-37A3-43ED-9A5E-CCC5F6DE9CB4}.Release|Win32.ActiveCfg = Release|Any CPU

+		{DE8DB97E-37A3-43ED-9A5E-CCC5F6DE9CB4}.Release|x64.ActiveCfg = Release|Any CPU

+		{FB79E297-5703-435C-A829-51AA51CD71C2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU

+		{FB79E297-5703-435C-A829-51AA51CD71C2}.Debug|Any CPU.Build.0 = Debug|Any CPU

+		{FB79E297-5703-435C-A829-51AA51CD71C2}.Debug|Mixed Platforms.ActiveCfg = Debug|Any CPU

+		{FB79E297-5703-435C-A829-51AA51CD71C2}.Debug|Mixed Platforms.Build.0 = Debug|Any CPU

+		{FB79E297-5703-435C-A829-51AA51CD71C2}.Debug|Win32.ActiveCfg = Debug|Any CPU

+		{FB79E297-5703-435C-A829-51AA51CD71C2}.Debug|x64.ActiveCfg = Debug|Any CPU

+		{FB79E297-5703-435C-A829-51AA51CD71C2}.Release|Any CPU.ActiveCfg = Release|Any CPU

+		{FB79E297-5703-435C-A829-51AA51CD71C2}.Release|Any CPU.Build.0 = Release|Any CPU

+		{FB79E297-5703-435C-A829-51AA51CD71C2}.Release|Mixed Platforms.ActiveCfg = Release|Any CPU

+		{FB79E297-5703-435C-A829-51AA51CD71C2}.Release|Mixed Platforms.Build.0 = Release|Any CPU

+		{FB79E297-5703-435C-A829-51AA51CD71C2}.Release|Win32.ActiveCfg = Release|Any CPU

+		{FB79E297-5703-435C-A829-51AA51CD71C2}.Release|x64.ActiveCfg = Release|Any CPU

+		{AF35C285-528D-46A1-8A0E-47B0733DC718}.Debug|Any CPU.ActiveCfg = Debug|Win32

+		{AF35C285-528D-46A1-8A0E-47B0733DC718}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32

+		{AF35C285-528D-46A1-8A0E-47B0733DC718}.Debug|Mixed Platforms.Build.0 = Debug|Win32

+		{AF35C285-528D-46A1-8A0E-47B0733DC718}.Debug|Win32.ActiveCfg = Debug|Win32

+		{AF35C285-528D-46A1-8A0E-47B0733DC718}.Debug|Win32.Build.0 = Debug|Win32

+		{AF35C285-528D-46A1-8A0E-47B0733DC718}.Debug|x64.ActiveCfg = Debug|Win32

+		{AF35C285-528D-46A1-8A0E-47B0733DC718}.Release|Any CPU.ActiveCfg = Release|Win32

+		{AF35C285-528D-46A1-8A0E-47B0733DC718}.Release|Mixed Platforms.ActiveCfg = Release|Win32

+		{AF35C285-528D-46A1-8A0E-47B0733DC718}.Release|Mixed Platforms.Build.0 = Release|Win32

+		{AF35C285-528D-46A1-8A0E-47B0733DC718}.Release|Win32.ActiveCfg = Release|Win32

+		{AF35C285-528D-46A1-8A0E-47B0733DC718}.Release|Win32.Build.0 = Release|Win32

+		{AF35C285-528D-46A1-8A0E-47B0733DC718}.Release|x64.ActiveCfg = Release|Win32

+		{4490229E-025A-478F-A2CF-51154DA83E39}.Debug|Any CPU.ActiveCfg = Debug|x64

+		{4490229E-025A-478F-A2CF-51154DA83E39}.Debug|Mixed Platforms.ActiveCfg = Debug|x64

+		{4490229E-025A-478F-A2CF-51154DA83E39}.Debug|Mixed Platforms.Build.0 = Debug|x64

+		{4490229E-025A-478F-A2CF-51154DA83E39}.Debug|Win32.ActiveCfg = Debug|Win32

+		{4490229E-025A-478F-A2CF-51154DA83E39}.Debug|Win32.Build.0 = Debug|Win32

+		{4490229E-025A-478F-A2CF-51154DA83E39}.Debug|x64.ActiveCfg = Debug|x64

+		{4490229E-025A-478F-A2CF-51154DA83E39}.Debug|x64.Build.0 = Debug|x64

+		{4490229E-025A-478F-A2CF-51154DA83E39}.Release|Any CPU.ActiveCfg = Release|x64

+		{4490229E-025A-478F-A2CF-51154DA83E39}.Release|Mixed Platforms.ActiveCfg = Release|x64

+		{4490229E-025A-478F-A2CF-51154DA83E39}.Release|Mixed Platforms.Build.0 = Release|x64

+		{4490229E-025A-478F-A2CF-51154DA83E39}.Release|Win32.ActiveCfg = Release|Win32

+		{4490229E-025A-478F-A2CF-51154DA83E39}.Release|Win32.Build.0 = Release|Win32

+		{4490229E-025A-478F-A2CF-51154DA83E39}.Release|x64.ActiveCfg = Release|x64

+		{4490229E-025A-478F-A2CF-51154DA83E39}.Release|x64.Build.0 = Release|x64

+		{5254AA9C-3D2E-4539-86D9-5EB0F4151215}.Debug|Any CPU.ActiveCfg = Debug|x64

+		{5254AA9C-3D2E-4539-86D9-5EB0F4151215}.Debug|Mixed Platforms.ActiveCfg = Debug|x64

+		{5254AA9C-3D2E-4539-86D9-5EB0F4151215}.Debug|Mixed Platforms.Build.0 = Debug|x64

+		{5254AA9C-3D2E-4539-86D9-5EB0F4151215}.Debug|Win32.ActiveCfg = Debug|Win32

+		{5254AA9C-3D2E-4539-86D9-5EB0F4151215}.Debug|Win32.Build.0 = Debug|Win32

+		{5254AA9C-3D2E-4539-86D9-5EB0F4151215}.Debug|x64.ActiveCfg = Debug|x64

+		{5254AA9C-3D2E-4539-86D9-5EB0F4151215}.Debug|x64.Build.0 = Debug|x64

+		{5254AA9C-3D2E-4539-86D9-5EB0F4151215}.Release|Any CPU.ActiveCfg = Release|x64

+		{5254AA9C-3D2E-4539-86D9-5EB0F4151215}.Release|Mixed Platforms.ActiveCfg = Release|x64

+		{5254AA9C-3D2E-4539-86D9-5EB0F4151215}.Release|Mixed Platforms.Build.0 = Release|x64

+		{5254AA9C-3D2E-4539-86D9-5EB0F4151215}.Release|Win32.ActiveCfg = Release|Win32

+		{5254AA9C-3D2E-4539-86D9-5EB0F4151215}.Release|Win32.Build.0 = Release|Win32

+		{5254AA9C-3D2E-4539-86D9-5EB0F4151215}.Release|x64.ActiveCfg = Release|x64

+		{5254AA9C-3D2E-4539-86D9-5EB0F4151215}.Release|x64.Build.0 = Release|x64

+		{0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Debug|Any CPU.ActiveCfg = Debug|x64

+		{0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Debug|Mixed Platforms.ActiveCfg = Debug|x64

+		{0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Debug|Mixed Platforms.Build.0 = Debug|x64

+		{0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Debug|Win32.ActiveCfg = Debug|Win32

+		{0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Debug|Win32.Build.0 = Debug|Win32

+		{0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Debug|x64.ActiveCfg = Debug|x64

+		{0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Debug|x64.Build.0 = Debug|x64

+		{0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Release|Any CPU.ActiveCfg = Release|x64

+		{0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Release|Mixed Platforms.ActiveCfg = Release|x64

+		{0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Release|Mixed Platforms.Build.0 = Release|x64

+		{0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Release|Win32.ActiveCfg = Release|Win32

+		{0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Release|Win32.Build.0 = Release|Win32

+		{0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Release|x64.ActiveCfg = Release|x64

+		{0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}.Release|x64.Build.0 = Release|x64

+		{7826EA27-D4CC-4FAA-AD23-DF813823227B}.Debug|Any CPU.ActiveCfg = Debug|Win32

+		{7826EA27-D4CC-4FAA-AD23-DF813823227B}.Debug|Mixed Platforms.ActiveCfg = Debug|Win32

+		{7826EA27-D4CC-4FAA-AD23-DF813823227B}.Debug|Mixed Platforms.Build.0 = Debug|Win32

+		{7826EA27-D4CC-4FAA-AD23-DF813823227B}.Debug|Win32.ActiveCfg = Debug|Win32

+		{7826EA27-D4CC-4FAA-AD23-DF813823227B}.Debug|Win32.Build.0 = Debug|Win32

+		{7826EA27-D4CC-4FAA-AD23-DF813823227B}.Debug|x64.ActiveCfg = Debug|Win32

+		{7826EA27-D4CC-4FAA-AD23-DF813823227B}.Release|Any CPU.ActiveCfg = Release|Win32

+		{7826EA27-D4CC-4FAA-AD23-DF813823227B}.Release|Mixed Platforms.ActiveCfg = Release|Win32

+		{7826EA27-D4CC-4FAA-AD23-DF813823227B}.Release|Mixed Platforms.Build.0 = Release|Win32

+		{7826EA27-D4CC-4FAA-AD23-DF813823227B}.Release|Win32.ActiveCfg = Release|Win32

+		{7826EA27-D4CC-4FAA-AD23-DF813823227B}.Release|Win32.Build.0 = Release|Win32

+		{7826EA27-D4CC-4FAA-AD23-DF813823227B}.Release|x64.ActiveCfg = Release|Win32

+	EndGlobalSection

+	GlobalSection(SolutionProperties) = preSolution

+		HideSolutionNode = FALSE

+	EndGlobalSection

+EndGlobal

diff --git a/mdnsresponder/mDNSShared/CommonServices.h b/mdnsresponder/mDNSShared/CommonServices.h
new file mode 100644
index 0000000..be49257
--- /dev/null
+++ b/mdnsresponder/mDNSShared/CommonServices.h
@@ -0,0 +1,1537 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@header		CommonServices
+	
+	Common Services for Mac OS X, Linux, Palm, VxWorks, Windows, and Windows CE.
+*/
+
+#ifndef	__COMMON_SERVICES__
+#define	__COMMON_SERVICES__
+
+#ifdef	__cplusplus
+	extern "C" {
+#endif
+
+#if 0
+#pragma mark == Target ==
+#endif
+
+//===========================================================================================================================
+//	 Target
+//===========================================================================================================================
+
+// Macintosh
+
+#if( !defined( TARGET_OS_MAC ) )
+	#if( ( macintosh || __MACH__ ) && !KERNEL )
+		// ConditionalMacros.h in CoreServices will define this TARGET_* flag.
+	#else
+		#define	TARGET_OS_MAC			0
+	#endif
+#endif
+
+#if( !defined( TARGET_API_MAC_OSX_KERNEL ) )
+	#if( __MACH__ && KERNEL )
+		#define	TARGET_API_MAC_OSX_KERNEL		1
+	#else
+		#define	TARGET_API_MAC_OSX_KERNEL		0
+	#endif
+#endif
+
+// FreeBSD
+
+#if( !defined( TARGET_OS_FREEBSD ) )
+	#if( defined( __FreeBSD__ ) )
+		#define TARGET_OS_FREEBSD		1
+	#else
+		#define TARGET_OS_FREEBSD		0
+	#endif
+#endif
+
+// Linux
+
+#if( !defined( TARGET_OS_LINUX ) )
+	#if( defined( __linux__ ) )
+		#define	TARGET_OS_LINUX			1
+	#else
+		#define	TARGET_OS_LINUX			0
+	#endif
+#endif
+
+// Solaris
+
+#if( !defined( TARGET_OS_SOLARIS ) )
+	#if( defined(solaris) || (defined(__SVR4) && defined(sun)) )
+		#define	TARGET_OS_SOLARIS		1
+	#else
+		#define	TARGET_OS_SOLARIS		0
+	#endif
+#endif
+
+// Palm
+
+#if( !defined( TARGET_OS_PALM ) )
+	#if( defined( __PALMOS_TRAPS__ ) || defined( __PALMOS_ARMLET__ ) )
+		#define	TARGET_OS_PALM			1
+	#else
+		#define	TARGET_OS_PALM			0
+	#endif
+#endif
+
+// VxWorks
+
+#if( !defined( TARGET_OS_VXWORKS ) )
+	
+	// No predefined macro for VxWorks so just assume VxWorks if nothing else is set.
+	
+	#if( !macintosh && !__MACH__  && !defined( __FreeBSD__ ) && !defined( __linux__ ) && !defined ( __SVR4 ) && !defined ( __sun ) && !defined( __PALMOS_TRAPS__ ) && !defined( __PALMOS_ARMLET__ ) && !defined( _WIN32 ) )
+		#define	TARGET_OS_VXWORKS		1
+	#else
+		#define	TARGET_OS_VXWORKS		0
+	#endif
+#endif
+
+// Windows
+
+#if( !defined( TARGET_OS_WIN32 ) )
+	#if( macintosh || __MACH__ )
+		// ConditionalMacros.h in CoreServices will define this TARGET_* flag.
+	#else			
+		#if( defined( _WIN32 ) )
+			#define	TARGET_OS_WIN32		1
+		#else
+			#define	TARGET_OS_WIN32		0
+		#endif
+	#endif
+#endif
+
+// Windows CE
+
+#if( !defined( TARGET_OS_WINDOWS_CE ) )
+	#if( defined( _WIN32_WCE ) )
+		#define	TARGET_OS_WINDOWS_CE	1
+	#else
+		#define	TARGET_OS_WINDOWS_CE	0
+	#endif
+#endif
+
+#if 0
+#pragma mark == Includes ==
+#endif
+
+//===========================================================================================================================
+//	 Includes
+//===========================================================================================================================
+
+#if( !KERNEL )
+	#if defined(WIN32) && !defined(_WSPIAPI_COUNTOF)
+		#define _WSPIAPI_COUNTOF(_Array) (sizeof(_Array) / sizeof(_Array[0]))
+	#endif
+	#include	<stddef.h>
+#endif
+	
+#if( ( macintosh || __MACH__ ) && !KERNEL )
+		
+	#if( defined( __MWERKS__ ) )
+		#if( __option( c9x ) )
+			#include	<stdbool.h>
+		#endif
+	#else
+		#include	<stdbool.h>
+	#endif
+	
+	#include	<stdint.h>
+	
+	#if( __MACH__ )
+		
+		// Mac OS X
+		
+		#include	<sys/types.h>
+		#include	<netinet/in.h>
+		#include	<arpa/inet.h>
+		#include	<fcntl.h>
+		#include	<pthread.h>
+		#include	<sys/ioctl.h>
+		#include	<sys/socket.h>
+		#include	<unistd.h>
+
+	#else
+		
+		// Classic Mac OS
+		
+		#include	<ConditionalMacros.h>
+		#include	<MacTypes.h>
+	
+	#endif
+	
+#elif( KERNEL )
+	
+	// Mac OS X Kernel
+	
+	#include	<stdint.h>
+	
+	#include	<libkern/OSTypes.h>
+	#include	<sys/types.h>
+	
+#elif( TARGET_OS_FREEBSD )
+
+	// FreeBSD
+	#include	<stdint.h>
+	#include	<pthread.h>
+	#include	<netinet/in.h>
+	#include	<arpa/inet.h>
+	#include	<sys/socket.h>
+
+#elif( TARGET_OS_LINUX )
+	
+	// Linux
+	
+	#include	<stdint.h>
+	#include	<arpa/inet.h>
+	
+#elif( TARGET_OS_SOLARIS )
+	
+	// Solaris
+
+	#include	<stdint.h>
+
+	#include	<arpa/inet.h>
+	#include	<arpa/nameser.h>
+
+	#if ( defined( BYTE_ORDER ) && defined( LITTLE_ENDIAN ) && ( BYTE_ORDER == LITTLE_ENDIAN ) )
+		#define TARGET_RT_LITTLE_ENDIAN		1
+	#endif
+	#if ( defined( BYTE_ORDER ) && defined( BIG_ENDIAN ) && ( BYTE_ORDER == BIG_ENDIAN ) )
+		#define TARGET_RT_BIG_ENDIAN		1
+	#endif
+
+#elif( TARGET_OS_PALM )
+	
+	// Palm (no special includes yet).
+
+#elif( TARGET_OS_VXWORKS )
+	
+	// VxWorks
+	
+	#include	"vxWorks.h"
+	
+#elif( TARGET_OS_WIN32 )
+	
+	// Windows
+	
+	#if( !defined( WIN32_WINDOWS ) )
+		#define	WIN32_WINDOWS		0x0401
+	#endif
+	
+	#if( !defined( _WIN32_WINDOWS ) )
+		#define	_WIN32_WINDOWS		0x0401
+	#endif
+	
+	#if( !defined( WIN32_LEAN_AND_MEAN ) )
+		#define	WIN32_LEAN_AND_MEAN			// Needed to avoid redefinitions by Windows interfaces.
+	#endif
+	
+	#if( defined( __MWERKS__ ) )
+	
+		#if( __option( c9x ) )
+			#include	<stdbool.h>
+		#endif
+		
+		#include	<stdint.h>
+		
+	#elif( defined( _MSC_VER ) )
+	
+		#pragma warning( disable:4127 )		// Disable "conditional expression is constant" warning for debug macros.
+		#pragma warning( disable:4706 )		// Disable "assignment within conditional expression" for Microsoft headers.
+		
+	#endif
+
+	#include	<windows.h>
+	#include	<winsock2.h>
+	#include	<Ws2tcpip.h>
+	
+	#if( defined( _MSC_VER ) )
+		#pragma warning( default:4706 )
+	#endif
+	
+#else
+	#error unknown OS - update this file to support your OS
+#endif
+
+#if( !defined( TARGET_BUILD_MAIN ) )
+	#if( !TARGET_OS_VXWORKS )
+		#define	TARGET_BUILD_MAIN		1
+	#endif
+#endif
+
+#if( __GNUC__ || !TARGET_OS_VXWORKS )
+	#define	TARGET_LANGUAGE_C_LIKE		1
+#else
+	#define	TARGET_LANGUAGE_C_LIKE		0
+#endif
+
+#if 0
+#pragma mark == CPU ==
+#endif
+
+//===========================================================================================================================
+//	CPU
+//===========================================================================================================================
+
+// PowerPC
+
+#if( !defined( TARGET_CPU_PPC ) )
+	#if( defined( __ppc__ ) || defined( __PPC__ ) || defined( powerpc ) || defined( ppc ) || defined( _M_MPPC ) )
+		#define	TARGET_CPU_PPC				1
+	#else
+		#define	TARGET_CPU_PPC				0
+	#endif
+#endif
+
+// x86
+
+#if( !defined( TARGET_CPU_X86 ) )
+	#if( __INTEL__ || defined( __i386__ ) || defined( i386 ) || defined( intel ) || defined( _M_IX86 ) )
+		#define	TARGET_CPU_X86				1
+	#else
+		#define	TARGET_CPU_X86				0
+	#endif
+#endif
+
+// MIPS
+
+#if( !defined( TARGET_CPU_MIPS ) )
+	#if( __MIPS__ || defined( MIPS32 ) || defined( R3000 ) || defined( R4000 ) || defined( R4650 ) || defined( _M_MRX000 ) )
+		#define	TARGET_CPU_MIPS				1
+	#else
+		#define	TARGET_CPU_MIPS				0
+	#endif
+#endif
+
+#if( !defined( TARGET_CPU_PPC ) && !defined( TARGET_CPU_X86 ) && !defined( TARGET_CPU_MIPS ) )
+	#error unknown CPU - update this file to support your CPU
+#endif
+
+#if 0
+#pragma mark == Byte Order ==
+#endif
+
+//===========================================================================================================================
+//	Byte Order
+//===========================================================================================================================
+
+// TARGET_RT_LITTLE_ENDIAN
+
+#if( !defined( TARGET_RT_LITTLE_ENDIAN ) )
+	#if( MIPSEL || IL_LITTLE_ENDIAN || defined( __LITTLE_ENDIAN__ ) 										|| \
+		 ( defined(   BYTE_ORDER ) && defined(   LITTLE_ENDIAN ) && (   BYTE_ORDER ==   LITTLE_ENDIAN ) )	|| \
+		 ( defined(  _BYTE_ORDER ) && defined(  _LITTLE_ENDIAN ) && (  _BYTE_ORDER ==  _LITTLE_ENDIAN ) )	|| \
+		 ( defined( __BYTE_ORDER ) && defined( __LITTLE_ENDIAN ) && ( __BYTE_ORDER == __LITTLE_ENDIAN ) )	|| \
+		 TARGET_CPU_X86 || ( defined( TARGET_RT_BIG_ENDIAN ) && !TARGET_RT_BIG_ENDIAN ) )
+		#define	TARGET_RT_LITTLE_ENDIAN		1
+	#else
+		#define	TARGET_RT_LITTLE_ENDIAN		0
+	#endif
+#endif
+
+// TARGET_RT_BIG_ENDIAN
+
+#if( !defined( TARGET_RT_BIG_ENDIAN ) )
+	#if( MIPSEB || IL_BIG_ENDIAN || defined( __BIG_ENDIAN__ ) 										|| \
+		 ( defined(   BYTE_ORDER ) && defined(   BIG_ENDIAN ) && (   BYTE_ORDER ==   BIG_ENDIAN ) )	|| \
+		 ( defined(  _BYTE_ORDER ) && defined(  _BIG_ENDIAN ) && (  _BYTE_ORDER ==  _BIG_ENDIAN ) )	|| \
+		 ( defined( __BYTE_ORDER ) && defined( __BIG_ENDIAN ) && ( __BYTE_ORDER == __BIG_ENDIAN ) )	|| \
+		( defined( TARGET_RT_LITTLE_ENDIAN ) && !TARGET_RT_LITTLE_ENDIAN ) )
+		#define	TARGET_RT_BIG_ENDIAN		1
+	#else
+		#define	TARGET_RT_BIG_ENDIAN		0
+	#endif
+#endif
+
+#if( defined( TARGET_RT_LITTLE_ENDIAN ) && !defined( TARGET_RT_BIG_ENDIAN ) )
+	#if( TARGET_RT_LITTLE_ENDIAN )
+		#define	TARGET_RT_BIG_ENDIAN		0
+	#else
+		#define	TARGET_RT_BIG_ENDIAN		1
+	#endif
+#endif
+
+#if( defined( TARGET_RT_BIG_ENDIAN ) && !defined( TARGET_RT_LITTLE_ENDIAN ) )
+	#if( TARGET_RT_BIG_ENDIAN )
+		#define	TARGET_RT_LITTLE_ENDIAN		0
+	#else
+		#define	TARGET_RT_LITTLE_ENDIAN		1
+	#endif
+#endif
+
+#if( !defined( TARGET_RT_LITTLE_ENDIAN ) || !defined( TARGET_RT_BIG_ENDIAN ) )
+	#error unknown byte order - update this file to support your byte order
+#endif
+
+// TARGET_RT_BYTE_ORDER
+
+#if( !defined( TARGET_RT_BYTE_ORDER_BIG_ENDIAN ) )
+	#define	TARGET_RT_BYTE_ORDER_BIG_ENDIAN			1234
+#endif
+
+#if( !defined( TARGET_RT_BYTE_ORDER_LITTLE_ENDIAN ) )
+	#define	TARGET_RT_BYTE_ORDER_LITTLE_ENDIAN		4321
+#endif
+
+#if( !defined( TARGET_RT_BYTE_ORDER ) )
+	#if( TARGET_RT_LITTLE_ENDIAN )
+		#define	TARGET_RT_BYTE_ORDER				TARGET_RT_BYTE_ORDER_LITTLE_ENDIAN
+	#else
+		#define	TARGET_RT_BYTE_ORDER				TARGET_RT_BYTE_ORDER_BIG_ENDIAN
+	#endif
+#endif
+
+#if 0
+#pragma mark == Constants ==
+#endif
+
+//===========================================================================================================================
+//	Constants
+//===========================================================================================================================
+
+#if( !TARGET_OS_MAC )
+	#define CR		'\r'
+#endif
+
+#define LF			'\n'
+#define	CRSTR		"\r"
+#define	LFSTR		"\n"
+#define CRLF		"\r\n"
+#define CRCR 		"\r\r"
+
+#if 0
+#pragma mark == Compatibility ==
+#endif
+
+//===========================================================================================================================
+//	Compatibility
+//===========================================================================================================================
+
+// Macros to allow the same code to work on Windows and other sockets API-compatible platforms.
+
+#if( TARGET_OS_WIN32 )
+	#define	close_compat( X )		closesocket( X )
+	#define	errno_compat()			(int) GetLastError()
+	#define	set_errno_compat( X )	SetLastError( X )
+	#define	EWOULDBLOCK_compat		WSAEWOULDBLOCK
+	#define	ETIMEDOUT_compat		WSAETIMEDOUT
+	#define	ENOTCONN_compat			WSAENOTCONN
+	#define	IsValidSocket( X )		( ( X ) != INVALID_SOCKET )
+	#define	kInvalidSocketRef		INVALID_SOCKET
+	#if( TARGET_LANGUAGE_C_LIKE )
+		typedef SOCKET				SocketRef;
+	#endif
+#else
+	#define	close_compat( X )		close( X )
+	#define	errno_compat()			errno
+	#define	set_errno_compat( X )	do { errno = ( X ); } while( 0 )
+	#define	EWOULDBLOCK_compat		EWOULDBLOCK
+	#define	ETIMEDOUT_compat		ETIMEDOUT
+	#define	ENOTCONN_compat			ENOTCONN
+	#define	IsValidSocket( X )		( ( X ) >= 0 )
+	#define	kInvalidSocketRef		-1
+	#if( TARGET_LANGUAGE_C_LIKE )
+		typedef int					SocketRef;
+	#endif
+#endif
+
+// socklen_t is not defined on the following platforms so emulate it if not defined:
+// 
+// - Pre-Panther Mac OS X. Panther defines SO_NOADDRERR so trigger off that.
+// - Windows SDK prior to 2003. 2003+ SDK's define EAI_AGAIN so trigger off that.
+// - VxWorks
+
+#if( TARGET_LANGUAGE_C_LIKE )
+	#if( ( TARGET_OS_MAC && !defined( SO_NOADDRERR ) ) || ( TARGET_OS_WIN32 && !defined( EAI_AGAIN ) ) || TARGET_OS_VXWORKS )
+		typedef int						socklen_t;
+	#endif
+#endif
+
+// ssize_t is not defined on the following platforms so emulate it if not defined:
+// 
+// - Mac OS X when not building with BSD headers
+// - Windows
+
+#if( TARGET_LANGUAGE_C_LIKE )
+	#if( !defined(_SSIZE_T) && ( TARGET_OS_WIN32 || !defined( _BSD_SSIZE_T_DEFINED_ ) ) && !TARGET_OS_FREEBSD && !TARGET_OS_LINUX && !TARGET_OS_VXWORKS && !TARGET_OS_MAC)
+		typedef int						ssize_t;
+	#endif
+#endif
+
+// sockaddr_storage is not supported on non-IPv6 machines so alias it to an IPv4-compatible structure.
+
+#if( TARGET_LANGUAGE_C_LIKE )
+	#if( !defined( AF_INET6 ) )
+		#define	sockaddr_storage		sockaddr_in
+		#define	ss_family				sin_family
+	#endif
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@defined	SOCKADDR_IS_IP_LOOPBACK
+	
+	@abstract	Determines if a sockaddr is an IPv4 or IPv6 loopback address (if IPv6 is supported).
+*/
+
+#if( defined( AF_INET6 ) )
+	#define	SOCKADDR_IS_IP_LOOPBACK( SA )															\
+		( ( (const struct sockaddr *)( SA ) )->sa_family == AF_INET )   							\
+		? ( ( (const struct sockaddr_in *)( SA ) )->sin_addr.s_addr == htonl( INADDR_LOOPBACK ) )	\
+		: ( ( (const struct sockaddr *)( SA ) )->sa_family == AF_INET6 ) 							\
+			? IN6_IS_ADDR_LOOPBACK( &( (const struct sockaddr_in6 *)( SA ) )->sin6_addr ) 			\
+			: 0
+#else
+	#define	SOCKADDR_IS_IP_LOOPBACK( SA )															\
+		( ( (const struct sockaddr *)( SA ) )->sa_family == AF_INET )  								\
+		? ( ( (const struct sockaddr_in *)( SA ) )->sin_addr.s_addr == htonl( INADDR_LOOPBACK ) ) 	\
+		: 0
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@defined	SOCKADDR_IS_IP_LINK_LOCAL
+	
+	@abstract	Determines if a sockaddr is an IPv4 or IPv6 link-local address (if IPv6 is supported).
+*/
+
+#if( defined( AF_INET6 ) )
+	#define	SOCKADDR_IS_IP_LINK_LOCAL( SA )																\
+		( ( ( (const struct sockaddr *)( SA ) )->sa_family == AF_INET )   								\
+		  ? ( ( ( (uint8_t *)( &( (const struct sockaddr_in *)( SA ) )->sin_addr ) )[ 0 ] == 169 ) && 	\
+			  ( ( (uint8_t *)( &( (const struct sockaddr_in *)( SA ) )->sin_addr ) )[ 1 ] == 254 ) )	\
+		  : IN6_IS_ADDR_LOOPBACK( &( (const struct sockaddr_in6 *)( SA ) )->sin6_addr ) )
+#else
+	#define	SOCKADDR_IS_IP_LINK_LOCAL( SA )																\
+		( ( ( (const struct sockaddr *)( SA ) )->sa_family == AF_INET )   								\
+		  ? ( ( ( (uint8_t *)( &( (const struct sockaddr_in *)( SA ) )->sin_addr ) )[ 0 ] == 169 ) && 	\
+			  ( ( (uint8_t *)( &( (const struct sockaddr_in *)( SA ) )->sin_addr ) )[ 1 ] == 254 ) )	\
+		  : 0 )
+#endif
+
+// _beginthreadex and _endthreadex are not supported on Windows CE 2.1 or later (the C runtime issues with leaking 
+// resources have apparently been resolved and they seem to have just ripped out support for the API) so map it to 
+// CreateThread on Windows CE.
+
+#if( TARGET_OS_WINDOWS_CE )
+	#define	_beginthreadex_compat( SECURITY_PTR, STACK_SIZE, START_ADDRESS, ARG_LIST, FLAGS, THREAD_ID_PTR )			\
+		(uintptr_t) CreateThread( SECURITY_PTR, STACK_SIZE, (LPTHREAD_START_ROUTINE) START_ADDRESS, ARG_LIST, FLAGS, 	\
+					  (LPDWORD) THREAD_ID_PTR )
+	
+	#define	_endthreadex_compat( RESULT )		ExitThread( (DWORD) RESULT )
+#elif( TARGET_OS_WIN32 )
+	#define	_beginthreadex_compat				_beginthreadex
+	#define	_endthreadex_compat					_endthreadex
+#endif
+
+// The C99 "inline" keyword is not supported by Microsoft compilers, but they do support __inline so map it when needed.
+
+#if( defined( _MSC_VER ) )
+	#define	inline_compat		__inline
+#else
+	#define	inline_compat		inline
+#endif
+
+// Calling conventions 
+
+#if( !defined( CALLBACK_COMPAT ) )
+	#if( TARGET_OS_WIN32 || TARGET_OS_WINDOWS_CE )
+		#define	CALLBACK_COMPAT		CALLBACK
+	#else
+		#define	CALLBACK_COMPAT
+	#endif
+#endif
+
+#if 0
+#pragma mark == Macros ==
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@defined	kSizeCString
+
+	@abstract	A meta-value to pass to supported routines to indicate the size should be calculated with strlen.
+*/
+
+#define	kSizeCString		( (size_t) -1 )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@defined	sizeof_array
+	
+	@abstract	Determines the number of elements in an array.
+*/
+
+#define	sizeof_array( X )		( sizeof( X ) / sizeof( X[ 0 ] ) )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@defined	sizeof_element
+	
+	@abstract	Determines the size of an array element.
+*/
+
+#define	sizeof_element( X )		sizeof( X[ 0 ] )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@defined	sizeof_string
+	
+	@abstract	Determines the size of a constant C string, excluding the null terminator.
+*/
+
+#define	sizeof_string( X )		( sizeof( ( X ) ) - 1 )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@defined	sizeof_field
+	
+	@abstract	Determines the size of a field of a type.
+*/
+
+#define	sizeof_field( TYPE, FIELD )		sizeof( ( ( (TYPE *) 0 )->FIELD ) )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@function	RoundUp
+
+	@abstract	Rounds X up to a multiple of Y.
+*/
+
+#define	RoundUp( X, Y )		( ( X ) + ( ( Y ) - ( ( X ) % ( Y ) ) ) )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@function	IsAligned
+
+	@abstract	Returns non-zero if X is aligned to a Y byte boundary and 0 if not. Y must be a power of 2.
+*/
+
+#define	IsAligned( X, Y )		( ( ( X ) & ( ( Y ) - 1 ) ) == 0 )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@function	IsFieldAligned
+
+	@abstract	Returns non-zero if FIELD of type TYPE is aligned to a Y byte boundary and 0 if not. Y must be a power of 2.
+*/
+
+#define	IsFieldAligned( X, TYPE, FIELD, Y )		IsAligned( ( (uintptr_t)( X ) ) + offsetof( TYPE, FIELD ), ( Y ) )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@function	AlignDown
+
+	@abstract	Aligns X down to a Y byte boundary. Y must be a power of 2.
+*/
+
+#define	AlignDown( X, Y )		( ( X ) & ~( ( Y ) - 1 ) )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@function	AlignUp
+
+	@abstract	Aligns X up to a Y byte boundary. Y must be a power of 2.
+*/
+
+#define	AlignUp( X, Y )		( ( ( X ) + ( ( Y ) - 1 ) ) & ~( ( Y ) - 1 ) )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@function	Min
+
+	@abstract	Returns the lesser of X and Y.
+*/
+
+#if( !defined( Min ) )
+	#define	Min( X, Y )		( ( ( X ) < ( Y ) ) ? ( X ) : ( Y ) )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@function	Max
+
+	@abstract	Returns the greater of X and Y.
+*/
+
+#if( !defined( Max ) )
+	#define	Max( X, Y )		( ( ( X ) > ( Y ) ) ? ( X ) : ( Y ) )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@function	InsertBits
+
+	@abstract	Inserts BITS (both 0 and 1 bits) into X, controlled by MASK and SHIFT, and returns the result.
+	
+	@discussion
+	
+	MASK is the bitmask of the bits in the final position.
+	SHIFT is the number of bits to shift left for 1 to reach the first bit position of MASK.
+	
+	For example, if you wanted to insert 0x3 into the leftmost 4 bits of a 32-bit value:
+	
+	InsertBits( 0, 0x3, 0xF0000000U, 28 ) == 0x30000000
+*/
+
+#define	InsertBits( X, BITS, MASK, SHIFT )		( ( ( X ) & ~( MASK ) ) | ( ( ( BITS ) << ( SHIFT ) ) & ( MASK ) ) )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@function	ExtractBits
+
+	@abstract	Extracts bits from X, controlled by MASK and SHIFT, and returns the result.
+	
+	@discussion
+	
+	MASK is the bitmask of the bits in the final position.
+	SHIFT is the number of bits to shift right to right justify MASK.
+	
+	For example, if you had a 32-bit value (e.g. 0x30000000) wanted the left-most 4 bits (e.g. 3 in this example):
+	
+	ExtractBits( 0x30000000U, 0xF0000000U, 28 ) == 0x3
+*/
+
+#define	ExtractBits( X, MASK, SHIFT )			( ( ( X ) >> ( SHIFT ) ) & ( ( MASK ) >> ( SHIFT ) ) )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@function	Stringify
+
+	@abstract	Stringify's an expression.
+	
+	@discussion
+	
+	Stringify macros to process raw text passed via -D options to C string constants. The double-wrapping is necessary 
+	because the C preprocessor doesn't perform its normal argument expansion pre-scan with stringified macros so the 
+	-D macro needs to be expanded once via the wrapper macro then stringified so the raw text is stringified. Otherwise, 
+	the replacement value would be used instead of the symbolic name (only for preprocessor symbols like #defines).
+	
+	For example:
+	
+		#define	kMyConstant		1
+		
+		printf( "%s", Stringify( kMyConstant ) );			// Prints "kMyConstant"
+		printf( "%s", StringifyExpansion( kMyConstant ) );	// Prints "1"
+		
+	Non-preprocessor symbols do not have this issue. For example:
+	
+		enum
+		{
+			kMyConstant = 1
+		};
+		
+		printf( "%s", Stringify( kMyConstant ) );			// Prints "kMyConstant"
+		printf( "%s", StringifyExpansion( kMyConstant ) );	// Prints "kMyConstant"
+	
+	See <http://gcc.gnu.org/onlinedocs/cpp/Argument-Prescan.html> for more info on C preprocessor pre-scanning.
+*/
+
+#define	Stringify( X )				# X
+#define	StringifyExpansion( X )		Stringify( X )
+
+#if 0
+#pragma mark == Types ==
+#endif
+
+#if( TARGET_LANGUAGE_C_LIKE )
+//===========================================================================================================================
+//	 Standard Types
+//===========================================================================================================================
+
+#if( !defined( INT8_MIN ) )
+	
+	#define INT8_MIN					SCHAR_MIN
+	
+	#if( defined( _MSC_VER ) )
+
+		// C99 stdint.h not supported in VC++/VS.NET yet.
+
+		typedef INT8					int8_t;
+		typedef UINT8					uint8_t;
+		typedef INT16					int16_t;
+		typedef UINT16					uint16_t;
+		typedef INT32					int32_t;
+		typedef UINT32					uint32_t;
+		typedef __int64					int64_t;
+		typedef unsigned __int64		uint64_t;
+		
+	#elif( TARGET_OS_VXWORKS && ( TORNADO_VERSION < 220 ) )
+		typedef long long				int64_t;
+		typedef unsigned long long		uint64_t;
+	#endif
+	
+	typedef int8_t						int_least8_t;
+	typedef int16_t						int_least16_t;
+	typedef int32_t						int_least32_t;
+	typedef int64_t						int_least64_t;
+
+	typedef uint8_t						uint_least8_t;
+	typedef uint16_t					uint_least16_t;
+	typedef uint32_t					uint_least32_t;
+	typedef uint64_t					uint_least64_t;
+	
+	typedef int8_t						int_fast8_t;
+	typedef int16_t						int_fast16_t;
+	typedef int32_t						int_fast32_t;
+	typedef int64_t						int_fast64_t;
+	
+	typedef uint8_t						uint_fast8_t;
+	typedef uint16_t					uint_fast16_t;
+	typedef uint32_t					uint_fast32_t;
+	typedef uint64_t					uint_fast64_t;
+
+	#if( !defined( _MSC_VER ) || TARGET_OS_WINDOWS_CE )
+		typedef long int				intptr_t;
+		typedef unsigned long int		uintptr_t;
+	#endif
+
+#endif
+
+// Macros for minimum-width integer constants
+
+#if( !defined( INT8_C ) )
+	#define INT8_C( value )			value
+#endif
+
+#if( !defined( INT16_C ) )
+	#define INT16_C( value )		value
+#endif
+
+#if( !defined( INT32_C ) )
+	#define INT32_C( value )		value ## L
+#endif
+
+#if( !defined( INT64_C ) )
+	#if( defined( _MSC_VER ) )
+		#define INT64_C( value )	value ## i64
+	#else
+		#define INT64_C( value )	value ## LL
+	#endif
+#endif
+
+#if( !defined( UINT8_C ) )
+	#define UINT8_C( value )		value ## U
+#endif
+
+#if( !defined( UINT16_C ) )
+	#define UINT16_C( value )		value ## U
+#endif
+
+#if( !defined( UINT32_C ) )
+	#define UINT32_C( value )		value ## UL
+#endif
+
+#if( !defined( UINT64_C ) )
+	#if( defined( _MSC_VER ) )
+		#define UINT64_C( value )	value ## UI64
+	#else
+		#define UINT64_C( value )	value ## ULL
+	#endif
+#endif
+
+#if 0
+#pragma mark == bool ==
+#endif
+
+//===========================================================================================================================
+//	 Boolean Constants and Types
+//===========================================================================================================================
+
+// C++ defines bool, true, and false. Metrowerks allows this to be controlled by the "bool" option though.
+// C99 defines __bool_true_false_are_defined when bool, true, and false are defined.
+// MacTypes.h defines true and false (Mac builds only).
+// 
+// Note: The Metrowerks has to be in its own block because Microsoft Visual Studio .NET does not completely 
+// short-circuit and gets confused by the option( bool ) portion of the conditional.
+
+#if( defined( __MWERKS__ ) )
+	
+	// Note: The following test is done on separate lines because CodeWarrior doesn't like it all on one line.
+	
+	#if( !__bool_true_false_are_defined && ( !defined( __cplusplus ) || !__option( bool ) ) )
+		#define	COMMON_SERVICES_NEEDS_BOOL		1
+	#else
+		#define	COMMON_SERVICES_NEEDS_BOOL		0
+	#endif
+	
+	// Workaround when building with CodeWarrior, but using the Apple stdbool.h header, which uses _Bool.
+	
+	#if( __bool_true_false_are_defined && !defined( __cplusplus ) && !__option( c9x ) )
+		#define _Bool	int
+	#endif
+	
+	// Workaround when building with CodeWarrior for C++ with bool disabled and using the Apple stdbool.h header, 
+	// which defines true and false to map to C++ true and false (which are not enabled). Serenity Now!
+	
+	#if( __bool_true_false_are_defined && defined( __cplusplus ) && !__option( bool ) )
+		#define	true	1
+		#define	false	0
+	#endif
+#else
+	#define	COMMON_SERVICES_NEEDS_BOOL			( !defined( __cplusplus ) && !__bool_true_false_are_defined )
+#endif
+
+#if( COMMON_SERVICES_NEEDS_BOOL )
+	
+	typedef int		bool;
+	
+	#define	bool	bool
+	
+	#if( !defined( __MACTYPES__ ) && !defined( true ) && !defined( false ) )
+		#define true	1
+		#define false	0
+	#endif
+	
+	#define __bool_true_false_are_defined		1
+#endif
+
+// IOKit IOTypes.h typedef's bool if TYPE_BOOL is not defined so define it here to prevent redefinition by IOTypes.h.
+
+#if( TARGET_API_MAC_OSX_KERNEL )
+	#define TYPE_BOOL		1
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@typedef	CStr255
+	
+	@abstract	255 character null-terminated (C-style) string.
+*/
+
+#if( TARGET_LANGUAGE_C_LIKE )
+	typedef char	CStr255[ 256 ];
+#endif
+
+#endif	// TARGET_LANGUAGE_C_LIKE
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@defined	TYPE_LONGLONG_NATIVE
+
+	@abstract	Defines whether long long (or its equivalent) is natively supported or requires special libraries.
+*/
+
+#if( !defined( TYPE_LONGLONG_NATIVE ) )
+	#if( !TARGET_OS_VXWORKS )
+		#define	TYPE_LONGLONG_NATIVE			1
+	#else
+		#define	TYPE_LONGLONG_NATIVE			0
+	#endif
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@defined	long_long_compat
+
+	@abstract	Compatibility type to map to the closest thing to long long and unsigned long long.
+	
+	@discussion
+	
+	Neither long long nor unsigned long long are supported by Microsoft compilers, but they do support proprietary
+	"__int64" and "unsigned __int64" equivalents so map to those types if the real long long is not supported.
+*/
+
+#if( TARGET_LANGUAGE_C_LIKE )
+	#if( TARGET_OS_WIN32 )
+		typedef __int64					long_long_compat;
+		typedef unsigned __int64		unsigned_long_long_compat;
+	#else
+		typedef signed long long		long_long_compat;
+		typedef unsigned long long		unsigned_long_long_compat;
+	#endif
+#endif
+
+#if 0
+#pragma mark == Errors ==
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@enum		OSStatus
+
+	@abstract	Status Code
+	
+	@constant	kNoErr						    0 No error occurred.
+	@constant	kInProgressErr				    1 Operation in progress.
+	@constant	kUnknownErr					-6700 Unknown error occurred.
+	@constant	kOptionErr					-6701 Option was not acceptable.
+	@constant	kSelectorErr				-6702 Selector passed in is invalid or unknown.
+	@constant	kExecutionStateErr			-6703 Call made in the wrong execution state (e.g. called at interrupt time).
+	@constant	kPathErr					-6704 Path is invalid, too long, or otherwise not usable.
+	@constant	kParamErr					-6705 Parameter is incorrect, missing, or not appropriate.
+	@constant	kParamCountErr				-6706 Incorrect or unsupported number of parameters.
+	@constant	kCommandErr					-6707 Command invalid or not supported.
+	@constant	kIDErr						-6708 Unknown, invalid, or inappropriate identifier.
+	@constant	kStateErr					-6709 Not in appropriate state to perform operation.
+	@constant	kRangeErr					-6710 Index is out of range or not valid.
+	@constant	kRequestErr					-6711 Request was improperly formed or not appropriate.
+	@constant	kResponseErr				-6712 Response was incorrect or out of sequence.
+	@constant	kChecksumErr				-6713 Checksum does not match the actual data.
+	@constant	kNotHandledErr				-6714 Operation was not handled (or not handled completely).
+	@constant	kVersionErr					-6715 Version is not incorrect or not compatibile.
+	@constant	kSignatureErr				-6716 Signature did not match what was expected.
+	@constant	kFormatErr					-6717 Unknown, invalid, or inappropriate file/data format.
+	@constant	kNotInitializedErr			-6718 Action request before needed services were initialized.
+	@constant	kAlreadyInitializedErr		-6719 Attempt made to initialize when already initialized.
+	@constant	kNotInUseErr				-6720 Object not in use (e.g. cannot abort if not already in use).
+	@constant	kInUseErr					-6721 Object is in use (e.g. cannot reuse active param blocks).
+	@constant	kTimeoutErr					-6722 Timeout occurred.
+	@constant	kCanceledErr				-6723 Operation canceled (successful cancel).
+	@constant	kAlreadyCanceledErr			-6724 Operation has already been canceled.
+	@constant	kCannotCancelErr			-6725 Operation could not be canceled (maybe already done or invalid).
+	@constant	kDeletedErr					-6726 Object has already been deleted.
+	@constant	kNotFoundErr				-6727 Something was not found.
+	@constant	kNoMemoryErr				-6728 Not enough memory was available to perform the operation.
+	@constant	kNoResourcesErr				-6729 Resources unavailable to perform the operation.
+	@constant	kDuplicateErr				-6730 Duplicate found or something is a duplicate.
+	@constant	kImmutableErr				-6731 Entity is not changeable.
+	@constant	kUnsupportedDataErr			-6732 Data is unknown or not supported.
+	@constant	kIntegrityErr				-6733 Data is corrupt.
+	@constant	kIncompatibleErr			-6734 Data is not compatible or it is in an incompatible format.
+	@constant	kUnsupportedErr				-6735 Feature or option is not supported.
+	@constant	kUnexpectedErr				-6736 Error occurred that was not expected.
+	@constant	kValueErr					-6737 Value is not appropriate.
+	@constant	kNotReadableErr				-6738 Could not read or reading is not allowed.
+	@constant	kNotWritableErr				-6739 Could not write or writing is not allowed.
+	@constant	kBadReferenceErr			-6740 An invalid or inappropriate reference was specified.
+	@constant	kFlagErr					-6741 An invalid, inappropriate, or unsupported flag was specified.
+	@constant	kMalformedErr				-6742 Something was not formed correctly.
+	@constant	kSizeErr					-6743 Size was too big, too small, or not appropriate.
+	@constant	kNameErr					-6744 Name was not correct, allowed, or appropriate.
+	@constant	kNotReadyErr				-6745 Device or service is not ready.
+	@constant	kReadErr					-6746 Could not read.
+	@constant	kWriteErr					-6747 Could not write.
+	@constant	kMismatchErr				-6748 Something does not match.
+	@constant	kDateErr					-6749 Date is invalid or out-of-range.
+	@constant	kUnderrunErr				-6750 Less data than expected.
+	@constant	kOverrunErr					-6751 More data than expected.
+	@constant	kEndingErr					-6752 Connection, session, or something is ending.
+	@constant	kConnectionErr				-6753 Connection failed or could not be established.
+	@constant	kAuthenticationErr			-6754 Authentication failed or is not supported.
+	@constant	kOpenErr					-6755 Could not open file, pipe, device, etc.
+	@constant	kTypeErr					-6756 Incorrect or incompatible type (e.g. file, data, etc.).
+	@constant	kSkipErr					-6757 Items should be or was skipped.
+	@constant	kNoAckErr					-6758 No acknowledge.
+	@constant	kCollisionErr				-6759 Collision occurred (e.g. two on bus at same time).
+	@constant	kBackoffErr					-6760 Backoff in progress and operation intentionally failed.
+	@constant	kNoAddressAckErr			-6761 No acknowledge of address.
+	@constant	kBusyErr					-6762 Cannot perform because something is busy.
+	@constant	kNoSpaceErr					-6763 Not enough space to perform operation.
+*/
+
+#if( TARGET_LANGUAGE_C_LIKE )
+	#if( !TARGET_OS_MAC && !TARGET_API_MAC_OSX_KERNEL )
+		typedef int32_t		OSStatus;
+	#endif
+#endif
+
+#define kNoErr						0
+#define kInProgressErr				1
+
+// Generic error codes are in the range -6700 to -6779.
+
+#define kGenericErrorBase			-6700	// Starting error code for all generic errors.
+	
+#define kUnknownErr					-6700
+#define kOptionErr					-6701
+#define kSelectorErr				-6702
+#define kExecutionStateErr			-6703
+#define kPathErr					-6704
+#define kParamErr					-6705
+#define kParamCountErr				-6706
+#define kCommandErr					-6707
+#define kIDErr						-6708
+#define kStateErr					-6709
+#define kRangeErr					-6710
+#define kRequestErr					-6711
+#define kResponseErr				-6712
+#define kChecksumErr				-6713
+#define kNotHandledErr				-6714
+#define kVersionErr					-6715
+#define kSignatureErr				-6716
+#define kFormatErr					-6717
+#define kNotInitializedErr			-6718
+#define kAlreadyInitializedErr		-6719
+#define kNotInUseErr				-6720
+#define kInUseErr					-6721
+#define kTimeoutErr					-6722
+#define kCanceledErr				-6723
+#define kAlreadyCanceledErr			-6724
+#define kCannotCancelErr			-6725
+#define kDeletedErr					-6726
+#define kNotFoundErr				-6727
+#define kNoMemoryErr				-6728
+#define kNoResourcesErr				-6729
+#define kDuplicateErr				-6730
+#define kImmutableErr				-6731
+#define kUnsupportedDataErr			-6732
+#define kIntegrityErr				-6733
+#define kIncompatibleErr			-6734
+#define kUnsupportedErr				-6735
+#define kUnexpectedErr				-6736
+#define kValueErr					-6737
+#define kNotReadableErr				-6738
+#define kNotWritableErr				-6739
+#define	kBadReferenceErr			-6740
+#define	kFlagErr					-6741
+#define	kMalformedErr				-6742
+#define	kSizeErr					-6743
+#define	kNameErr					-6744
+#define	kNotReadyErr				-6745
+#define	kReadErr					-6746
+#define	kWriteErr					-6747
+#define	kMismatchErr				-6748
+#define	kDateErr					-6749
+#define	kUnderrunErr				-6750
+#define	kOverrunErr					-6751
+#define	kEndingErr					-6752
+#define	kConnectionErr				-6753
+#define	kAuthenticationErr			-6754
+#define	kOpenErr					-6755
+#define	kTypeErr					-6756
+#define	kSkipErr					-6757
+#define	kNoAckErr					-6758
+#define	kCollisionErr				-6759
+#define	kBackoffErr					-6760
+#define	kNoAddressAckErr			-6761
+#define	kBusyErr					-6762
+#define	kNoSpaceErr					-6763
+
+#define kGenericErrorEnd			-6779	// Last generic error code (inclusive)
+
+#if 0
+#pragma mark == Mac Compatibility ==
+#endif
+
+//===========================================================================================================================
+//	Mac Compatibility
+//===========================================================================================================================
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@enum		Duration
+	
+	@abstract	Type used to specify a duration of time.
+	
+	@constant	kDurationImmediate			Indicates no delay/wait time.
+	@constant	kDurationMicrosecond		Microsecond units.
+	@constant	kDurationMillisecond		Millisecond units.
+	@constant	kDurationSecond				Second units.
+	@constant	kDurationMinute				Minute units.
+	@constant	kDurationHour				Hour units.
+	@constant	kDurationDay				Day units.
+	@constant	kDurationForever			Infinite period of time (no timeout).
+
+	@discussion 
+	
+	Duration values are intended to be multiplied by the specific interval to achieve an actual duration. For example, 
+	to wait for 5 seconds you would use "5 * kDurationSecond".
+*/
+
+#if( TARGET_LANGUAGE_C_LIKE )
+	#if( !TARGET_OS_MAC )
+		typedef	int32_t		Duration;
+	#endif
+#endif
+
+#define	kDurationImmediate				0L
+#define	kDurationMicrosecond			-1L
+#define	kDurationMillisecond			1L
+#define	kDurationSecond					( 1000L * kDurationMillisecond )
+#define	kDurationMinute					( 60L * kDurationSecond )
+#define	kDurationHour					( 60L * kDurationMinute )
+#define	kDurationDay					( 24L * kDurationHour )
+#define	kDurationForever				0x7FFFFFFFL
+
+// Seconds <-> Minutes <-> Hours <-> Days <-> Weeks <-> Months <-> Years conversions
+
+#define kNanosecondsPerMicrosecond		1000
+#define kNanosecondsPerMillisecond		1000000
+#define kNanosecondsPerSecond			1000000000
+#define kMicrosecondsPerSecond			1000000
+#define kMicrosecondsPerMillisecond		1000
+#define kMillisecondsPerSecond			1000
+#define kSecondsPerMinute				60
+#define kSecondsPerHour					( 60 * 60 )				// 3600
+#define kSecondsPerDay					( 60 * 60 * 24 )		// 86400
+#define kSecondsPerWeek					( 60 * 60 * 24 * 7 )	// 604800
+#define kMinutesPerHour					60
+#define kMinutesPerDay					( 60 * 24 )				// 1440
+#define kHoursPerDay					24
+#define kDaysPerWeek					7
+#define kWeeksPerYear					52
+#define kMonthsPerYear					12
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@defined	VersionStages
+
+	@abstract	NumVersion-style version stages.
+*/
+
+#define	kVersionStageDevelopment		0x20
+#define	kVersionStageAlpha				0x40
+#define	kVersionStageBeta				0x60
+#define	kVersionStageFinal				0x80
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@function	NumVersionBuild
+
+	@abstract	Builds a 32-bit Mac-style NumVersion value (e.g. NumVersionBuild( 1, 2, 3, kVersionStageBeta, 4 ) -> 1.2.3b4).
+*/
+
+#define	NumVersionBuild( MAJOR, MINOR, BUGFIX, STAGE, REV )	\
+	( ( ( ( MAJOR )  & 0xFF ) << 24 ) |						\
+	  ( ( ( MINOR )  & 0x0F ) << 20 ) |						\
+	  ( ( ( BUGFIX ) & 0x0F ) << 16 ) |						\
+	  ( ( ( STAGE )  & 0xFF ) <<  8 ) |						\
+	  ( ( ( REV )    & 0xFF )       ) )
+
+#define	NumVersionExtractMajor( VERSION )				( (uint8_t)( ( ( VERSION ) >> 24 ) & 0xFF ) )
+#define	NumVersionExtractMinorAndBugFix( VERSION )		( (uint8_t)( ( ( VERSION ) >> 16 ) & 0xFF ) )
+#define	NumVersionExtractMinor( VERSION )				( (uint8_t)( ( ( VERSION ) >> 20 ) & 0x0F ) )
+#define	NumVersionExtractBugFix( VERSION )				( (uint8_t)( ( ( VERSION ) >> 16 ) & 0x0F ) )
+#define	NumVersionExtractStage( VERSION )				( (uint8_t)( ( ( VERSION ) >>  8 ) & 0xFF ) )
+#define	NumVersionExtractRevision( VERSION )			( (uint8_t)(   ( VERSION )         & 0xFF ) )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@function	NumVersionCompare
+
+	@abstract	Compares two NumVersion values and returns the following values:
+	
+		left < right -> -1
+		left > right ->  1
+		left = right ->  0
+*/
+
+#if( TARGET_LANGUAGE_C_LIKE )
+	int	NumVersionCompare( uint32_t inLeft, uint32_t inRight );
+#endif
+
+#if 0
+#pragma mark == Binary Constants ==
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@defined	binary_4
+	
+	@abstract	Macro to generate an 4-bit constant using binary notation (e.g. binary_4( 1010 ) == 0xA).
+*/
+
+#define	binary_4( a )						binary_4_hex_wrap( hex_digit4( a ) )
+#define binary_4_hex_wrap( a )				binary_4_hex( a )
+#define binary_4_hex( a )					( 0x ## a )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@defined	binary_8
+	
+	@abstract	Macro to generate an 8-bit constant using binary notation (e.g. binary_8( 01111011 ) == 0x7B).
+*/
+
+#define	binary_8( a )						binary_8_hex_wrap( hex_digit8( a ) )
+#define binary_8_hex_wrap( a )				binary_8_hex( a )
+#define binary_8_hex( a )					( 0x ## a )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@defined	binary_16
+	
+	@abstract	Macro to generate an 16-bit constant using binary notation (e.g. binary_16( 01111011, 01111011 ) == 0x7B7B).
+*/
+
+#define	binary_16( a, b )					binary_16_hex_wrap( hex_digit8( a ), hex_digit8( b ) )
+#define binary_16_hex_wrap( a, b )			binary_16_hex( a, b )
+#define binary_16_hex( a, b )				( 0x ## a ## b )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@defined	binary_32
+	
+	@abstract	Macro to generate an 32-bit constant using binary notation 
+				(e.g. binary_32( 01111011, 01111011, 01111011, 01111011 ) == 0x7B7B7B7B).
+*/
+
+#define	binary_32( a, b, c, d )				binary_32_hex_wrap( hex_digit8( a ), hex_digit8( b ), hex_digit8( c ), hex_digit8( d ) )
+#define binary_32_hex_wrap( a, b, c, d )	binary_32_hex( a, b, c, d )
+#define binary_32_hex( a, b, c, d )			( 0x ## a ## b ## c ## d )
+
+// Binary Constant Helpers
+
+#define hex_digit8( a )						HEX_DIGIT_ ## a
+#define hex_digit4( a )						HEX_DIGIT_ ## 0000 ## a
+
+#define HEX_DIGIT_00000000					00
+#define HEX_DIGIT_00000001					01
+#define HEX_DIGIT_00000010					02
+#define HEX_DIGIT_00000011					03
+#define HEX_DIGIT_00000100					04
+#define HEX_DIGIT_00000101					05
+#define HEX_DIGIT_00000110					06
+#define HEX_DIGIT_00000111					07
+#define HEX_DIGIT_00001000					08
+#define HEX_DIGIT_00001001					09
+#define HEX_DIGIT_00001010					0A
+#define HEX_DIGIT_00001011					0B
+#define HEX_DIGIT_00001100					0C
+#define HEX_DIGIT_00001101					0D
+#define HEX_DIGIT_00001110					0E
+#define HEX_DIGIT_00001111					0F
+#define HEX_DIGIT_00010000					10
+#define HEX_DIGIT_00010001					11
+#define HEX_DIGIT_00010010					12
+#define HEX_DIGIT_00010011					13
+#define HEX_DIGIT_00010100					14
+#define HEX_DIGIT_00010101					15
+#define HEX_DIGIT_00010110					16
+#define HEX_DIGIT_00010111					17
+#define HEX_DIGIT_00011000					18
+#define HEX_DIGIT_00011001					19
+#define HEX_DIGIT_00011010					1A
+#define HEX_DIGIT_00011011					1B
+#define HEX_DIGIT_00011100					1C
+#define HEX_DIGIT_00011101					1D
+#define HEX_DIGIT_00011110					1E
+#define HEX_DIGIT_00011111					1F
+#define HEX_DIGIT_00100000					20
+#define HEX_DIGIT_00100001					21
+#define HEX_DIGIT_00100010					22
+#define HEX_DIGIT_00100011					23
+#define HEX_DIGIT_00100100					24
+#define HEX_DIGIT_00100101					25
+#define HEX_DIGIT_00100110					26
+#define HEX_DIGIT_00100111					27
+#define HEX_DIGIT_00101000					28
+#define HEX_DIGIT_00101001					29
+#define HEX_DIGIT_00101010					2A
+#define HEX_DIGIT_00101011					2B
+#define HEX_DIGIT_00101100					2C
+#define HEX_DIGIT_00101101					2D
+#define HEX_DIGIT_00101110					2E
+#define HEX_DIGIT_00101111					2F
+#define HEX_DIGIT_00110000					30
+#define HEX_DIGIT_00110001					31
+#define HEX_DIGIT_00110010					32
+#define HEX_DIGIT_00110011					33
+#define HEX_DIGIT_00110100					34
+#define HEX_DIGIT_00110101					35
+#define HEX_DIGIT_00110110					36
+#define HEX_DIGIT_00110111					37
+#define HEX_DIGIT_00111000					38
+#define HEX_DIGIT_00111001					39
+#define HEX_DIGIT_00111010					3A
+#define HEX_DIGIT_00111011					3B
+#define HEX_DIGIT_00111100					3C
+#define HEX_DIGIT_00111101					3D
+#define HEX_DIGIT_00111110					3E
+#define HEX_DIGIT_00111111					3F
+#define HEX_DIGIT_01000000					40
+#define HEX_DIGIT_01000001					41
+#define HEX_DIGIT_01000010					42
+#define HEX_DIGIT_01000011					43
+#define HEX_DIGIT_01000100					44
+#define HEX_DIGIT_01000101					45
+#define HEX_DIGIT_01000110					46
+#define HEX_DIGIT_01000111					47
+#define HEX_DIGIT_01001000					48
+#define HEX_DIGIT_01001001					49
+#define HEX_DIGIT_01001010					4A
+#define HEX_DIGIT_01001011					4B
+#define HEX_DIGIT_01001100					4C
+#define HEX_DIGIT_01001101					4D
+#define HEX_DIGIT_01001110					4E
+#define HEX_DIGIT_01001111					4F
+#define HEX_DIGIT_01010000					50
+#define HEX_DIGIT_01010001					51
+#define HEX_DIGIT_01010010					52
+#define HEX_DIGIT_01010011					53
+#define HEX_DIGIT_01010100					54
+#define HEX_DIGIT_01010101					55
+#define HEX_DIGIT_01010110					56
+#define HEX_DIGIT_01010111					57
+#define HEX_DIGIT_01011000					58
+#define HEX_DIGIT_01011001					59
+#define HEX_DIGIT_01011010					5A
+#define HEX_DIGIT_01011011					5B
+#define HEX_DIGIT_01011100					5C
+#define HEX_DIGIT_01011101					5D
+#define HEX_DIGIT_01011110					5E
+#define HEX_DIGIT_01011111					5F
+#define HEX_DIGIT_01100000					60
+#define HEX_DIGIT_01100001					61
+#define HEX_DIGIT_01100010					62
+#define HEX_DIGIT_01100011					63
+#define HEX_DIGIT_01100100					64
+#define HEX_DIGIT_01100101					65
+#define HEX_DIGIT_01100110					66
+#define HEX_DIGIT_01100111					67
+#define HEX_DIGIT_01101000					68
+#define HEX_DIGIT_01101001					69
+#define HEX_DIGIT_01101010					6A
+#define HEX_DIGIT_01101011					6B
+#define HEX_DIGIT_01101100					6C
+#define HEX_DIGIT_01101101					6D
+#define HEX_DIGIT_01101110					6E
+#define HEX_DIGIT_01101111					6F
+#define HEX_DIGIT_01110000					70
+#define HEX_DIGIT_01110001					71
+#define HEX_DIGIT_01110010					72
+#define HEX_DIGIT_01110011					73
+#define HEX_DIGIT_01110100					74
+#define HEX_DIGIT_01110101					75
+#define HEX_DIGIT_01110110					76
+#define HEX_DIGIT_01110111					77
+#define HEX_DIGIT_01111000					78
+#define HEX_DIGIT_01111001					79
+#define HEX_DIGIT_01111010					7A
+#define HEX_DIGIT_01111011					7B
+#define HEX_DIGIT_01111100					7C
+#define HEX_DIGIT_01111101					7D
+#define HEX_DIGIT_01111110					7E
+#define HEX_DIGIT_01111111					7F
+#define HEX_DIGIT_10000000					80
+#define HEX_DIGIT_10000001					81
+#define HEX_DIGIT_10000010					82
+#define HEX_DIGIT_10000011					83
+#define HEX_DIGIT_10000100					84
+#define HEX_DIGIT_10000101					85
+#define HEX_DIGIT_10000110					86
+#define HEX_DIGIT_10000111					87
+#define HEX_DIGIT_10001000					88
+#define HEX_DIGIT_10001001					89
+#define HEX_DIGIT_10001010					8A
+#define HEX_DIGIT_10001011					8B
+#define HEX_DIGIT_10001100					8C
+#define HEX_DIGIT_10001101					8D
+#define HEX_DIGIT_10001110					8E
+#define HEX_DIGIT_10001111					8F
+#define HEX_DIGIT_10010000					90
+#define HEX_DIGIT_10010001					91
+#define HEX_DIGIT_10010010					92
+#define HEX_DIGIT_10010011					93
+#define HEX_DIGIT_10010100					94
+#define HEX_DIGIT_10010101					95
+#define HEX_DIGIT_10010110					96
+#define HEX_DIGIT_10010111					97
+#define HEX_DIGIT_10011000					98
+#define HEX_DIGIT_10011001					99
+#define HEX_DIGIT_10011010					9A
+#define HEX_DIGIT_10011011					9B
+#define HEX_DIGIT_10011100					9C
+#define HEX_DIGIT_10011101					9D
+#define HEX_DIGIT_10011110					9E
+#define HEX_DIGIT_10011111					9F
+#define HEX_DIGIT_10100000					A0
+#define HEX_DIGIT_10100001					A1
+#define HEX_DIGIT_10100010					A2
+#define HEX_DIGIT_10100011					A3
+#define HEX_DIGIT_10100100					A4
+#define HEX_DIGIT_10100101					A5
+#define HEX_DIGIT_10100110					A6
+#define HEX_DIGIT_10100111					A7
+#define HEX_DIGIT_10101000					A8
+#define HEX_DIGIT_10101001					A9
+#define HEX_DIGIT_10101010					AA
+#define HEX_DIGIT_10101011					AB
+#define HEX_DIGIT_10101100					AC
+#define HEX_DIGIT_10101101					AD
+#define HEX_DIGIT_10101110					AE
+#define HEX_DIGIT_10101111					AF
+#define HEX_DIGIT_10110000					B0
+#define HEX_DIGIT_10110001					B1
+#define HEX_DIGIT_10110010					B2
+#define HEX_DIGIT_10110011					B3
+#define HEX_DIGIT_10110100					B4
+#define HEX_DIGIT_10110101					B5
+#define HEX_DIGIT_10110110					B6
+#define HEX_DIGIT_10110111					B7
+#define HEX_DIGIT_10111000					B8
+#define HEX_DIGIT_10111001					B9
+#define HEX_DIGIT_10111010					BA
+#define HEX_DIGIT_10111011					BB
+#define HEX_DIGIT_10111100					BC
+#define HEX_DIGIT_10111101					BD
+#define HEX_DIGIT_10111110					BE
+#define HEX_DIGIT_10111111					BF
+#define HEX_DIGIT_11000000					C0
+#define HEX_DIGIT_11000001					C1
+#define HEX_DIGIT_11000010					C2
+#define HEX_DIGIT_11000011					C3
+#define HEX_DIGIT_11000100					C4
+#define HEX_DIGIT_11000101					C5
+#define HEX_DIGIT_11000110					C6
+#define HEX_DIGIT_11000111					C7
+#define HEX_DIGIT_11001000					C8
+#define HEX_DIGIT_11001001					C9
+#define HEX_DIGIT_11001010					CA
+#define HEX_DIGIT_11001011					CB
+#define HEX_DIGIT_11001100					CC
+#define HEX_DIGIT_11001101					CD
+#define HEX_DIGIT_11001110					CE
+#define HEX_DIGIT_11001111					CF
+#define HEX_DIGIT_11010000					D0
+#define HEX_DIGIT_11010001					D1
+#define HEX_DIGIT_11010010					D2
+#define HEX_DIGIT_11010011					D3
+#define HEX_DIGIT_11010100					D4
+#define HEX_DIGIT_11010101					D5
+#define HEX_DIGIT_11010110					D6
+#define HEX_DIGIT_11010111					D7
+#define HEX_DIGIT_11011000					D8
+#define HEX_DIGIT_11011001					D9
+#define HEX_DIGIT_11011010					DA
+#define HEX_DIGIT_11011011					DB
+#define HEX_DIGIT_11011100					DC
+#define HEX_DIGIT_11011101					DD
+#define HEX_DIGIT_11011110					DE
+#define HEX_DIGIT_11011111					DF
+#define HEX_DIGIT_11100000					E0
+#define HEX_DIGIT_11100001					E1
+#define HEX_DIGIT_11100010					E2
+#define HEX_DIGIT_11100011					E3
+#define HEX_DIGIT_11100100					E4
+#define HEX_DIGIT_11100101					E5
+#define HEX_DIGIT_11100110					E6
+#define HEX_DIGIT_11100111					E7
+#define HEX_DIGIT_11101000					E8
+#define HEX_DIGIT_11101001					E9
+#define HEX_DIGIT_11101010					EA
+#define HEX_DIGIT_11101011					EB
+#define HEX_DIGIT_11101100					EC
+#define HEX_DIGIT_11101101					ED
+#define HEX_DIGIT_11101110					EE
+#define HEX_DIGIT_11101111					EF
+#define HEX_DIGIT_11110000					F0
+#define HEX_DIGIT_11110001					F1
+#define HEX_DIGIT_11110010					F2
+#define HEX_DIGIT_11110011					F3
+#define HEX_DIGIT_11110100					F4
+#define HEX_DIGIT_11110101					F5
+#define HEX_DIGIT_11110110					F6
+#define HEX_DIGIT_11110111					F7
+#define HEX_DIGIT_11111000					F8
+#define HEX_DIGIT_11111001					F9
+#define HEX_DIGIT_11111010					FA
+#define HEX_DIGIT_11111011					FB
+#define HEX_DIGIT_11111100					FC
+#define HEX_DIGIT_11111101					FD
+#define HEX_DIGIT_11111110					FE
+#define HEX_DIGIT_11111111					FF
+
+#if 0
+#pragma mark == Debugging ==
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@function	CommonServicesTest
+
+	@abstract	Unit test.
+*/
+
+#if( DEBUG )
+	#if( TARGET_LANGUAGE_C_LIKE )
+		OSStatus	CommonServicesTest( void );
+	#endif
+#endif
+
+#ifdef	__cplusplus
+	}
+#endif
+
+#endif	// __COMMON_SERVICES__
diff --git a/mdnsresponder/mDNSShared/DebugServices.c b/mdnsresponder/mDNSShared/DebugServices.c
new file mode 100644
index 0000000..6473296
--- /dev/null
+++ b/mdnsresponder/mDNSShared/DebugServices.c
@@ -0,0 +1,3075 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+
+	To Do:
+	
+	- Use StackWalk on Windows to optionally print stack frames.
+*/
+
+#if 0
+#pragma mark == Includes ==
+#endif
+
+//===========================================================================================================================
+//	Includes
+//===========================================================================================================================
+
+#if( !KERNEL )
+	#include	<ctype.h>
+	#include	<stdio.h>
+	#include	<string.h>
+#endif
+
+#include	"CommonServices.h"
+
+#include	"DebugServices.h"
+
+#if( DEBUG )
+
+#if( TARGET_OS_VXWORKS )
+	#include	"intLib.h"
+#endif
+
+#if( TARGET_OS_WIN32 )
+	#include	<time.h>
+	
+	#if( !TARGET_OS_WINDOWS_CE )
+		#include	<fcntl.h>
+		#include	<io.h>
+	#endif
+#endif
+
+#if( DEBUG_IDEBUG_ENABLED && TARGET_API_MAC_OSX_KERNEL )
+	#include	<IOKit/IOLib.h>
+#endif
+
+// If MDNS_DEBUGMSGS is defined (even if defined 0), it is aware of mDNS and it is probably safe to include mDNSEmbeddedAPI.h.
+
+#if( defined( MDNS_DEBUGMSGS ) )
+	#include	"mDNSEmbeddedAPI.h"
+#endif
+
+#if 0
+#pragma mark == Macros ==
+#endif
+
+//===========================================================================================================================
+//	Macros
+//===========================================================================================================================
+
+#define DebugIsPrint( C )		( ( ( C ) >= 0x20 ) && ( ( C ) <= 0x7E ) )
+
+#if 0
+#pragma mark == Prototypes ==
+#endif
+
+//===========================================================================================================================
+//	Prototypes
+//===========================================================================================================================
+
+static OSStatus	DebugPrint( DebugLevel inLevel, char *inData, size_t inSize );
+
+// fprintf
+
+#if( DEBUG_FPRINTF_ENABLED )
+	static OSStatus	DebugFPrintFInit( DebugOutputTypeFlags inFlags, const char *inFilename );
+	static void		DebugFPrintFPrint( char *inData, size_t inSize );
+#endif
+
+// iDebug (Mac OS X user and kernel)
+
+#if( DEBUG_IDEBUG_ENABLED )
+	static OSStatus	DebugiDebugInit( void );
+	static void		DebugiDebugPrint( char *inData, size_t inSize );
+#endif
+
+// kprintf (Mac OS X Kernel)
+
+#if( DEBUG_KPRINTF_ENABLED )
+	static void	DebugKPrintFPrint( char *inData, size_t inSize );
+#endif
+
+// Mac OS X IOLog (Mac OS X Kernel)
+
+#if( DEBUG_MAC_OS_X_IOLOG_ENABLED )
+	static void	DebugMacOSXIOLogPrint( char *inData, size_t inSize );
+#endif
+
+// Mac OS X Log
+
+#if( TARGET_OS_MAC )
+	static OSStatus	DebugMacOSXLogInit( void );
+	static void		DebugMacOSXLogPrint( char *inData, size_t inSize );
+#endif
+
+// Windows Debugger
+
+#if( TARGET_OS_WIN32 )
+	static void	DebugWindowsDebuggerPrint( char *inData, size_t inSize );
+#endif
+
+// Windows Event Log
+
+#if( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE )
+	static OSStatus	DebugWindowsEventLogInit( const char *inName, HMODULE inModule );
+	static void	DebugWindowsEventLogPrint( DebugLevel inLevel, char *inData, size_t inSize );
+#endif
+
+// DebugLib support
+
+#if( DEBUG_CORE_SERVICE_ASSERTS_ENABLED )
+	static pascal void	
+		DebugAssertOutputHandler( 
+			OSType 				inComponentSignature, 
+			UInt32 				inOptions, 
+			const char *		inAssertionString, 
+			const char *		inExceptionString, 
+			const char *		inErrorString, 
+			const char *		inFileName, 
+			long 				inLineNumber, 
+			void *				inValue, 
+			ConstStr255Param 	inOutputMsg );
+#endif
+
+// Utilities
+
+static char *	DebugNumVersionToString( uint32_t inVersion, char *inString );
+
+#if( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE )
+	static void	DebugWinEnableConsole( void );
+#endif
+
+#if( TARGET_OS_WIN32 )
+	static TCHAR *
+		DebugWinCharToTCharString( 
+			const char *	inCharString, 
+			size_t 			inCharCount, 
+			TCHAR *			outTCharString, 
+			size_t 			inTCharCountMax, 
+			size_t *		outTCharCount );
+#endif
+
+#if 0
+#pragma mark == Globals ==
+#endif
+
+//===========================================================================================================================
+//	Private Globals
+//===========================================================================================================================
+
+#if( TARGET_OS_VXWORKS )
+	// TCP States for inetstatShow.
+
+	extern char **	pTcpstates;		// defined in tcpLib.c
+
+	const char *		kDebugTCPStates[] =
+	{
+		"(0)  TCPS_CLOSED", 
+		"(1)  TCPS_LISTEN", 
+		"(2)  TCPS_SYN_SENT", 
+		"(3)  TCPS_SYN_RECEIVED", 
+		"(4)  TCPS_ESTABLISHED", 
+		"(5)  TCPS_CLOSE_WAIT", 
+		"(6)  TCPS_FIN_WAIT_1", 
+		"(7)  TCPS_CLOSING", 
+		"(8)  TCPS_LAST_ACK", 
+		"(9)  TCPS_FIN_WAIT_2", 
+		"(10) TCPS_TIME_WAIT",
+	};
+#endif
+
+// General
+
+static bool									gDebugInitialized				= false;
+static DebugOutputType						gDebugOutputType 				= kDebugOutputTypeNone;
+static DebugLevel							gDebugPrintLevelMin				= kDebugLevelInfo;
+static DebugLevel							gDebugPrintLevelMax				= kDebugLevelMax;
+static DebugLevel							gDebugBreakLevel				= kDebugLevelAssert;
+#if( DEBUG_CORE_SERVICE_ASSERTS_ENABLED )
+	static DebugAssertOutputHandlerUPP		gDebugAssertOutputHandlerUPP	= NULL;
+#endif
+
+// Custom
+
+static DebugOutputFunctionPtr				gDebugCustomOutputFunction 		= NULL;
+static void *								gDebugCustomOutputContext 		= NULL;
+
+// fprintf
+
+#if( DEBUG_FPRINTF_ENABLED )
+	static FILE *							gDebugFPrintFFile 				= NULL;
+#endif
+
+// MacOSXLog
+
+#if( TARGET_OS_MAC )
+	typedef int	( *DebugMacOSXLogFunctionPtr )( const char *inFormat, ... );
+	
+	static DebugMacOSXLogFunctionPtr		gDebugMacOSXLogFunction			= NULL;
+#endif
+
+// WindowsEventLog
+
+
+#if( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE )
+	static HANDLE							gDebugWindowsEventLogEventSource = NULL;
+#endif
+
+#if 0
+#pragma mark -
+#pragma mark == General ==
+#endif
+
+//===========================================================================================================================
+//	DebugInitialize
+//===========================================================================================================================
+
+DEBUG_EXPORT OSStatus	DebugInitialize( DebugOutputType inType, ... )
+{
+	OSStatus			err;
+	DebugOutputType		type;
+	va_list				args;
+	
+	va_start( args, inType );
+
+#if( TARGET_OS_VXWORKS )
+	// Set up the TCP state strings if they are not already set up by VxWorks (normally not set up for some reason).
+	
+	if( !pTcpstates )
+	{
+		pTcpstates = (char **) kDebugTCPStates;
+	}
+#endif
+	
+	// Set up DebugLib stuff (if building with Debugging.h).
+	
+#if( DEBUG_CORE_SERVICE_ASSERTS_ENABLED )
+	if( !gDebugAssertOutputHandlerUPP )
+	{
+		gDebugAssertOutputHandlerUPP = NewDebugAssertOutputHandlerUPP( DebugAssertOutputHandler );
+		check( gDebugAssertOutputHandlerUPP );
+		if( gDebugAssertOutputHandlerUPP )
+		{
+			InstallDebugAssertOutputHandler( gDebugAssertOutputHandlerUPP );
+		}
+	}
+#endif
+	
+	// Pre-process meta-output kind to pick an appropriate output kind for the platform.
+	
+	type = inType;
+	if( type == kDebugOutputTypeMetaConsole )
+	{
+		#if( TARGET_OS_MAC )
+			type = kDebugOutputTypeMacOSXLog;
+		#elif( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE )
+			#if( DEBUG_FPRINTF_ENABLED )
+				type = kDebugOutputTypeFPrintF;
+			#else
+				type = kDebugOutputTypeWindowsDebugger;
+			#endif
+		#elif( TARGET_API_MAC_OSX_KERNEL )
+			#if( DEBUG_MAC_OS_X_IOLOG_ENABLED )
+				type = kDebugOutputTypeMacOSXIOLog;
+			#elif( DEBUG_IDEBUG_ENABLED )
+				type = kDebugOutputTypeiDebug;
+			#elif( DEBUG_KPRINTF_ENABLED )
+				type = kDebugOutputTypeKPrintF;
+			#endif
+		#elif( TARGET_OS_VXWORKS )
+			#if( DEBUG_FPRINTF_ENABLED )
+				type = kDebugOutputTypeFPrintF;
+			#else
+				#error target is VxWorks, but fprintf output is disabled
+			#endif
+		#else
+			#if( DEBUG_FPRINTF_ENABLED )
+				type = kDebugOutputTypeFPrintF;
+			#endif
+		#endif
+	}
+	
+	// Process output kind.
+	
+	gDebugOutputType = type;
+	switch( type )
+	{
+		case kDebugOutputTypeNone:
+			err = kNoErr;
+			break;
+
+		case kDebugOutputTypeCustom:
+			gDebugCustomOutputFunction = va_arg( args, DebugOutputFunctionPtr );
+			gDebugCustomOutputContext  = va_arg( args, void * );
+			err = kNoErr;
+			break;
+
+#if( DEBUG_FPRINTF_ENABLED )
+		case kDebugOutputTypeFPrintF:
+			if( inType == kDebugOutputTypeMetaConsole )
+			{
+				err = DebugFPrintFInit( kDebugOutputTypeFlagsStdErr, NULL );
+			}
+			else
+			{
+				DebugOutputTypeFlags		flags;
+				const char *				filename;
+				
+				flags = (DebugOutputTypeFlags) va_arg( args, unsigned int );
+				if( ( flags & kDebugOutputTypeFlagsTypeMask ) == kDebugOutputTypeFlagsFile )
+				{
+					filename = va_arg( args, const char * );
+				}
+				else
+				{
+					filename = NULL;
+				}
+				err = DebugFPrintFInit( flags, filename );
+			}
+			break;
+#endif
+
+#if( DEBUG_IDEBUG_ENABLED )
+		case kDebugOutputTypeiDebug:
+			err = DebugiDebugInit();
+			break;
+#endif
+
+#if( DEBUG_KPRINTF_ENABLED )
+		case kDebugOutputTypeKPrintF:
+			err = kNoErr;
+			break;
+#endif
+
+#if( DEBUG_MAC_OS_X_IOLOG_ENABLED )
+		case kDebugOutputTypeMacOSXIOLog:
+			err = kNoErr;
+			break;
+#endif
+
+#if( TARGET_OS_MAC )
+		case kDebugOutputTypeMacOSXLog:
+			err = DebugMacOSXLogInit();
+			break;
+#endif
+
+#if( TARGET_OS_WIN32 )
+		case kDebugOutputTypeWindowsDebugger:
+			err = kNoErr;
+			break;
+#endif
+
+#if( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE )
+		case kDebugOutputTypeWindowsEventLog:
+		{
+			const char *		name;
+			HMODULE				module;
+			
+			name   = va_arg( args, const char * );
+			module = va_arg( args, HMODULE );
+			err = DebugWindowsEventLogInit( name, module );
+		}
+		break;
+#endif
+
+		default:
+			err = kParamErr;
+			goto exit;
+	}
+	gDebugInitialized = true;
+	
+exit:
+	va_end( args );
+	return( err );
+}
+
+//===========================================================================================================================
+//	DebugFinalize
+//===========================================================================================================================
+
+DEBUG_EXPORT void		DebugFinalize( void )
+{
+#if( DEBUG_CORE_SERVICE_ASSERTS_ENABLED )
+	check( gDebugAssertOutputHandlerUPP );
+	if( gDebugAssertOutputHandlerUPP )
+	{
+		InstallDebugAssertOutputHandler( NULL );
+		DisposeDebugAssertOutputHandlerUPP( gDebugAssertOutputHandlerUPP );
+		gDebugAssertOutputHandlerUPP = NULL;
+	}
+#endif
+}
+
+//===========================================================================================================================
+//	DebugGetProperty
+//===========================================================================================================================
+
+DEBUG_EXPORT OSStatus	DebugGetProperty( DebugPropertyTag inTag, ... )
+{
+	OSStatus			err;
+	va_list				args;
+	DebugLevel *		level;
+	
+	va_start( args, inTag );
+	switch( inTag )
+	{
+		case kDebugPropertyTagPrintLevelMin:
+			level  = va_arg( args, DebugLevel * );
+			*level = gDebugPrintLevelMin;
+			err = kNoErr;
+			break;
+		
+		case kDebugPropertyTagPrintLevelMax:
+			level  = va_arg( args, DebugLevel * );
+			*level = gDebugPrintLevelMax;
+			err = kNoErr;
+			break;
+		
+		case kDebugPropertyTagBreakLevel:
+			level  = va_arg( args, DebugLevel * );
+			*level = gDebugBreakLevel;
+			err = kNoErr;
+			break;		
+		
+		default:
+			err = kUnsupportedErr;
+			break;
+	}
+	va_end( args );
+	return( err );
+}
+
+//===========================================================================================================================
+//	DebugSetProperty
+//===========================================================================================================================
+
+DEBUG_EXPORT OSStatus	DebugSetProperty( DebugPropertyTag inTag, ... )
+{
+	OSStatus		err;
+	va_list			args;
+	DebugLevel		level;
+	
+	va_start( args, inTag );
+	switch( inTag )
+	{
+		case kDebugPropertyTagPrintLevelMin:
+			level  = va_arg( args, DebugLevel );
+			gDebugPrintLevelMin = level;
+			err = kNoErr;
+			break;
+		
+		case kDebugPropertyTagPrintLevelMax:
+			level  = va_arg( args, DebugLevel );
+			gDebugPrintLevelMax = level;
+			err = kNoErr;
+			break;
+		
+		case kDebugPropertyTagBreakLevel:
+			level  = va_arg( args, DebugLevel );
+			gDebugBreakLevel = level;
+			err = kNoErr;
+			break;		
+		
+		default:
+			err = kUnsupportedErr;
+			break;
+	}
+	va_end( args );
+	return( err );
+}
+
+#if 0
+#pragma mark -
+#pragma mark == Output ==
+#endif
+
+//===========================================================================================================================
+//	DebugPrintF
+//===========================================================================================================================
+
+DEBUG_EXPORT size_t	DebugPrintF( DebugLevel inLevel, const char *inFormat, ... )
+{	
+	va_list		args;
+	size_t		n;
+	
+	// Skip if the level is not in the enabled range..
+	
+	if( ( inLevel < gDebugPrintLevelMin ) || ( inLevel > gDebugPrintLevelMax ) )
+	{
+		n = 0;
+		goto exit;
+	}
+	
+	va_start( args, inFormat );
+	n = DebugPrintFVAList( inLevel, inFormat, args );
+	va_end( args );
+
+exit:
+	return( n );
+}
+
+//===========================================================================================================================
+//	DebugPrintFVAList
+//===========================================================================================================================
+
+DEBUG_EXPORT size_t	DebugPrintFVAList( DebugLevel inLevel, const char *inFormat, va_list inArgs )
+{
+	size_t		n;
+	char		buffer[ 512 ];
+	
+	// Skip if the level is not in the enabled range..
+	
+	if( ( inLevel < gDebugPrintLevelMin ) || ( inLevel > gDebugPrintLevelMax ) )
+	{
+		n = 0;
+		goto exit;
+	}
+	
+	n = DebugSNPrintFVAList( buffer, sizeof( buffer ), inFormat, inArgs );
+	DebugPrint( inLevel, buffer, (size_t) n );
+
+exit:
+	return( n );
+}
+
+//===========================================================================================================================
+//	DebugPrint
+//===========================================================================================================================
+
+static OSStatus	DebugPrint( DebugLevel inLevel, char *inData, size_t inSize )
+{
+	OSStatus		err;
+	
+	// Skip if the level is not in the enabled range..
+	
+	if( ( inLevel < gDebugPrintLevelMin ) || ( inLevel > gDebugPrintLevelMax ) )
+	{
+		err = kRangeErr;
+		goto exit;
+	}
+	
+	// Printing is not safe at interrupt time so check for this and warn with an interrupt safe mechanism (if available).
+	
+	if( DebugTaskLevel() & kDebugInterruptLevelMask )
+	{
+		#if( TARGET_OS_VXWORKS )
+			logMsg( "\ncannot print at interrupt time\n\n", 1, 2, 3, 4, 5, 6 );
+		#endif
+		
+		err = kExecutionStateErr;
+		goto exit;
+	}
+	
+	// Initialize the debugging library if it hasn't already been initialized (allows for zero-config usage).
+	
+	if( !gDebugInitialized )
+	{
+		debug_initialize( kDebugOutputTypeMetaConsole );
+	}
+	
+	// Print based on the current output type.
+	
+	switch( gDebugOutputType )
+	{
+		case kDebugOutputTypeNone:
+			break;
+		
+		case kDebugOutputTypeCustom:
+			if( gDebugCustomOutputFunction )
+			{
+				gDebugCustomOutputFunction( inData, inSize, gDebugCustomOutputContext );
+			}
+			break;
+
+#if( DEBUG_FPRINTF_ENABLED )
+		case kDebugOutputTypeFPrintF:
+			DebugFPrintFPrint( inData, inSize );
+			break;
+#endif
+
+#if( DEBUG_IDEBUG_ENABLED )
+		case kDebugOutputTypeiDebug:
+			DebugiDebugPrint( inData, inSize );
+			break;
+#endif
+
+#if( DEBUG_KPRINTF_ENABLED )
+		case kDebugOutputTypeKPrintF:
+			DebugKPrintFPrint( inData, inSize );
+			break;
+#endif
+
+#if( DEBUG_MAC_OS_X_IOLOG_ENABLED )
+		case kDebugOutputTypeMacOSXIOLog:
+			DebugMacOSXIOLogPrint( inData, inSize );
+			break;
+#endif
+
+#if( TARGET_OS_MAC )
+		case kDebugOutputTypeMacOSXLog:
+			DebugMacOSXLogPrint( inData, inSize );
+			break;
+#endif
+
+#if( TARGET_OS_WIN32 )
+		case kDebugOutputTypeWindowsDebugger:
+			DebugWindowsDebuggerPrint( inData, inSize );
+			break;
+#endif
+
+#if( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE )
+		case kDebugOutputTypeWindowsEventLog:
+			DebugWindowsEventLogPrint( inLevel, inData, inSize );
+			break;
+#endif
+
+		default:
+			break;
+	}
+	err = kNoErr;
+	
+exit:
+	return( err );
+}
+
+//===========================================================================================================================
+//	DebugPrintAssert
+//
+//	Warning: This routine relies on several of the strings being string constants that will exist forever because the
+//           underlying logMsg API that does the printing is asynchronous so it cannot use temporary/stack-based 
+//           pointer variables (e.g. local strings). The debug macros that invoke this function only use constant 
+//           constant strings, but if this function is invoked directly from other places, it must use constant strings.
+//===========================================================================================================================
+
+DEBUG_EXPORT void
+	DebugPrintAssert( 
+		int_least32_t	inErrorCode, 
+		const char *	inAssertString, 
+		const char *	inMessage, 
+		const char *	inFilename, 
+		int_least32_t	inLineNumber, 
+		const char *	inFunction )
+{
+	// Skip if the level is not in the enabled range..
+	
+	if( ( kDebugLevelAssert < gDebugPrintLevelMin ) || ( kDebugLevelAssert > gDebugPrintLevelMax ) )
+	{
+		return;
+	}
+	
+	if( inErrorCode != 0 )
+	{
+		DebugPrintF( 
+			kDebugLevelAssert, 
+			"\n"
+			"[ASSERT] error:  %ld (%m)\n"
+			"[ASSERT] where:  \"%s\", line %ld, \"%s\"\n"
+			"\n", 
+			inErrorCode, inErrorCode, 
+			inFilename ? inFilename : "", 
+			inLineNumber, 
+			inFunction ? inFunction : "" );
+	}
+	else
+	{
+		DebugPrintF( 
+			kDebugLevelAssert, 
+			"\n"
+			"[ASSERT] assert: \"%s\" %s\n"
+			"[ASSERT] where:  \"%s\", line %ld, \"%s\"\n"
+			"\n", 
+			inAssertString ? inAssertString : "", 
+			inMessage ? inMessage : "", 
+			inFilename ? inFilename : "", 
+			inLineNumber, 
+			inFunction ? inFunction : "" );
+	}
+	
+	// Break into the debugger if enabled.
+	
+	#if( TARGET_OS_WIN32 )
+		if( gDebugBreakLevel <= kDebugLevelAssert )
+		{
+			if( IsDebuggerPresent() )
+			{
+				DebugBreak();
+			}
+		}
+	#endif
+}
+
+#if 0
+#pragma mark -
+#endif
+
+#if( DEBUG_FPRINTF_ENABLED )
+//===========================================================================================================================
+//	DebugFPrintFInit
+//===========================================================================================================================
+
+static OSStatus	DebugFPrintFInit( DebugOutputTypeFlags inFlags, const char *inFilename )
+{
+	OSStatus					err;
+	DebugOutputTypeFlags		typeFlags;
+	
+	typeFlags = inFlags & kDebugOutputTypeFlagsTypeMask;
+	if( typeFlags == kDebugOutputTypeFlagsStdOut )
+	{
+		#if( TARGET_OS_WIN32 )
+			DebugWinEnableConsole();
+		#endif
+
+		gDebugFPrintFFile = stdout;
+	}
+	else if( typeFlags == kDebugOutputTypeFlagsStdErr )
+	{
+		#if( TARGET_OS_WIN32 )
+			DebugWinEnableConsole();
+		#endif
+		
+		gDebugFPrintFFile = stdout;
+	}
+	else if( typeFlags == kDebugOutputTypeFlagsFile )
+	{
+		require_action_quiet( inFilename && ( *inFilename != '\0' ), exit, err = kOpenErr );
+		
+		gDebugFPrintFFile = fopen( inFilename, "a" );
+		require_action_quiet( gDebugFPrintFFile, exit, err = kOpenErr );
+	}
+	else
+	{
+		err = kParamErr;
+		goto exit;
+	}
+	err = kNoErr;
+	
+exit:
+	return( err );
+}
+
+//===========================================================================================================================
+//	DebugFPrintFPrint
+//===========================================================================================================================
+
+static void	DebugFPrintFPrint( char *inData, size_t inSize )
+{
+	char *		p;
+	char *		q;
+	
+	// Convert \r to \n. fprintf will interpret \n and convert to whatever is appropriate for the platform.
+	
+	p = inData;
+	q = p + inSize;
+	while( p < q )
+	{
+		if( *p == '\r' )
+		{
+			*p = '\n';
+		}
+		++p;
+	}
+	
+	// Write the data and flush.
+	
+	if( gDebugFPrintFFile )
+	{
+		fprintf( gDebugFPrintFFile, "%.*s", (int) inSize, inData );
+		fflush( gDebugFPrintFFile );
+	}
+}
+#endif	// DEBUG_FPRINTF_ENABLED
+
+#if( DEBUG_IDEBUG_ENABLED )
+//===========================================================================================================================
+//	DebugiDebugInit
+//===========================================================================================================================
+
+static OSStatus	DebugiDebugInit( void )
+{
+	OSStatus		err;
+	
+	#if( TARGET_API_MAC_OSX_KERNEL )
+		
+		extern uint32_t *		_giDebugReserved1;
+		
+		// Emulate the iDebugSetOutputType macro in iDebugServices.h.
+		// Note: This is not thread safe, but neither is iDebugServices.h nor iDebugKext.
+		
+		if( !_giDebugReserved1 )
+		{
+			_giDebugReserved1 = (uint32_t *) IOMalloc( sizeof( uint32_t ) );
+			require_action_quiet( _giDebugReserved1, exit, err = kNoMemoryErr );
+		}
+		*_giDebugReserved1 = 0x00010000U;
+		err = kNoErr;
+exit:
+	#else
+		
+		__private_extern__ void	iDebugSetOutputTypeInternal( uint32_t inType );
+		
+		iDebugSetOutputTypeInternal( 0x00010000U );
+		err = kNoErr;
+		
+	#endif
+	
+	return( err );
+}
+
+//===========================================================================================================================
+//	DebugiDebugPrint
+//===========================================================================================================================
+
+static void	DebugiDebugPrint( char *inData, size_t inSize )
+{
+	#if( TARGET_API_MAC_OSX_KERNEL )
+		
+		// Locally declared here so we do not need to include iDebugKext.h.
+		// Note: IOKit uses a global namespace for all code and only a partial link occurs at build time. When the 
+		// KEXT is loaded, the runtime linker will link in this extern'd symbol (assuming iDebug is present).
+		// _giDebugLogInternal is actually part of IOKit proper so this should link even if iDebug is not present.
+		
+		typedef void ( *iDebugLogFunctionPtr )( uint32_t inLevel, uint32_t inTag, const char *inFormat, ... );
+		
+		extern iDebugLogFunctionPtr		_giDebugLogInternal;
+		
+		if( _giDebugLogInternal )
+		{
+			_giDebugLogInternal( 0, 0, "%.*s", (int) inSize, inData );
+		}
+		
+	#else
+	
+		__private_extern__ void	iDebugLogInternal( uint32_t inLevel, uint32_t inTag, const char *inFormat, ... );
+		
+		iDebugLogInternal( 0, 0, "%.*s", (int) inSize, inData );
+	
+	#endif
+}
+#endif
+
+#if( DEBUG_KPRINTF_ENABLED )
+//===========================================================================================================================
+//	DebugKPrintFPrint
+//===========================================================================================================================
+
+static void	DebugKPrintFPrint( char *inData, size_t inSize )
+{
+	extern void	kprintf( const char *inFormat, ... );
+	
+	kprintf( "%.*s", (int) inSize, inData );
+}
+#endif
+
+#if( DEBUG_MAC_OS_X_IOLOG_ENABLED )
+//===========================================================================================================================
+//	DebugMacOSXIOLogPrint
+//===========================================================================================================================
+
+static void	DebugMacOSXIOLogPrint( char *inData, size_t inSize )
+{
+	extern void	IOLog( const char *inFormat, ... );
+	
+	IOLog( "%.*s", (int) inSize, inData );
+}
+#endif
+
+#if( TARGET_OS_MAC )
+//===========================================================================================================================
+//	DebugMacOSXLogInit
+//===========================================================================================================================
+
+static OSStatus	DebugMacOSXLogInit( void )
+{
+	OSStatus		err;
+	CFStringRef		path;
+	CFURLRef		url;
+	CFBundleRef		bundle;
+	CFStringRef		functionName;
+	void *			functionPtr;
+	
+	bundle = NULL;
+	
+	// Create a bundle reference for System.framework.
+	
+	path = CFSTR( "/System/Library/Frameworks/System.framework" );
+	url = CFURLCreateWithFileSystemPath( NULL, path, kCFURLPOSIXPathStyle, true );
+	require_action_quiet( url, exit, err = memFullErr );
+	
+	bundle = CFBundleCreate( NULL, url );
+	CFRelease( url );
+	require_action_quiet( bundle, exit, err = memFullErr );
+	
+	// Get a ptr to the system's "printf" function from System.framework.
+	
+	functionName = CFSTR( "printf" );
+	functionPtr = CFBundleGetFunctionPointerForName( bundle, functionName );
+	require_action_quiet( functionPtr, exit, err = memFullErr );	
+	
+	// Success! Note: The bundle cannot be released because it would invalidate the function ptr.
+	
+	gDebugMacOSXLogFunction = (DebugMacOSXLogFunctionPtr) functionPtr;
+	bundle = NULL;
+	err = noErr;
+	
+exit:
+	if( bundle )
+	{
+		CFRelease( bundle );
+	}
+	return( err );
+}
+
+//===========================================================================================================================
+//	DebugMacOSXLogPrint
+//===========================================================================================================================
+
+static void	DebugMacOSXLogPrint( char *inData, size_t inSize )
+{	
+	if( gDebugMacOSXLogFunction )
+	{
+		gDebugMacOSXLogFunction( "%.*s", (int) inSize, inData );
+	}
+}
+#endif
+
+#if( TARGET_OS_WIN32 )
+//===========================================================================================================================
+//	DebugWindowsDebuggerPrint
+//===========================================================================================================================
+
+void	DebugWindowsDebuggerPrint( char *inData, size_t inSize )
+{
+	TCHAR				buffer[ 512 ];
+	const char *		src;
+	const char *		end;
+	TCHAR *				dst;
+	char				c;
+	
+	// Copy locally and null terminate the string. This also converts from char to TCHAR in case we are 
+	// building with UNICODE enabled since the input is always char. Also convert \r to \n in the process.
+
+	src = inData;
+	if( inSize >= sizeof_array( buffer ) )
+	{
+		inSize = sizeof_array( buffer ) - 1;
+	}
+	end = src + inSize;
+	dst = buffer;	
+	while( src < end )
+	{
+		c = *src++;
+		if( c == '\r' )
+		{
+			c = '\n';
+		}
+		*dst++ = (TCHAR) c;
+	}
+	*dst = 0;
+	
+	// Print out the string to the debugger.
+	
+	OutputDebugString( buffer );
+}
+#endif
+
+#if( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE )
+//===========================================================================================================================
+//	DebugWindowsEventLogInit
+//===========================================================================================================================
+
+static OSStatus	DebugWindowsEventLogInit( const char *inName, HMODULE inModule )
+{
+	OSStatus			err;
+	HKEY				key;
+	TCHAR				name[ 128 ];
+	const char *		src;
+	TCHAR				path[ MAX_PATH ];
+	size_t				size;
+	DWORD				typesSupported;
+	DWORD 				n;
+	
+	key = NULL;
+
+	// Use a default name if needed then convert the name to TCHARs so it works on ANSI or Unicode builds.
+
+	if( !inName || ( *inName == '\0' ) )
+	{
+		inName = "DefaultApp";
+	}
+	DebugWinCharToTCharString( inName, kSizeCString, name, sizeof( name ), NULL );
+	
+	// Build the path string using the fixed registry path and app name.
+	
+	src = "SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\";
+	DebugWinCharToTCharString( src, kSizeCString, path, sizeof_array( path ), &size );
+	DebugWinCharToTCharString( inName, kSizeCString, path + size, sizeof_array( path ) - size, NULL );
+	
+	// Add/Open the source name as a sub-key under the Application key in the EventLog registry key.
+	
+	err = RegCreateKeyEx( HKEY_LOCAL_MACHINE, path, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_ALL_ACCESS, NULL, &key, NULL );
+	require_noerr_quiet( err, exit );
+	
+	// Set the path in the EventMessageFile subkey. Add 1 to the TCHAR count to include the null terminator.
+	
+	n = GetModuleFileName( inModule, path, sizeof_array( path ) );
+	err = translate_errno( n > 0, (OSStatus) GetLastError(), kParamErr );
+	require_noerr_quiet( err, exit );
+	n += 1;
+	n *= sizeof( TCHAR );
+	
+	err = RegSetValueEx( key, TEXT( "EventMessageFile" ), 0, REG_EXPAND_SZ, (const LPBYTE) path, n );
+	require_noerr_quiet( err, exit );
+	
+	// Set the supported event types in the TypesSupported subkey.
+	
+	typesSupported = EVENTLOG_SUCCESS | EVENTLOG_ERROR_TYPE | EVENTLOG_WARNING_TYPE | EVENTLOG_INFORMATION_TYPE |
+					 EVENTLOG_AUDIT_SUCCESS | EVENTLOG_AUDIT_FAILURE;
+	err = RegSetValueEx( key, TEXT( "TypesSupported" ), 0, REG_DWORD, (const LPBYTE) &typesSupported, sizeof( DWORD ) );
+	require_noerr_quiet( err, exit );
+	
+	// Set up the event source.
+	
+	gDebugWindowsEventLogEventSource = RegisterEventSource( NULL, name );
+	err = translate_errno( gDebugWindowsEventLogEventSource, (OSStatus) GetLastError(), kParamErr );
+	require_noerr_quiet( err, exit );
+	
+exit:
+	if( key )
+	{
+		RegCloseKey( key );
+	}
+	return( err );
+}
+
+//===========================================================================================================================
+//	DebugWindowsEventLogPrint
+//===========================================================================================================================
+
+static void	DebugWindowsEventLogPrint( DebugLevel inLevel, char *inData, size_t inSize )
+{
+	WORD				type;
+	TCHAR				buffer[ 512 ];
+	const char *		src;
+	const char *		end;
+	TCHAR *				dst;
+	char				c;
+	const TCHAR *		array[ 1 ];
+	
+	// Map the debug level to a Windows EventLog type.
+	
+	if( inLevel <= kDebugLevelNotice )
+	{
+		type = EVENTLOG_INFORMATION_TYPE;
+	}
+	else if( inLevel <= kDebugLevelWarning )
+	{
+		type = EVENTLOG_WARNING_TYPE;
+	}
+	else
+	{
+		type = EVENTLOG_ERROR_TYPE;
+	}
+	
+	// Copy locally and null terminate the string. This also converts from char to TCHAR in case we are 
+	// building with UNICODE enabled since the input is always char. Also convert \r to \n in the process.
+	
+	src = inData;
+	if( inSize >= sizeof_array( buffer ) )
+	{
+		inSize = sizeof_array( buffer ) - 1;
+	}
+	end = src + inSize;
+	dst = buffer;	
+	while( src < end )
+	{
+		c = *src++;
+		if( c == '\r' )
+		{
+			c = '\n';
+		}
+		*dst++ = (TCHAR) c;
+	}
+	*dst = 0;
+	
+	// Add the the string to the event log.
+	
+	array[ 0 ] = buffer;
+	if( gDebugWindowsEventLogEventSource )
+	{
+		ReportEvent( gDebugWindowsEventLogEventSource, type, 0, 0x20000001L, NULL, 1, 0, array, NULL );
+	}
+}
+#endif	// TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE
+
+#if( DEBUG_CORE_SERVICE_ASSERTS_ENABLED )
+//===========================================================================================================================
+//	DebugAssertOutputHandler
+//===========================================================================================================================
+
+static pascal void	
+	DebugAssertOutputHandler( 
+		OSType 				inComponentSignature, 
+		UInt32 				inOptions, 
+		const char *		inAssertString, 
+		const char *		inExceptionString, 
+		const char *		inErrorString, 
+		const char *		inFileName, 
+		long 				inLineNumber, 
+		void *				inValue, 
+		ConstStr255Param 	inOutputMsg )
+{
+	DEBUG_UNUSED( inComponentSignature );
+	DEBUG_UNUSED( inOptions );
+	DEBUG_UNUSED( inExceptionString );
+	DEBUG_UNUSED( inValue );
+	DEBUG_UNUSED( inOutputMsg );
+	
+	DebugPrintAssert( 0, inAssertString, inErrorString, inFileName, (int_least32_t) inLineNumber, "" );
+}
+#endif
+
+#if 0
+#pragma mark -
+#pragma mark == Utilities ==
+#endif
+
+//===========================================================================================================================
+//	DebugSNPrintF
+//
+//	Stolen from mDNS.c's mDNS_snprintf/mDNS_vsnprintf with the following changes:
+//
+//	Changed names to avoid name collisions with the mDNS versions.
+//	Changed types to standard C types since mDNSEmbeddedAPI.h may not be available.
+//	Conditionalized mDNS stuff so it can be used with or with mDNSEmbeddedAPI.h.
+//	Added 64-bit support for %d (%lld), %i (%lli), %u (%llu), %o (%llo), %x (%llx), and %b (%llb).
+//	Added %@   - Cocoa/CoreFoundation object. Param is the object. Strings are used directly. Others use CFCopyDescription.
+//	Added %.8a - FIbre Channel address. Arg=ptr to address.
+//	Added %##a - IPv4 (if AF_INET defined) or IPv6 (if AF_INET6 defined) sockaddr. Arg=ptr to sockaddr.
+//	Added %b   - Binary representation of integer (e.g. 01101011). Modifiers and arg=the same as %d, %x, etc.
+//	Added %C   - Mac-style FourCharCode (e.g. 'APPL'). Arg=32-bit value to print as a Mac-style FourCharCode.
+//	Added %H   - Hex Dump (e.g. "\x6b\xa7" -> "6B A7"). 1st arg=ptr, 2nd arg=size, 3rd arg=max size.
+//	Added %#H  - Hex Dump & ASCII (e.g. "\x41\x62" -> "6B A7 'Ab'"). 1st arg=ptr, 2nd arg=size, 3rd arg=max size.
+//	Added %m   - Error Message (e.g. 0 -> "kNoErr"). Modifiers and error code args are the same as %d, %x, etc.
+//	Added %S   - UTF-16 string. Host order if no BOM. Precision is UTF-16 char count. BOM counts in any precision. Arg=ptr.
+//	Added %#S  - Big Endian UTF-16 string (unless BOM overrides). Otherwise the same as %S.
+//	Added %##S - Little Endian UTF-16 string (unless BOM overrides). Otherwise the same as %S.
+//	Added %U   - Universally Unique Identifier (UUID) (e.g. 6ba7b810-9dad-11d1-80b4-00c04fd430c8). Arg=ptr to 16-byte UUID.
+//===========================================================================================================================
+
+DEBUG_EXPORT size_t DebugSNPrintF(char *sbuffer, size_t buflen, const char *fmt, ...)
+	{
+	size_t length;
+	
+	va_list ptr;
+	va_start(ptr,fmt);
+	length = DebugSNPrintFVAList(sbuffer, buflen, fmt, ptr);
+	va_end(ptr);
+	
+	return(length);
+	}
+
+//===========================================================================================================================
+//	DebugSNPrintFVAList	- va_list version of DebugSNPrintF. See DebugSNPrintF for more info.
+//===========================================================================================================================
+
+DEBUG_EXPORT size_t DebugSNPrintFVAList(char *sbuffer, size_t buflen, const char *fmt, va_list arg)
+	{
+	static const struct DebugSNPrintF_format
+		{
+		unsigned      leftJustify : 1;
+		unsigned      forceSign : 1;
+		unsigned      zeroPad : 1;
+		unsigned      havePrecision : 1;
+		unsigned      hSize : 1;
+		char          lSize;
+		char          altForm;
+		char          sign;		// +, - or space
+		unsigned int  fieldWidth;
+		unsigned int  precision;
+		} DebugSNPrintF_format_default = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
+
+	size_t nwritten = 0;
+	int c;
+	if (buflen == 0) return(0);
+	buflen--;		// Pre-reserve one space in the buffer for the terminating nul
+	if (buflen == 0) goto exit;
+	
+	for (c = *fmt; c != 0; c = *++fmt)
+		{
+		if (c != '%')
+			{
+			*sbuffer++ = (char)c;
+			if (++nwritten >= buflen) goto exit;
+			}
+		else
+			{
+			size_t i=0, j;
+			// The mDNS Vsprintf Argument Conversion Buffer is used as a temporary holding area for
+			// generating decimal numbers, hexdecimal numbers, IP addresses, domain name strings, etc.
+			// The size needs to be enough for a 256-byte domain name plus some error text.
+			#define mDNS_VACB_Size 300
+			char mDNS_VACB[mDNS_VACB_Size];
+			#define mDNS_VACB_Lim (&mDNS_VACB[mDNS_VACB_Size])
+			#define mDNS_VACB_Remain(s) ((size_t)(mDNS_VACB_Lim - s))
+			char *s = mDNS_VACB_Lim;
+			const char *digits = "0123456789ABCDEF";
+			struct DebugSNPrintF_format F = DebugSNPrintF_format_default;
+	
+			for(;;)	//  decode flags
+				{
+				c = *++fmt;
+				if      (c == '-') F.leftJustify = 1;
+				else if (c == '+') F.forceSign = 1;
+				else if (c == ' ') F.sign = ' ';
+				else if (c == '#') F.altForm++;
+				else if (c == '0') F.zeroPad = 1;
+				else break;
+				}
+	
+			if (c == '*')	//  decode field width
+				{
+				int f = va_arg(arg, int);
+				if (f < 0) { f = -f; F.leftJustify = 1; }
+				F.fieldWidth = (unsigned int)f;
+				c = *++fmt;
+				}
+			else
+				{
+				for (; c >= '0' && c <= '9'; c = *++fmt)
+					F.fieldWidth = (10 * F.fieldWidth) + (c - '0');
+				}
+	
+			if (c == '.')	//  decode precision
+				{
+				if ((c = *++fmt) == '*')
+					{ F.precision = va_arg(arg, unsigned int); c = *++fmt; }
+				else for (; c >= '0' && c <= '9'; c = *++fmt)
+						F.precision = (10 * F.precision) + (c - '0');
+				F.havePrecision = 1;
+				}
+	
+			if (F.leftJustify) F.zeroPad = 0;
+	
+			conv:
+			switch (c)	//  perform appropriate conversion
+				{
+				#if TYPE_LONGLONG_NATIVE
+					unsigned_long_long_compat n;
+					unsigned_long_long_compat base;
+				#else
+					unsigned long n;
+					unsigned long base;
+				#endif
+				case 'h' :	F.hSize = 1; c = *++fmt; goto conv;
+				case 'l' :	// fall through
+				case 'L' :	F.lSize++; c = *++fmt; goto conv;
+				case 'd' :
+				case 'i' :	base = 10;
+							goto canBeSigned;
+				case 'u' :	base = 10;
+							goto notSigned;
+				case 'o' :	base = 8;
+							goto notSigned;
+				case 'b' :	base = 2;
+							goto notSigned;
+				case 'p' :	n = va_arg(arg, uintptr_t);
+							F.havePrecision = 1;
+							F.precision = (sizeof(uintptr_t) == 4) ? 8 : 16;
+							F.sign = 0;
+							base = 16;
+							c = 'x';
+							goto number;
+				case 'x' :	digits = "0123456789abcdef";
+				case 'X' :	base = 16;
+							goto notSigned;
+				canBeSigned:
+							#if TYPE_LONGLONG_NATIVE
+								if (F.lSize == 1) n = (unsigned_long_long_compat)va_arg(arg, long);
+								else if (F.lSize == 2) n = (unsigned_long_long_compat)va_arg(arg, long_long_compat);
+								else n = (unsigned_long_long_compat)va_arg(arg, int);
+							#else
+								if (F.lSize == 1) n = (unsigned long)va_arg(arg, long);
+								else if (F.lSize == 2) goto exit;
+								else n = (unsigned long)va_arg(arg, int);
+							#endif
+							if (F.hSize) n = (short) n;
+							#if TYPE_LONGLONG_NATIVE
+								if ((long_long_compat) n < 0) { n = (unsigned_long_long_compat)-(long_long_compat)n; F.sign = '-'; }
+							#else
+								if ((long) n < 0) { n = (unsigned long)-(long)n; F.sign = '-'; }
+							#endif
+							else if (F.forceSign) F.sign = '+';
+							goto number;
+				
+				notSigned:	if (F.lSize == 1) n = va_arg(arg, unsigned long);
+							else if (F.lSize == 2)
+								{
+								#if TYPE_LONGLONG_NATIVE
+									n = va_arg(arg, unsigned_long_long_compat);
+								#else
+									goto exit;
+								#endif
+								}
+							else n = va_arg(arg, unsigned int);
+							if (F.hSize) n = (unsigned short) n;
+							F.sign = 0;
+							goto number;
+				
+				number:		if (!F.havePrecision)
+								{
+								if (F.zeroPad)
+									{
+									F.precision = F.fieldWidth;
+									if (F.altForm) F.precision -= 2;
+									if (F.sign) --F.precision;
+									}
+								if (F.precision < 1) F.precision = 1;
+								}
+							if (F.precision > mDNS_VACB_Size - 1)
+								F.precision = mDNS_VACB_Size - 1;
+							for (i = 0; n; n /= base, i++) *--s = (char)(digits[n % base]);
+							for (; i < F.precision; i++) *--s = '0';
+							if (F.altForm) { *--s = (char)c; *--s = '0'; i += 2; }
+							if (F.sign) { *--s = F.sign; i++; }
+							break;
+	
+				case 'a' :	{
+							unsigned char *a = va_arg(arg, unsigned char *);
+							char pre[4] = "";
+							char post[32] = "";
+							if (!a) { static char emsg[] = "<<NULL>>"; s = emsg; i = sizeof(emsg)-1; }
+							else
+								{
+								s = mDNS_VACB;	// Adjust s to point to the start of the buffer, not the end
+								if (F.altForm == 1)
+									{
+									#if(defined(MDNS_DEBUGMSGS))
+										mDNSAddr *ip = (mDNSAddr*)a;
+										switch (ip->type)
+											{
+											case mDNSAddrType_IPv4: F.precision =  4; a = (unsigned char *)&ip->ip.v4; break;
+											case mDNSAddrType_IPv6: F.precision = 16; a = (unsigned char *)&ip->ip.v6; break;
+											default:                F.precision =  0; break;
+											}
+									#else
+										F.precision = 0;	// mDNSEmbeddedAPI.h not included so no mDNSAddr support
+									#endif
+									}
+								else if (F.altForm == 2)
+									{
+									#ifdef AF_INET
+										const struct sockaddr *sa;
+										unsigned char *port;
+										sa = (const struct sockaddr*)a;
+										switch (sa->sa_family)
+											{
+											case AF_INET:  F.precision =  4; a = (unsigned char*)&((const struct sockaddr_in *)a)->sin_addr;
+											               port = (unsigned char*)&((const struct sockaddr_in *)sa)->sin_port;
+											               DebugSNPrintF(post, sizeof(post), ":%d", (port[0] << 8) | port[1]); break;
+											#ifdef AF_INET6
+											case AF_INET6: F.precision = 16; a = (unsigned char*)&((const struct sockaddr_in6 *)a)->sin6_addr; 
+											               pre[0] = '['; pre[1] = '\0';
+											               port = (unsigned char*)&((const struct sockaddr_in6 *)sa)->sin6_port;
+											               DebugSNPrintF(post, sizeof(post), "%%%d]:%d", 
+											               		(int)((const struct sockaddr_in6 *)sa)->sin6_scope_id,
+											               		(port[0] << 8) | port[1]); break;
+											#endif
+											default:       F.precision =  0; break;
+											}
+									#else
+										F.precision = 0;	// socket interfaces not included so no sockaddr support
+									#endif
+									}
+								switch (F.precision)
+									{
+									case  4: i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), "%d.%d.%d.%d%s",
+														a[0], a[1], a[2], a[3], post); break;
+									case  6: i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), "%02X:%02X:%02X:%02X:%02X:%02X",
+														a[0], a[1], a[2], a[3], a[4], a[5]); break;
+									case  8: i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), "%02X:%02X:%02X:%02X:%02X:%02X:%02X:%02X",
+														a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7]); break;
+									case 16: i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), 
+														"%s%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X:%02X%02X%s",
+														pre, a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7], a[8], 
+														a[9], a[10], a[11], a[12], a[13], a[14], a[15], post); break;		
+									default: i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), "%s", "<< ERROR: Must specify address size "
+														"(i.e. %.4a=IPv4, %.6a=Ethernet, %.8a=Fibre Channel %.16a=IPv6) >>"); break;
+									}
+								}
+							}
+							break;
+
+				case 'U' :	{
+							unsigned char *a = va_arg(arg, unsigned char *);
+							if (!a) { static char emsg[] = "<<NULL>>"; s = emsg; i = sizeof(emsg)-1; }
+							else
+								{
+								s = mDNS_VACB;	// Adjust s to point to the start of the buffer, not the end
+								i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x",
+										*((uint32_t*) &a[0]), *((uint16_t*) &a[4]), *((uint16_t*) &a[6]), 
+										a[8], a[9], a[10], a[11], a[12], a[13], a[14], a[15]); break;
+								}
+							}
+							break;
+
+				case 'c' :	*--s = (char)va_arg(arg, int); i = 1; break;
+	
+				case 'C' :	if (F.lSize) n = va_arg(arg, unsigned long);
+							else n = va_arg(arg, unsigned int);
+							if (F.hSize) n = (unsigned short) n;
+							c = (int)( n        & 0xFF); *--s = (char)(DebugIsPrint(c) ? c : '^');
+							c = (int)((n >>  8) & 0xFF); *--s = (char)(DebugIsPrint(c) ? c : '^');
+							c = (int)((n >> 16) & 0xFF); *--s = (char)(DebugIsPrint(c) ? c : '^');
+							c = (int)((n >> 24) & 0xFF); *--s = (char)(DebugIsPrint(c) ? c : '^');
+							i = 4;
+							break;
+	
+				case 's' :	s = va_arg(arg, char *);
+							if (!s) { static char emsg[] = "<<NULL>>"; s = emsg; i = sizeof(emsg)-1; }
+							else switch (F.altForm)
+								{
+								case 0:	i=0;
+										if (F.havePrecision)				// C string
+											{
+											while((i < F.precision) && s[i]) i++;
+											// Make sure we don't truncate in the middle of a UTF-8 character.
+											// If the last character is part of a multi-byte UTF-8 character, back up to the start of it.
+											j=0;
+											while((i > 0) && ((c = s[i-1]) & 0x80)) { j++; i--; if((c & 0xC0) != 0x80) break; }
+											// If the actual count of UTF-8 characters matches the encoded UTF-8 count, add it back.
+											if((j > 1) && (j <= 6))
+												{
+												int test = (0xFF << (8-j)) & 0xFF;
+												int mask = test | (1 << ((8-j)-1));
+												if((c & mask) == test) i += j;
+												}
+											}
+										else
+											while(s[i]) i++;
+										break;								
+								case 1: i = (unsigned char) *s++; break;	// Pascal string
+								case 2: {									// DNS label-sequence name
+										unsigned char *a = (unsigned char *)s;
+										s = mDNS_VACB;	// Adjust s to point to the start of the buffer, not the end
+										if (*a == 0) *s++ = '.';	// Special case for root DNS name
+										while (*a)
+											{
+											if (*a > 63) { s += DebugSNPrintF(s, mDNS_VACB_Remain(s), "<<INVALID LABEL LENGTH %u>>", *a); break; }
+											if (s + *a >= &mDNS_VACB[254]) { s += DebugSNPrintF(s, mDNS_VACB_Remain(s), "<<NAME TOO LONG>>"); break; }
+											s += DebugSNPrintF(s, mDNS_VACB_Remain(s), "%#s.", a);
+											a += 1 + *a;
+											}
+										i = (size_t)(s - mDNS_VACB);
+										s = mDNS_VACB;	// Reset s back to the start of the buffer
+										break;
+										}
+								}
+							if (F.havePrecision && i > F.precision)		// Make sure we don't truncate in the middle of a UTF-8 character
+								{ i = F.precision; while (i>0 && (s[i] & 0xC0) == 0x80) i--; }
+							break;
+				
+				case 'S':	{	// UTF-16 string
+							unsigned char *a = va_arg(arg, unsigned char *);
+							uint16_t      *u = (uint16_t*)a;
+							if (!u) { static char emsg[] = "<<NULL>>"; s = emsg; i = sizeof(emsg)-1; }
+							if ((!F.havePrecision || F.precision))
+								{
+								if      ((a[0] == 0xFE) && (a[1] == 0xFF)) { F.altForm = 1; u += 1; a += 2; F.precision--; }	// Big Endian
+								else if ((a[0] == 0xFF) && (a[1] == 0xFE)) { F.altForm = 2; u += 1; a += 2; F.precision--; }	// Little Endian
+								}
+							s = mDNS_VACB;	// Adjust s to point to the start of the buffer, not the end
+							switch (F.altForm)
+								{
+								case 0:	while ((!F.havePrecision || (i < F.precision)) && u[i] && mDNS_VACB_Remain(s))	// Host Endian
+											{ c = u[i]; *s++ = (char)(DebugIsPrint(c) ? c : '^'); i++; }
+										break;
+								case 1: while ((!F.havePrecision || (i < F.precision)) && u[i] && mDNS_VACB_Remain(s))	// Big Endian
+											{ c = ((a[0] << 8) | a[1]) & 0xFF; *s++ = (char)(DebugIsPrint(c) ? c : '^'); i++; a += 2; }
+										break;
+								case 2: while ((!F.havePrecision || (i < F.precision)) && u[i] && mDNS_VACB_Remain(s))	// Little Endian
+											{ c = ((a[1] << 8) | a[0]) & 0xFF; *s++ = (char)(DebugIsPrint(c) ? c : '^'); i++; a += 2; }
+										break;
+								}
+							}
+							s = mDNS_VACB;	// Reset s back to the start of the buffer
+							break;
+			
+			#if TARGET_OS_MAC
+				case '@':	{	// Cocoa/CoreFoundation object
+							CFTypeRef cfObj;
+							CFStringRef cfStr;
+							cfObj = (CFTypeRef) va_arg(arg, void *);
+							cfStr = (CFGetTypeID(cfObj) == CFStringGetTypeID()) ? (CFStringRef)CFRetain(cfObj) : CFCopyDescription(cfObj);
+							s = mDNS_VACB;	// Adjust s to point to the start of the buffer, not the end
+							if (cfStr)
+								{
+								CFRange range;
+								CFIndex m;
+								range = CFRangeMake(0, CFStringGetLength(cfStr));
+								m = 0;
+								CFStringGetBytes(cfStr, range, kCFStringEncodingUTF8, '^', false, (UInt8*)mDNS_VACB, (CFIndex)sizeof(mDNS_VACB), &m);
+								CFRelease(cfStr);
+								i = (size_t) m;
+								}
+							else
+								{
+								i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), "%s", "ERROR: <invalid CF object>" );
+								}
+							}
+							if (F.havePrecision && i > F.precision)		// Make sure we don't truncate in the middle of a UTF-8 character
+								{ i = F.precision; while (i>0 && (s[i] & 0xC0) == 0x80) i--; }
+							break;
+			#endif
+
+				case 'm' :	{	// Error Message
+							long err;
+							if (F.lSize) err = va_arg(arg, long);
+							else err = va_arg(arg, int);
+							if (F.hSize) err = (short)err;
+							DebugGetErrorString(err, mDNS_VACB, sizeof(mDNS_VACB));
+							s = mDNS_VACB;	// Adjust s to point to the start of the buffer, not the end
+							for(i=0;s[i];i++) {}
+							}
+							break;
+
+				case 'H' :	{	// Hex Dump
+							void *a = va_arg(arg, void *);
+							size_t size = (size_t)va_arg(arg, int);
+							size_t max = (size_t)va_arg(arg, int);
+							DebugFlags flags = 
+								kDebugFlagsNoAddress | kDebugFlagsNoOffset | kDebugFlagsNoNewLine |
+								kDebugFlags8BitSeparator | kDebugFlagsNo32BitSeparator |
+								kDebugFlagsNo16ByteHexPad | kDebugFlagsNoByteCount;
+							if (F.altForm == 0) flags |= kDebugFlagsNoASCII;
+							size = (max < size) ? max : size;
+							s = mDNS_VACB;	// Adjust s to point to the start of the buffer, not the end
+							i = DebugHexDump(kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, a, a, size, flags, mDNS_VACB, sizeof(mDNS_VACB));
+							}
+							break;
+				
+				case 'v' :	{	// Version
+							uint32_t version;
+							version = va_arg(arg, unsigned int);
+							DebugNumVersionToString(version, mDNS_VACB);
+							s = mDNS_VACB;	// Adjust s to point to the start of the buffer, not the end
+							for(i=0;s[i];i++) {}
+							}
+							break;
+
+				case 'n' :	s = va_arg(arg, char *);
+							if      (F.hSize) * (short *) s = (short)nwritten;
+							else if (F.lSize) * (long  *) s = (long)nwritten;
+							else              * (int   *) s = (int)nwritten;
+							continue;
+	
+				default:	s = mDNS_VACB;
+							i = DebugSNPrintF(mDNS_VACB, sizeof(mDNS_VACB), "<<UNKNOWN FORMAT CONVERSION CODE %%%c>>", c);
+
+				case '%' :	*sbuffer++ = (char)c;
+							if (++nwritten >= buflen) goto exit;
+							break;
+				}
+	
+			if (i < F.fieldWidth && !F.leftJustify)			// Pad on the left
+				do	{
+					*sbuffer++ = ' ';
+					if (++nwritten >= buflen) goto exit;
+					} while (i < --F.fieldWidth);
+	
+			if (i > buflen - nwritten)	// Make sure we don't truncate in the middle of a UTF-8 character
+				{ i = buflen - nwritten; while (i>0 && (s[i] & 0xC0) == 0x80) i--; }
+			for (j=0; j<i; j++) *sbuffer++ = *s++;			// Write the converted result
+			nwritten += i;
+			if (nwritten >= buflen) goto exit;
+	
+			for (; i < F.fieldWidth; i++)					// Pad on the right
+				{
+				*sbuffer++ = ' ';
+				if (++nwritten >= buflen) goto exit;
+				}
+			}
+		}
+	exit:
+	*sbuffer++ = 0;
+	return(nwritten);
+	}
+
+//===========================================================================================================================
+//	DebugGetErrorString
+//===========================================================================================================================
+
+DEBUG_EXPORT const char *	DebugGetErrorString( int_least32_t inErrorCode, char *inBuffer, size_t inBufferSize )
+{
+	const char *		s;
+	char *				dst;
+	char *				end;
+#if( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE )
+	char				buffer[ 256 ];
+#endif
+	
+	switch( inErrorCode )
+	{
+		#define	CaseErrorString( X, STR )					case X: s = STR; break
+		#define	CaseErrorStringify( X )						case X: s = # X; break
+		#define	CaseErrorStringifyHardCode( VALUE, X )		case VALUE: s = # X; break
+		
+		// General Errors
+		
+		CaseErrorString( 0,  "no error" );
+		CaseErrorString( 1,  "in-progress/waiting" );
+		CaseErrorString( -1, "catch-all unknown error" );
+				
+		// ACP Errors
+		
+		CaseErrorStringifyHardCode( -2,  kACPBadRequestErr );
+		CaseErrorStringifyHardCode( -3,  kACPNoMemoryErr );
+		CaseErrorStringifyHardCode( -4,  kACPBadParamErr );
+		CaseErrorStringifyHardCode( -5,  kACPNotFoundErr );
+		CaseErrorStringifyHardCode( -6,  kACPBadChecksumErr );
+		CaseErrorStringifyHardCode( -7,  kACPCommandNotHandledErr );
+		CaseErrorStringifyHardCode( -8,  kACPNetworkErr );
+		CaseErrorStringifyHardCode( -9,  kACPDuplicateCommandHandlerErr );
+		CaseErrorStringifyHardCode( -10, kACPUnknownPropertyErr );
+		CaseErrorStringifyHardCode( -11, kACPImmutablePropertyErr );
+		CaseErrorStringifyHardCode( -12, kACPBadPropertyValueErr );
+		CaseErrorStringifyHardCode( -13, kACPNoResourcesErr );
+		CaseErrorStringifyHardCode( -14, kACPBadOptionErr );
+		CaseErrorStringifyHardCode( -15, kACPBadSizeErr );
+		CaseErrorStringifyHardCode( -16, kACPBadPasswordErr );
+		CaseErrorStringifyHardCode( -17, kACPNotInitializedErr );
+		CaseErrorStringifyHardCode( -18, kACPNonReadablePropertyErr );
+		CaseErrorStringifyHardCode( -19, kACPBadVersionErr );
+		CaseErrorStringifyHardCode( -20, kACPBadSignatureErr );
+		CaseErrorStringifyHardCode( -21, kACPBadIndexErr );
+		CaseErrorStringifyHardCode( -22, kACPUnsupportedErr );
+		CaseErrorStringifyHardCode( -23, kACPInUseErr );
+		CaseErrorStringifyHardCode( -24, kACPParamCountErr );
+		CaseErrorStringifyHardCode( -25, kACPIDErr );
+		CaseErrorStringifyHardCode( -26, kACPFormatErr );
+		CaseErrorStringifyHardCode( -27, kACPUnknownUserErr );
+		CaseErrorStringifyHardCode( -28, kACPAccessDeniedErr );
+		CaseErrorStringifyHardCode( -29, kACPIncorrectFWErr );
+		
+		// Common Services Errors
+		
+		CaseErrorStringify( kUnknownErr );
+		CaseErrorStringify( kOptionErr );
+		CaseErrorStringify( kSelectorErr );
+		CaseErrorStringify( kExecutionStateErr );
+		CaseErrorStringify( kPathErr );
+		CaseErrorStringify( kParamErr );
+		CaseErrorStringify( kParamCountErr );
+		CaseErrorStringify( kCommandErr );
+		CaseErrorStringify( kIDErr );
+		CaseErrorStringify( kStateErr );
+		CaseErrorStringify( kRangeErr );
+		CaseErrorStringify( kRequestErr );
+		CaseErrorStringify( kResponseErr );
+		CaseErrorStringify( kChecksumErr );
+		CaseErrorStringify( kNotHandledErr );
+		CaseErrorStringify( kVersionErr );
+		CaseErrorStringify( kSignatureErr );
+		CaseErrorStringify( kFormatErr );
+		CaseErrorStringify( kNotInitializedErr );
+		CaseErrorStringify( kAlreadyInitializedErr );
+		CaseErrorStringify( kNotInUseErr );
+		CaseErrorStringify( kInUseErr );
+		CaseErrorStringify( kTimeoutErr );
+		CaseErrorStringify( kCanceledErr );
+		CaseErrorStringify( kAlreadyCanceledErr );
+		CaseErrorStringify( kCannotCancelErr );
+		CaseErrorStringify( kDeletedErr );
+		CaseErrorStringify( kNotFoundErr );
+		CaseErrorStringify( kNoMemoryErr );
+		CaseErrorStringify( kNoResourcesErr );
+		CaseErrorStringify( kDuplicateErr );
+		CaseErrorStringify( kImmutableErr );
+		CaseErrorStringify( kUnsupportedDataErr );
+		CaseErrorStringify( kIntegrityErr );
+		CaseErrorStringify( kIncompatibleErr );
+		CaseErrorStringify( kUnsupportedErr );
+		CaseErrorStringify( kUnexpectedErr );
+		CaseErrorStringify( kValueErr );
+		CaseErrorStringify( kNotReadableErr );
+		CaseErrorStringify( kNotWritableErr );
+		CaseErrorStringify( kBadReferenceErr );
+		CaseErrorStringify( kFlagErr );
+		CaseErrorStringify( kMalformedErr );
+		CaseErrorStringify( kSizeErr );
+		CaseErrorStringify( kNameErr );
+		CaseErrorStringify( kNotReadyErr );
+		CaseErrorStringify( kReadErr );
+		CaseErrorStringify( kWriteErr );
+		CaseErrorStringify( kMismatchErr );
+		CaseErrorStringify( kDateErr );
+		CaseErrorStringify( kUnderrunErr );
+		CaseErrorStringify( kOverrunErr );
+		CaseErrorStringify( kEndingErr );
+		CaseErrorStringify( kConnectionErr );
+		CaseErrorStringify( kAuthenticationErr );
+		CaseErrorStringify( kOpenErr );
+		CaseErrorStringify( kTypeErr );
+		CaseErrorStringify( kSkipErr );
+		CaseErrorStringify( kNoAckErr );
+		CaseErrorStringify( kCollisionErr );
+		CaseErrorStringify( kBackoffErr );
+		CaseErrorStringify( kNoAddressAckErr );
+		CaseErrorStringify( kBusyErr );
+		CaseErrorStringify( kNoSpaceErr );
+		
+		// mDNS/DNS-SD Errors
+		
+		CaseErrorStringifyHardCode( -65537, mStatus_UnknownErr );
+		CaseErrorStringifyHardCode( -65538, mStatus_NoSuchNameErr );
+		CaseErrorStringifyHardCode( -65539, mStatus_NoMemoryErr );
+		CaseErrorStringifyHardCode( -65540, mStatus_BadParamErr );
+		CaseErrorStringifyHardCode( -65541, mStatus_BadReferenceErr );
+		CaseErrorStringifyHardCode( -65542, mStatus_BadStateErr );
+		CaseErrorStringifyHardCode( -65543, mStatus_BadFlagsErr );
+		CaseErrorStringifyHardCode( -65544, mStatus_UnsupportedErr );
+		CaseErrorStringifyHardCode( -65545, mStatus_NotInitializedErr );
+		CaseErrorStringifyHardCode( -65546, mStatus_NoCache );
+		CaseErrorStringifyHardCode( -65547, mStatus_AlreadyRegistered );
+		CaseErrorStringifyHardCode( -65548, mStatus_NameConflict );
+		CaseErrorStringifyHardCode( -65549, mStatus_Invalid );
+		CaseErrorStringifyHardCode( -65550, mStatus_GrowCache );
+		CaseErrorStringifyHardCode( -65551, mStatus_BadInterfaceErr );
+		CaseErrorStringifyHardCode( -65552, mStatus_Incompatible );
+		CaseErrorStringifyHardCode( -65791, mStatus_ConfigChanged );
+		CaseErrorStringifyHardCode( -65792, mStatus_MemFree );
+		
+		// RSP Errors
+		
+		CaseErrorStringifyHardCode( -400000, kRSPUnknownErr );
+		CaseErrorStringifyHardCode( -400050, kRSPParamErr );
+		CaseErrorStringifyHardCode( -400108, kRSPNoMemoryErr );
+		CaseErrorStringifyHardCode( -405246, kRSPRangeErr );
+		CaseErrorStringifyHardCode( -409057, kRSPSizeErr );
+		CaseErrorStringifyHardCode( -400200, kRSPHardwareErr );
+		CaseErrorStringifyHardCode( -401712, kRSPTimeoutErr );	
+		CaseErrorStringifyHardCode( -402053, kRSPUnsupportedErr );
+		CaseErrorStringifyHardCode( -402419, kRSPIDErr );
+		CaseErrorStringifyHardCode( -403165, kRSPFlagErr );
+		CaseErrorString( 			-200000, "kRSPControllerStatusBase - 0x50" );		
+		CaseErrorString(			-200080, "kRSPCommandSucceededErr - 0x50" );
+		CaseErrorString( 			-200001, "kRSPCommandFailedErr - 0x01" );
+		CaseErrorString( 			-200051, "kRSPChecksumErr - 0x33" );
+		CaseErrorString( 			-200132, "kRSPCommandTimeoutErr - 0x84" );
+		CaseErrorString( 			-200034, "kRSPPasswordRequiredErr - 0x22 OBSOLETE" );
+		CaseErrorString( 			-200128, "kRSPCanceledErr - 0x02 Async" );
+		
+		// XML Errors
+		
+		CaseErrorStringifyHardCode( -100043, kXMLNotFoundErr );
+		CaseErrorStringifyHardCode( -100050, kXMLParamErr );
+		CaseErrorStringifyHardCode( -100108, kXMLNoMemoryErr );
+		CaseErrorStringifyHardCode( -100206, kXMLFormatErr );
+		CaseErrorStringifyHardCode( -100586, kXMLNoRootElementErr );
+		CaseErrorStringifyHardCode( -101703, kXMLWrongDataTypeErr );
+		CaseErrorStringifyHardCode( -101726, kXMLKeyErr );
+		CaseErrorStringifyHardCode( -102053, kXMLUnsupportedErr );
+		CaseErrorStringifyHardCode( -102063, kXMLMissingElementErr );
+		CaseErrorStringifyHardCode( -103026, kXMLParseErr );
+		CaseErrorStringifyHardCode( -103159, kXMLBadDataErr );
+		CaseErrorStringifyHardCode( -103170, kXMLBadNameErr );
+		CaseErrorStringifyHardCode( -105246, kXMLRangeErr );
+		CaseErrorStringifyHardCode( -105251, kXMLUnknownElementErr );
+		CaseErrorStringifyHardCode( -108739, kXMLMalformedInputErr );
+		CaseErrorStringifyHardCode( -109057, kXMLBadSizeErr );
+		CaseErrorStringifyHardCode( -101730, kXMLMissingChildElementErr );
+		CaseErrorStringifyHardCode( -102107, kXMLMissingParentElementErr );
+		CaseErrorStringifyHardCode( -130587, kXMLNonRootElementErr );
+		CaseErrorStringifyHardCode( -102015, kXMLDateErr );
+
+	#if( __MACH__ )
+	
+		// Mach Errors
+
+		CaseErrorStringifyHardCode( 0x00002000, MACH_MSG_IPC_SPACE );
+		CaseErrorStringifyHardCode( 0x00001000, MACH_MSG_VM_SPACE );
+		CaseErrorStringifyHardCode( 0x00000800, MACH_MSG_IPC_KERNEL );
+		CaseErrorStringifyHardCode( 0x00000400, MACH_MSG_VM_KERNEL );
+		CaseErrorStringifyHardCode( 0x10000001, MACH_SEND_IN_PROGRESS );
+		CaseErrorStringifyHardCode( 0x10000002, MACH_SEND_INVALID_DATA );
+		CaseErrorStringifyHardCode( 0x10000003, MACH_SEND_INVALID_DEST );
+		CaseErrorStringifyHardCode( 0x10000004, MACH_SEND_TIMED_OUT );
+		CaseErrorStringifyHardCode( 0x10000007, MACH_SEND_INTERRUPTED );
+		CaseErrorStringifyHardCode( 0x10000008, MACH_SEND_MSG_TOO_SMALL );
+		CaseErrorStringifyHardCode( 0x10000009, MACH_SEND_INVALID_REPLY );
+		CaseErrorStringifyHardCode( 0x1000000A, MACH_SEND_INVALID_RIGHT );
+		CaseErrorStringifyHardCode( 0x1000000B, MACH_SEND_INVALID_NOTIFY );
+		CaseErrorStringifyHardCode( 0x1000000C, MACH_SEND_INVALID_MEMORY );
+		CaseErrorStringifyHardCode( 0x1000000D, MACH_SEND_NO_BUFFER );
+		CaseErrorStringifyHardCode( 0x1000000E, MACH_SEND_TOO_LARGE );
+		CaseErrorStringifyHardCode( 0x1000000F, MACH_SEND_INVALID_TYPE );
+		CaseErrorStringifyHardCode( 0x10000010, MACH_SEND_INVALID_HEADER );
+		CaseErrorStringifyHardCode( 0x10000011, MACH_SEND_INVALID_TRAILER );
+		CaseErrorStringifyHardCode( 0x10000015, MACH_SEND_INVALID_RT_OOL_SIZE );
+		CaseErrorStringifyHardCode( 0x10004001, MACH_RCV_IN_PROGRESS );
+		CaseErrorStringifyHardCode( 0x10004002, MACH_RCV_INVALID_NAME );
+		CaseErrorStringifyHardCode( 0x10004003, MACH_RCV_TIMED_OUT );
+		CaseErrorStringifyHardCode( 0x10004004, MACH_RCV_TOO_LARGE );
+		CaseErrorStringifyHardCode( 0x10004005, MACH_RCV_INTERRUPTED );
+		CaseErrorStringifyHardCode( 0x10004006, MACH_RCV_PORT_CHANGED );
+		CaseErrorStringifyHardCode( 0x10004007, MACH_RCV_INVALID_NOTIFY );
+		CaseErrorStringifyHardCode( 0x10004008, MACH_RCV_INVALID_DATA );
+		CaseErrorStringifyHardCode( 0x10004009, MACH_RCV_PORT_DIED );
+		CaseErrorStringifyHardCode( 0x1000400A, MACH_RCV_IN_SET );
+		CaseErrorStringifyHardCode( 0x1000400B, MACH_RCV_HEADER_ERROR );
+		CaseErrorStringifyHardCode( 0x1000400C, MACH_RCV_BODY_ERROR );
+		CaseErrorStringifyHardCode( 0x1000400D, MACH_RCV_INVALID_TYPE );
+		CaseErrorStringifyHardCode( 0x1000400E, MACH_RCV_SCATTER_SMALL );
+		CaseErrorStringifyHardCode( 0x1000400F, MACH_RCV_INVALID_TRAILER );
+		CaseErrorStringifyHardCode( 0x10004011, MACH_RCV_IN_PROGRESS_TIMED );
+
+		// Mach OSReturn Errors
+
+		CaseErrorStringifyHardCode( 0xDC000001, kOSReturnError );
+		CaseErrorStringifyHardCode( 0xDC004001, kOSMetaClassInternal );
+		CaseErrorStringifyHardCode( 0xDC004002, kOSMetaClassHasInstances );
+		CaseErrorStringifyHardCode( 0xDC004003, kOSMetaClassNoInit );
+		CaseErrorStringifyHardCode( 0xDC004004, kOSMetaClassNoTempData );
+		CaseErrorStringifyHardCode( 0xDC004005, kOSMetaClassNoDicts );
+		CaseErrorStringifyHardCode( 0xDC004006, kOSMetaClassNoKModSet );
+		CaseErrorStringifyHardCode( 0xDC004007, kOSMetaClassNoInsKModSet );
+		CaseErrorStringifyHardCode( 0xDC004008, kOSMetaClassNoSuper );
+		CaseErrorStringifyHardCode( 0xDC004009, kOSMetaClassInstNoSuper );
+		CaseErrorStringifyHardCode( 0xDC00400A, kOSMetaClassDuplicateClass );
+
+		// IOKit Errors
+
+		CaseErrorStringifyHardCode( 0xE00002BC, kIOReturnError );
+		CaseErrorStringifyHardCode( 0xE00002BD, kIOReturnNoMemory );
+		CaseErrorStringifyHardCode( 0xE00002BE, kIOReturnNoResources );
+		CaseErrorStringifyHardCode( 0xE00002BF, kIOReturnIPCError );
+		CaseErrorStringifyHardCode( 0xE00002C0, kIOReturnNoDevice );
+		CaseErrorStringifyHardCode( 0xE00002C1, kIOReturnNotPrivileged );
+		CaseErrorStringifyHardCode( 0xE00002C2, kIOReturnBadArgument );
+		CaseErrorStringifyHardCode( 0xE00002C3, kIOReturnLockedRead );
+		CaseErrorStringifyHardCode( 0xE00002C4, kIOReturnLockedWrite );
+		CaseErrorStringifyHardCode( 0xE00002C5, kIOReturnExclusiveAccess );
+		CaseErrorStringifyHardCode( 0xE00002C6, kIOReturnBadMessageID );
+		CaseErrorStringifyHardCode( 0xE00002C7, kIOReturnUnsupported );
+		CaseErrorStringifyHardCode( 0xE00002C8, kIOReturnVMError );
+		CaseErrorStringifyHardCode( 0xE00002C9, kIOReturnInternalError );
+		CaseErrorStringifyHardCode( 0xE00002CA, kIOReturnIOError );
+		CaseErrorStringifyHardCode( 0xE00002CC, kIOReturnCannotLock );
+		CaseErrorStringifyHardCode( 0xE00002CD, kIOReturnNotOpen );
+		CaseErrorStringifyHardCode( 0xE00002CE, kIOReturnNotReadable );
+		CaseErrorStringifyHardCode( 0xE00002CF, kIOReturnNotWritable );
+		CaseErrorStringifyHardCode( 0xE00002D0, kIOReturnNotAligned );
+		CaseErrorStringifyHardCode( 0xE00002D1, kIOReturnBadMedia );
+		CaseErrorStringifyHardCode( 0xE00002D2, kIOReturnStillOpen );
+		CaseErrorStringifyHardCode( 0xE00002D3, kIOReturnRLDError );
+		CaseErrorStringifyHardCode( 0xE00002D4, kIOReturnDMAError );
+		CaseErrorStringifyHardCode( 0xE00002D5, kIOReturnBusy );
+		CaseErrorStringifyHardCode( 0xE00002D6, kIOReturnTimeout );
+		CaseErrorStringifyHardCode( 0xE00002D7, kIOReturnOffline );
+		CaseErrorStringifyHardCode( 0xE00002D8, kIOReturnNotReady );
+		CaseErrorStringifyHardCode( 0xE00002D9, kIOReturnNotAttached );
+		CaseErrorStringifyHardCode( 0xE00002DA, kIOReturnNoChannels );
+		CaseErrorStringifyHardCode( 0xE00002DB, kIOReturnNoSpace );
+		CaseErrorStringifyHardCode( 0xE00002DD, kIOReturnPortExists );
+		CaseErrorStringifyHardCode( 0xE00002DE, kIOReturnCannotWire );
+		CaseErrorStringifyHardCode( 0xE00002DF, kIOReturnNoInterrupt );
+		CaseErrorStringifyHardCode( 0xE00002E0, kIOReturnNoFrames );
+		CaseErrorStringifyHardCode( 0xE00002E1, kIOReturnMessageTooLarge );
+		CaseErrorStringifyHardCode( 0xE00002E2, kIOReturnNotPermitted );
+		CaseErrorStringifyHardCode( 0xE00002E3, kIOReturnNoPower );
+		CaseErrorStringifyHardCode( 0xE00002E4, kIOReturnNoMedia );
+		CaseErrorStringifyHardCode( 0xE00002E5, kIOReturnUnformattedMedia );
+		CaseErrorStringifyHardCode( 0xE00002E6, kIOReturnUnsupportedMode );
+		CaseErrorStringifyHardCode( 0xE00002E7, kIOReturnUnderrun );
+		CaseErrorStringifyHardCode( 0xE00002E8, kIOReturnOverrun );
+		CaseErrorStringifyHardCode( 0xE00002E9, kIOReturnDeviceError	 );
+		CaseErrorStringifyHardCode( 0xE00002EA, kIOReturnNoCompletion	 );
+		CaseErrorStringifyHardCode( 0xE00002EB, kIOReturnAborted	 );
+		CaseErrorStringifyHardCode( 0xE00002EC, kIOReturnNoBandwidth	 );
+		CaseErrorStringifyHardCode( 0xE00002ED, kIOReturnNotResponding	 );
+		CaseErrorStringifyHardCode( 0xE00002EE, kIOReturnIsoTooOld	 );
+		CaseErrorStringifyHardCode( 0xE00002EF, kIOReturnIsoTooNew	 );
+		CaseErrorStringifyHardCode( 0xE00002F0, kIOReturnNotFound );
+		CaseErrorStringifyHardCode( 0xE0000001, kIOReturnInvalid );
+
+		// IOKit FireWire Errors
+
+		CaseErrorStringifyHardCode( 0xE0008010, kIOFireWireResponseBase );
+		CaseErrorStringifyHardCode( 0xE0008020, kIOFireWireBusReset );
+		CaseErrorStringifyHardCode( 0xE0008001, kIOConfigNoEntry );
+		CaseErrorStringifyHardCode( 0xE0008002, kIOFireWirePending );
+		CaseErrorStringifyHardCode( 0xE0008003, kIOFireWireLastDCLToken );
+		CaseErrorStringifyHardCode( 0xE0008004, kIOFireWireConfigROMInvalid );
+		CaseErrorStringifyHardCode( 0xE0008005, kIOFireWireAlreadyRegistered );
+		CaseErrorStringifyHardCode( 0xE0008006, kIOFireWireMultipleTalkers );
+		CaseErrorStringifyHardCode( 0xE0008007, kIOFireWireChannelActive );
+		CaseErrorStringifyHardCode( 0xE0008008, kIOFireWireNoListenerOrTalker );
+		CaseErrorStringifyHardCode( 0xE0008009, kIOFireWireNoChannels );
+		CaseErrorStringifyHardCode( 0xE000800A, kIOFireWireChannelNotAvailable );
+		CaseErrorStringifyHardCode( 0xE000800B, kIOFireWireSeparateBus );
+		CaseErrorStringifyHardCode( 0xE000800C, kIOFireWireBadSelfIDs );
+		CaseErrorStringifyHardCode( 0xE000800D, kIOFireWireLowCableVoltage );
+		CaseErrorStringifyHardCode( 0xE000800E, kIOFireWireInsufficientPower );
+		CaseErrorStringifyHardCode( 0xE000800F, kIOFireWireOutOfTLabels );
+		CaseErrorStringifyHardCode( 0xE0008101, kIOFireWireBogusDCLProgram );
+		CaseErrorStringifyHardCode( 0xE0008102, kIOFireWireTalkingAndListening );
+		CaseErrorStringifyHardCode( 0xE0008103, kIOFireWireHardwareSlept );
+		CaseErrorStringifyHardCode( 0xE00087D0, kIOFWMessageServiceIsRequestingClose );
+		CaseErrorStringifyHardCode( 0xE00087D1, kIOFWMessagePowerStateChanged );
+		CaseErrorStringifyHardCode( 0xE00087D2, kIOFWMessageTopologyChanged );
+
+		// IOKit USB Errors
+				
+		CaseErrorStringifyHardCode( 0xE0004061, kIOUSBUnknownPipeErr );
+		CaseErrorStringifyHardCode( 0xE0004060, kIOUSBTooManyPipesErr );
+		CaseErrorStringifyHardCode( 0xE000405F, kIOUSBNoAsyncPortErr );
+		CaseErrorStringifyHardCode( 0xE000405E, kIOUSBNotEnoughPipesErr );
+		CaseErrorStringifyHardCode( 0xE000405D, kIOUSBNotEnoughPowerErr );
+		CaseErrorStringifyHardCode( 0xE0004057, kIOUSBEndpointNotFound );
+		CaseErrorStringifyHardCode( 0xE0004056, kIOUSBConfigNotFound );
+		CaseErrorStringifyHardCode( 0xE0004051, kIOUSBTransactionTimeout );
+		CaseErrorStringifyHardCode( 0xE0004050, kIOUSBTransactionReturned );
+		CaseErrorStringifyHardCode( 0xE000404F, kIOUSBPipeStalled );
+		CaseErrorStringifyHardCode( 0xE000404E, kIOUSBInterfaceNotFound );
+		CaseErrorStringifyHardCode( 0xE000404D, kIOUSBLowLatencyBufferNotPreviouslyAllocated );
+		CaseErrorStringifyHardCode( 0xE000404C, kIOUSBLowLatencyFrameListNotPreviouslyAllocated );
+		CaseErrorStringifyHardCode( 0xE000404B, kIOUSBHighSpeedSplitError );
+		CaseErrorStringifyHardCode( 0xE0004010, kIOUSBLinkErr );
+		CaseErrorStringifyHardCode( 0xE000400F, kIOUSBNotSent2Err );
+		CaseErrorStringifyHardCode( 0xE000400E, kIOUSBNotSent1Err );
+		CaseErrorStringifyHardCode( 0xE000400D, kIOUSBBufferUnderrunErr );
+		CaseErrorStringifyHardCode( 0xE000400C, kIOUSBBufferOverrunErr );
+		CaseErrorStringifyHardCode( 0xE000400B, kIOUSBReserved2Err );
+		CaseErrorStringifyHardCode( 0xE000400A, kIOUSBReserved1Err );
+		CaseErrorStringifyHardCode( 0xE0004007, kIOUSBWrongPIDErr );
+		CaseErrorStringifyHardCode( 0xE0004006, kIOUSBPIDCheckErr );
+		CaseErrorStringifyHardCode( 0xE0004003, kIOUSBDataToggleErr );
+		CaseErrorStringifyHardCode( 0xE0004002, kIOUSBBitstufErr );
+		CaseErrorStringifyHardCode( 0xE0004001, kIOUSBCRCErr );
+	
+	#endif	// __MACH__
+
+		// Other Errors
+		
+		default:
+			s = NULL;
+			#if( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE )
+				if( inBuffer && ( inBufferSize > 0 ) )
+				{
+					DWORD		n;
+					
+					n = FormatMessageA( FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, NULL, (DWORD) inErrorCode, 
+						MAKELANGID( LANG_NEUTRAL, SUBLANG_DEFAULT ), buffer, sizeof( buffer ), NULL );
+					if( n > 0 )
+					{
+						// Remove any trailing CR's or LF's since some messages have them.
+						
+						while( ( n > 0 ) && isspace( ( (unsigned char *) buffer )[ n - 1 ] ) )
+						{
+							buffer[ --n ] = '\0';
+						}
+						s = buffer;
+					}
+				}
+			#endif
+			
+			if( !s )
+			{
+				#if( !TARGET_API_MAC_OSX_KERNEL && !TARGET_OS_WINDOWS_CE )
+					s = strerror( inErrorCode );
+				#endif
+				if( !s )
+				{
+					s = "<unknown error code>";
+				}
+			}
+			break;
+	}
+	
+	// Copy the string to the output buffer. If no buffer is supplied or it is empty, return an empty string.
+	
+	if( inBuffer && ( inBufferSize > 0 ) )
+	{
+		dst = inBuffer;
+		end = dst + ( inBufferSize - 1 );
+		while( ( ( end - dst ) > 0 ) && ( *s != '\0' ) )
+		{
+			*dst++ = *s++;
+		}
+		*dst = '\0';
+		s = inBuffer;
+	}
+	return( s );
+}
+
+//===========================================================================================================================
+//	DebugHexDump
+//===========================================================================================================================
+
+DEBUG_EXPORT size_t
+	DebugHexDump( 
+		DebugLevel		inLevel, 
+		int				inIndent, 
+		const char * 	inLabel, 
+		size_t 			inLabelSize, 
+		int				inLabelMinWidth, 
+		const char *	inType, 
+		size_t 			inTypeSize, 
+		const void *	inDataStart, 
+		const void *	inData, 
+		size_t 			inDataSize, 
+		DebugFlags	 	inFlags, 
+		char *			outBuffer, 
+		size_t			inBufferSize )
+{
+	static const char		kHexChars[] = "0123456789ABCDEF";
+	const uint8_t *			start;
+	const uint8_t *			src;
+	char *					dst;
+	char *					end;
+	size_t					n;
+	int						offset;
+	int						width;
+	const char *			newline;
+	char					separator[ 8 ];
+	char *					s;
+	
+	DEBUG_UNUSED( inType );
+	DEBUG_UNUSED( inTypeSize );
+	
+	// Set up the function-wide variables.
+	
+	if( inLabelSize == kSizeCString )
+	{
+		inLabelSize = strlen( inLabel );
+	}
+	start 	= (const uint8_t *) inData;
+	src 	= start;
+	dst		= outBuffer;
+	end		= dst + inBufferSize;
+	offset 	= (int)( (intptr_t) inData - (intptr_t) inDataStart );
+	width	= ( (int) inLabelSize > inLabelMinWidth ) ? (int) inLabelSize : inLabelMinWidth;
+	newline	= ( inFlags & kDebugFlagsNoNewLine ) ? "" : "\n";
+		
+	// Set up the separator string. This is used to insert spaces on subsequent "lines" when not using newlines.
+	
+	s = separator;
+	if( inFlags & kDebugFlagsNoNewLine )
+	{
+		if( inFlags & kDebugFlags8BitSeparator )
+		{
+			*s++ = ' ';
+		}
+		if( inFlags & kDebugFlags16BitSeparator )
+		{
+			*s++ = ' ';
+		}
+		if( !( inFlags & kDebugFlagsNo32BitSeparator ) )
+		{
+			*s++ = ' ';
+		}
+		check( ( (size_t)( s - separator ) ) < sizeof( separator ) );
+	}
+	*s = '\0';
+	
+	for( ;; )
+	{
+		char		prefixString[ 32 ];
+		char		hexString[ 64 ];
+		char		asciiString[ 32 ];
+		char		byteCountString[ 32 ];
+		int			c;
+		size_t		chunkSize;
+		size_t		i;
+		
+		// If this is a label-only item (i.e. no data), print the label (accounting for prefix string spacing) and exit.
+		
+		if( inDataSize == 0 )
+		{
+			if( inLabel && ( inLabelSize > 0 ) )
+			{
+				width = 0;
+				if( !( inFlags & kDebugFlagsNoAddress ) )
+				{
+					width += 8;			// "00000000"
+					if( !( inFlags & kDebugFlagsNoOffset ) )
+					{
+						width += 1;		// "+"
+					}
+				}
+				if( inFlags & kDebugFlags32BitOffset )
+				{
+					width += 8;			// "00000000"
+				}
+				else if( !( inFlags & kDebugFlagsNoOffset ) )
+				{
+					width += 4;			// "0000"
+				}
+				
+				if( outBuffer )
+				{
+					dst += DebugSNPrintF( dst, (size_t)( end - dst ), "%*s" "%-*.*s" "%.*s" "%s", 
+						width, "", 
+						( width > 0 ) ? ": " : "", 
+						width, (int) inLabelSize, inLabel, 
+						newline );
+				}
+				else
+				{
+					dst += DebugPrintF( inLevel, "%*s" "%-*.*s" "%.*s" "%s", 
+						width, "", 
+						( width > 0 ) ? ": " : "", 
+						width, (int) inLabelSize, inLabel, 
+						newline );
+				}
+			}
+			break;
+		}
+		
+		// Build the prefix string. It will be in one of the following formats:
+		//
+		// 1) "00000000+0000[0000]"	(address and offset)
+		// 2) "00000000"			(address only)
+		// 3) "0000[0000]"			(offset only)
+		// 4) ""					(no address or offset)
+		//
+		// Note: If we're printing multiple "lines", but not printing newlines, a space is used to separate.
+		
+		s = prefixString;
+		if( !( inFlags & kDebugFlagsNoAddress ) )
+		{
+			*s++ = kHexChars[ ( ( (uintptr_t) src ) >> 28 ) & 0xF ];
+			*s++ = kHexChars[ ( ( (uintptr_t) src ) >> 24 ) & 0xF ];
+			*s++ = kHexChars[ ( ( (uintptr_t) src ) >> 20 ) & 0xF ];
+			*s++ = kHexChars[ ( ( (uintptr_t) src ) >> 16 ) & 0xF ];
+			*s++ = kHexChars[ ( ( (uintptr_t) src ) >> 12 ) & 0xF ];
+			*s++ = kHexChars[ ( ( (uintptr_t) src ) >>  8 ) & 0xF ];
+			*s++ = kHexChars[ ( ( (uintptr_t) src ) >>  4 ) & 0xF ];
+			*s++ = kHexChars[   ( (uintptr_t) src )         & 0xF ];
+			
+			if( !( inFlags & kDebugFlagsNoOffset ) )
+			{
+				*s++ = '+';
+			}
+		}
+		if( !( inFlags & kDebugFlagsNoOffset ) )
+		{
+			if( inFlags & kDebugFlags32BitOffset )
+			{
+				*s++ = kHexChars[ ( offset >> 28 ) & 0xF ];
+				*s++ = kHexChars[ ( offset >> 24 ) & 0xF ];
+				*s++ = kHexChars[ ( offset >> 20 ) & 0xF ];
+				*s++ = kHexChars[ ( offset >> 16 ) & 0xF ];
+			}
+			*s++ = kHexChars[ ( offset >> 12 ) & 0xF ];
+			*s++ = kHexChars[ ( offset >>  8 ) & 0xF ];
+			*s++ = kHexChars[ ( offset >>  4 ) & 0xF ];
+			*s++ = kHexChars[   offset         & 0xF ];
+		}
+		if( s != prefixString )
+		{
+			*s++ = ':';
+			*s++ = ' ';
+		}
+		check( ( (size_t)( s - prefixString ) ) < sizeof( prefixString ) );
+		*s = '\0';
+		
+		// Build a hex string with a optional spaces after every 1, 2, and/or 4 bytes to make it easier to read.
+		// Optionally pads the hex string with space to fill the full 16 byte range (so it lines up).
+		
+		s = hexString;
+		chunkSize = ( inDataSize < 16 ) ? inDataSize : 16;
+		n = ( inFlags & kDebugFlagsNo16ByteHexPad ) ? chunkSize : 16;
+		for( i = 0; i < n; ++i )
+		{
+			if( ( inFlags & kDebugFlags8BitSeparator ) && ( i > 0 ) )
+			{
+				*s++ = ' ';
+			}
+			if( ( inFlags & kDebugFlags16BitSeparator ) && ( i > 0 ) && ( ( i % 2 ) == 0 ) )
+			{
+				*s++ = ' ';
+			}
+			if( !( inFlags & kDebugFlagsNo32BitSeparator ) && ( i > 0 ) && ( ( i % 4 ) == 0 ) )
+			{
+				*s++ = ' ';
+			}
+			if( i < chunkSize )
+			{
+				*s++ = kHexChars[ src[ i ] >> 4   ];
+				*s++ = kHexChars[ src[ i ] &  0xF ];
+			}
+			else
+			{
+				*s++ = ' ';
+				*s++ = ' ';
+			}
+		}
+		check( ( (size_t)( s - hexString ) ) < sizeof( hexString ) );
+		*s = '\0';
+		
+		// Build a string with the ASCII version of the data (replaces non-printable characters with '^').
+		// Optionally pads the string with '`' to fill the full 16 byte range (so it lines up).
+		
+		s = asciiString;
+		if( !( inFlags & kDebugFlagsNoASCII ) )
+		{
+			*s++ = ' ';
+			*s++ = '|';
+			for( i = 0; i < n; ++i )
+			{
+				if( i < chunkSize )
+				{
+					c = src[ i ];
+					if( !DebugIsPrint( c ) )
+					{
+						c = '^';
+					}
+				}
+				else
+				{
+					c = '`';
+				}
+				*s++ = (char) c;
+			}
+			*s++ = '|';
+			check( ( (size_t)( s - asciiString ) ) < sizeof( asciiString ) );
+		}
+		*s = '\0';
+		
+		// Build a string indicating how bytes are in the hex dump. Only printed on the first line.
+		
+		s = byteCountString;
+		if( !( inFlags & kDebugFlagsNoByteCount ) )
+		{
+			if( src == start )
+			{
+				s += DebugSNPrintF( s, sizeof( byteCountString ), " (%d bytes)", (int) inDataSize );
+			}
+		}
+		check( ( (size_t)( s - byteCountString ) ) < sizeof( byteCountString ) );
+		*s = '\0';
+		
+		// Build the entire line from all the pieces we've previously built.
+			
+		if( outBuffer )
+		{
+			if( src == start )
+			{
+				dst += DebugSNPrintF( dst, (size_t)( end - dst ), 
+					"%*s"		// Indention
+					"%s" 		// Separator (only if needed)
+					"%s" 		// Prefix
+					"%-*.*s"	// Label
+					"%s"		// Separator
+					"%s"		// Hex
+					"%s"		// ASCII
+					"%s"		// Byte Count
+					"%s", 		// Newline
+					inIndent, "", 
+					( src != start ) ? separator : "", 
+					prefixString, 
+					width, (int) inLabelSize, inLabel ? inLabel : "", 
+					( width > 0 ) ? " " : "", 
+					hexString, 
+					asciiString, 
+					byteCountString, 
+					newline );
+			}
+			else
+			{
+				dst += DebugSNPrintF( dst, (size_t)( end - dst ), 
+					"%*s"		// Indention
+					"%s" 		// Separator (only if needed)
+					"%s" 		// Prefix
+					"%*s"		// Label Spacing
+					"%s"		// Separator
+					"%s"		// Hex
+					"%s"		// ASCII
+					"%s"		// Byte Count
+					"%s", 		// Newline
+					inIndent, "", 
+					( src != start ) ? separator : "", 
+					prefixString, 
+					width, "", 
+					( width > 0 ) ? " " : "", 
+					hexString, 
+					asciiString, 
+					byteCountString, 
+					newline );
+			}
+		}
+		else
+		{
+			if( src == start )
+			{
+				dst += DebugPrintF( inLevel, 
+					"%*s"		// Indention
+					"%s" 		// Separator (only if needed)
+					"%s" 		// Prefix
+					"%-*.*s"	// Label
+					"%s"		// Separator
+					"%s"		// Hex
+					"%s"		// ASCII
+					"%s"		// Byte Count
+					"%s", 		// Newline
+					inIndent, "", 
+					( src != start ) ? separator : "", 
+					prefixString, 
+					width, (int) inLabelSize, inLabel, 
+					( width > 0 ) ? " " : "", 
+					hexString, 
+					asciiString, 
+					byteCountString, 
+					newline );
+			}
+			else
+			{
+				dst += DebugPrintF( inLevel, 
+					"%*s"		// Indention
+					"%s" 		// Separator (only if needed)
+					"%s" 		// Prefix
+					"%*s"		// Label Spacing
+					"%s"		// Separator
+					"%s"		// Hex
+					"%s"		// ASCII
+					"%s"		// Byte Count
+					"%s", 		// Newline
+					inIndent, "", 
+					( src != start ) ? separator : "", 
+					prefixString, 
+					width, "", 
+					( width > 0 ) ? " " : "", 
+					hexString, 
+					asciiString, 
+					byteCountString, 
+					newline );
+			}
+		}
+		
+		// Move to the next chunk. Exit if there is no more data.
+		
+		offset		+= (int) chunkSize;
+		src 		+= chunkSize;
+		inDataSize	-= chunkSize;
+		if( inDataSize == 0 )
+		{
+			break;
+		}
+	}
+	
+	// Note: The "dst - outBuffer" size calculation works even if "outBuffer" is NULL because it's all relative.
+	
+	return( (size_t)( dst - outBuffer ) );
+}
+
+//===========================================================================================================================
+//	DebugNumVersionToString
+//===========================================================================================================================
+
+static char *	DebugNumVersionToString( uint32_t inVersion, char *inString )
+{
+	char *		s;
+	uint8_t		majorRev;
+	uint8_t		minor;
+	uint8_t		bugFix;
+	uint8_t		stage;
+	uint8_t		revision;
+	
+	check( inString );
+	
+	majorRev 	= (uint8_t)( ( inVersion >> 24 ) & 0xFF );
+	minor		= (uint8_t)( ( inVersion >> 20 ) & 0x0F );
+	bugFix		= (uint8_t)( ( inVersion >> 16 ) & 0x0F );
+	stage 		= (uint8_t)( ( inVersion >>  8 ) & 0xFF );
+	revision 	= (uint8_t)(   inVersion         & 0xFF );
+	
+	// Convert the major, minor, and bugfix numbers.
+	
+	s  = inString;
+	s += sprintf( s, "%u", majorRev );
+	s += sprintf( s, ".%u", minor );
+	if( bugFix != 0 )
+	{
+		s += sprintf( s, ".%u", bugFix );
+	}
+	
+	// Convert the version stage and non-release revision number.
+	
+	switch( stage )
+	{
+		case kVersionStageDevelopment:
+			s += sprintf( s, "d%u", revision );
+			break;
+		
+		case kVersionStageAlpha:
+			s += sprintf( s, "a%u", revision );
+			break;
+		
+		case kVersionStageBeta:
+			s += sprintf( s, "b%u", revision );
+			break;
+		
+		case kVersionStageFinal:
+			
+			// A non-release revision of zero is a special case indicating the software is GM (at the golden master 
+			// stage) and therefore, the non-release revision should not be added to the string.
+			
+			if( revision != 0 )
+			{
+				s += sprintf( s, "f%u", revision );
+			}
+			break;
+		
+		default:
+			dlog( kDebugLevelError, "invalid NumVersion stage (0x%02X)\n", stage );
+			break;
+	}
+	return( inString );
+}
+
+//===========================================================================================================================
+//	DebugTaskLevel
+//===========================================================================================================================
+
+DEBUG_EXPORT uint32_t	DebugTaskLevel( void )
+{
+	uint32_t		level;
+	
+	level = 0;
+	
+#if( TARGET_OS_VXWORKS )
+	if( intContext() )
+	{
+		level |= ( ( 1 << kDebugInterruptLevelShift ) & kDebugInterruptLevelMask );
+	}
+#endif
+	
+	return( level );
+}
+
+#if( TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE )
+//===========================================================================================================================
+//	DebugWinEnableConsole
+//===========================================================================================================================
+
+#pragma warning( disable:4311 )
+
+static void	DebugWinEnableConsole( void )
+{
+	static bool		sConsoleEnabled = false;
+	BOOL			result;
+	int				fileHandle;
+	FILE *			file;
+	int				err;
+	
+	if( sConsoleEnabled )
+	{
+		goto exit;
+	}
+	
+	// Create console window.
+	
+	result = AllocConsole();
+	require_quiet( result, exit );
+
+	// Redirect stdin to the console stdin.
+	
+	fileHandle = _open_osfhandle( (long) GetStdHandle( STD_INPUT_HANDLE ), _O_TEXT );
+	
+	#if( defined( __MWERKS__ ) )
+		file = __handle_reopen( (unsigned long) fileHandle, "r", stdin );
+		require_quiet( file, exit );
+	#else
+		file = _fdopen( fileHandle, "r" );
+		require_quiet( file, exit );
+	
+		*stdin = *file;
+	#endif
+	
+	err = setvbuf( stdin, NULL, _IONBF, 0 );
+	require_noerr_quiet( err, exit );
+	
+	// Redirect stdout to the console stdout.
+		
+	fileHandle = _open_osfhandle( (long) GetStdHandle( STD_OUTPUT_HANDLE ), _O_TEXT );
+	
+	#if( defined( __MWERKS__ ) )
+		file = __handle_reopen( (unsigned long) fileHandle, "w", stdout );
+		require_quiet( file, exit );
+	#else
+		file = _fdopen( fileHandle, "w" );
+		require_quiet( file, exit );
+		
+		*stdout = *file;
+	#endif
+	
+	err = setvbuf( stdout, NULL, _IONBF, 0 );
+	require_noerr_quiet( err, exit );
+	
+	// Redirect stderr to the console stdout.
+	
+	fileHandle = _open_osfhandle( (long) GetStdHandle( STD_OUTPUT_HANDLE ), _O_TEXT );
+	
+	#if( defined( __MWERKS__ ) )
+		file = __handle_reopen( (unsigned long) fileHandle, "w", stderr );
+		require_quiet( file, exit );
+	#else
+		file = _fdopen( fileHandle, "w" );
+		require_quiet( file, exit );
+	
+		*stderr = *file;
+	#endif
+	
+	err = setvbuf( stderr, NULL, _IONBF, 0 );
+	require_noerr_quiet( err, exit );
+	
+	sConsoleEnabled = true;
+	
+exit:
+	return;
+}
+
+#pragma warning( default:4311 )
+
+#endif	// TARGET_OS_WIN32 && !TARGET_OS_WINDOWS_CE
+
+#if( TARGET_OS_WIN32 )
+//===========================================================================================================================
+//	DebugWinCharToTCharString
+//===========================================================================================================================
+
+static TCHAR *
+	DebugWinCharToTCharString( 
+		const char *	inCharString, 
+		size_t 			inCharCount, 
+		TCHAR *			outTCharString, 
+		size_t 			inTCharCountMax, 
+		size_t *		outTCharCount )
+{
+	const char *		src;
+	TCHAR *				dst;
+	TCHAR *				end;
+	
+	if( inCharCount == kSizeCString )
+	{
+		inCharCount = strlen( inCharString );
+	}
+	src = inCharString;
+	dst = outTCharString;
+	if( inTCharCountMax > 0 )
+	{
+		inTCharCountMax -= 1;
+		if( inTCharCountMax > inCharCount )
+		{
+			inTCharCountMax = inCharCount;
+		}
+		
+		end = dst + inTCharCountMax;
+		while( dst < end )
+		{
+			*dst++ = (TCHAR) *src++;
+		}
+		*dst = 0;
+	}
+	if( outTCharCount )
+	{
+		*outTCharCount = (size_t)( dst - outTCharString );
+	}
+	return( outTCharString );
+}
+#endif
+
+#if 0
+#pragma mark -
+#pragma mark == Debugging ==
+#endif
+
+//===========================================================================================================================
+//	DebugServicesTest
+//===========================================================================================================================
+
+DEBUG_EXPORT OSStatus	DebugServicesTest( void )
+{
+	OSStatus		err;
+	char			s[ 512 ];
+	uint8_t *		p;
+	uint8_t			data[] = 
+	{
+		0x11, 0x22, 0x33, 0x44, 
+		0x55, 0x66, 
+		0x77, 0x88, 0x99, 0xAA, 
+		0xBB, 0xCC, 0xDD, 
+		0xEE,
+		0xFF, 
+		0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A, 
+		0x10, 0x20, 0x30, 0x40, 0x50, 0x60, 0x70, 0x80, 0x90, 0xA0, 
+		0x11, 0x21, 0x31, 0x41, 0x51, 0x61, 0x71, 0x81, 0x91, 0xA1 
+	};
+	
+	debug_initialize( kDebugOutputTypeMetaConsole );	
+	
+	// check's
+	
+	check( 0 && "SHOULD SEE: check" );
+	check( 1 && "SHOULD *NOT* SEE: check (valid)" );
+	check_string( 0, "SHOULD SEE: check_string" );
+	check_string( 1, "SHOULD *NOT* SEE: check_string (valid)" );
+	check_noerr( -123 );
+	check_noerr( 10038 );
+	check_noerr( 22 );
+	check_noerr( 0 );
+	check_noerr_string( -6712, "SHOULD SEE: check_noerr_string" );
+	check_noerr_string( 0, "SHOULD *NOT* SEE: check_noerr_string (valid)" );
+	check_translated_errno( 0 >= 0 && "SHOULD *NOT* SEE", -384, -999 );
+	check_translated_errno( -1 >= 0 && "SHOULD SEE", -384, -999 );
+	check_translated_errno( -1 >= 0 && "SHOULD SEE", 0, -999 );
+	check_ptr_overlap( "SHOULD *NOT* SEE" ? 10 : 0, 10, 22, 10 );
+	check_ptr_overlap( "SHOULD SEE" ? 10 : 0, 10,  5, 10 );
+	check_ptr_overlap( "SHOULD SEE" ? 10 : 0, 10, 12,  6 );
+	check_ptr_overlap( "SHOULD SEE" ? 12 : 0,  6, 10, 10 );
+	check_ptr_overlap( "SHOULD SEE" ? 12 : 0, 10, 10, 10 );
+	check_ptr_overlap( "SHOULD *NOT* SEE" ? 22 : 0, 10, 10, 10 );
+	check_ptr_overlap( "SHOULD *NOT* SEE" ? 10 : 0, 10, 20, 10 );
+	check_ptr_overlap( "SHOULD *NOT* SEE" ? 20 : 0, 10, 10, 10 );
+		
+	// require's
+	
+	require( 0 && "SHOULD SEE", require1 );
+	{ err = kResponseErr; goto exit; }
+require1:
+	require( 1 && "SHOULD *NOT* SEE", require2 );
+	goto require2Good;
+require2:
+	{ err = kResponseErr; goto exit; }
+require2Good:
+	require_string( 0 && "SHOULD SEE", require3, "SHOULD SEE: require_string" );
+	{ err = kResponseErr; goto exit; }
+require3:
+	require_string( 1 && "SHOULD *NOT* SEE", require4, "SHOULD *NOT* SEE: require_string (valid)" );
+	goto require4Good;
+require4:
+	{ err = kResponseErr; goto exit; }
+require4Good:
+	require_quiet( 0 && "SHOULD SEE", require5 );
+	{ err = kResponseErr; goto exit; }
+require5:
+	require_quiet( 1 && "SHOULD *NOT* SEE", require6 );
+	goto require6Good;
+require6:
+	{ err = kResponseErr; goto exit; }
+require6Good:
+	require_noerr( -1, require7 );
+	{ err = kResponseErr; goto exit; }
+require7:
+	require_noerr( 0, require8 );
+	goto require8Good;
+require8:
+	{ err = kResponseErr; goto exit; }
+require8Good:
+	require_noerr_string( -2, require9, "SHOULD SEE: require_noerr_string");
+	{ err = kResponseErr; goto exit; }
+require9:
+	require_noerr_string( 0, require10, "SHOULD *NOT* SEE: require_noerr_string (valid)" );
+	goto require10Good;
+require10:
+	{ err = kResponseErr; goto exit; }
+require10Good:
+	require_noerr_action_string( -3, require11, dlog( kDebugLevelMax, "action 1 (expected)\n" ), "require_noerr_action_string" );
+	{ err = kResponseErr; goto exit; }
+require11:
+	require_noerr_action_string( 0, require12, dlog( kDebugLevelMax, "action 2\n" ), "require_noerr_action_string (valid)" );
+	goto require12Good;
+require12:
+	{ err = kResponseErr; goto exit; }
+require12Good:
+	require_noerr_quiet( -4, require13 );
+	{ err = kResponseErr; goto exit; }
+require13:
+	require_noerr_quiet( 0, require14 );
+	goto require14Good;
+require14:
+	{ err = kResponseErr; goto exit; }
+require14Good:
+	require_noerr_action( -5, require15, dlog( kDebugLevelMax, "SHOULD SEE: action 3 (expected)\n" ) );
+	{ err = kResponseErr; goto exit; }
+require15:
+	require_noerr_action( 0, require16, dlog( kDebugLevelMax, "SHOULD *NOT* SEE: action 4\n" ) );
+	goto require16Good;
+require16:
+	{ err = kResponseErr; goto exit; }
+require16Good:
+	require_noerr_action_quiet( -4, require17, dlog( kDebugLevelMax, "SHOULD SEE: action 5 (expected)\n" ) );
+	{ err = kResponseErr; goto exit; }
+require17:
+	require_noerr_action_quiet( 0, require18, dlog( kDebugLevelMax, "SHOULD *NOT* SEE: action 6\n" ) );
+	goto require18Good;
+require18:
+	{ err = kResponseErr; goto exit; }
+require18Good:
+	require_action( 0 && "SHOULD SEE", require19, dlog( kDebugLevelMax, "SHOULD SEE: action 7 (expected)\n" ) );
+	{ err = kResponseErr; goto exit; }
+require19:
+	require_action( 1 && "SHOULD *NOT* SEE", require20, dlog( kDebugLevelMax, "SHOULD *NOT* SEE: action 8\n" ) );
+	goto require20Good;
+require20:
+	{ err = kResponseErr; goto exit; }
+require20Good:
+	require_action_quiet( 0, require21, dlog( kDebugLevelMax, "SHOULD SEE: action 9 (expected)\n" ) );
+	{ err = kResponseErr; goto exit; }
+require21:
+	require_action_quiet( 1, require22, dlog( kDebugLevelMax, "SHOULD *NOT* SEE: action 10\n" ) );
+	goto require22Good;
+require22:
+	{ err = kResponseErr; goto exit; }
+require22Good:
+	require_action_string( 0, require23, dlog( kDebugLevelMax, "SHOULD SEE: action 11 (expected)\n" ), "SHOULD SEE: require_action_string" );
+	{ err = kResponseErr; goto exit; }
+require23:
+	require_action_string( 1, require24, dlog( kDebugLevelMax, "SHOULD *NOT* SEE: action 12\n" ), "SHOULD *NOT* SEE: require_action_string" );
+	goto require24Good;
+require24:
+	{ err = kResponseErr; goto exit; }
+require24Good:
+
+#if( defined( __MWERKS__ )  )
+	#if( defined( __cplusplus ) && __option( exceptions ) )
+		#define COMPILER_HAS_EXCEPTIONS		1
+	#else
+		#define COMPILER_HAS_EXCEPTIONS		0
+	#endif
+#else
+	#if( defined( __cplusplus ) )
+		#define COMPILER_HAS_EXCEPTIONS		1
+	#else
+		#define COMPILER_HAS_EXCEPTIONS		0
+	#endif
+#endif
+
+#if( COMPILER_HAS_EXCEPTIONS )
+	try
+	{
+		require_throw( 1 && "SHOULD *NOT* SEE" );
+		require_throw( 0 && "SHOULD SEE" );
+	}
+	catch( ... )
+	{
+		goto require26Good;
+	}
+	{ err = kResponseErr; goto exit; }
+require26Good:
+#endif
+
+	// translate_errno
+	
+	err = translate_errno( 1 != -1, -123, -567 );
+	require( ( err == 0 ) && "SHOULD *NOT* SEE", exit );
+	
+	err = translate_errno( -1 != -1, -123, -567 );
+	require( ( err == -123 ) && "SHOULD *NOT* SEE", exit );
+	
+	err = translate_errno( -1 != -1, 0, -567 );
+	require( ( err == -567 ) && "SHOULD *NOT* SEE", exit );
+
+	// debug_string
+	
+	debug_string( "debug_string" );
+	
+	// DebugSNPrintF
+	
+	DebugSNPrintF( s, sizeof( s ), "%d", 1234 );
+	require_action( strcmp( s, "1234" ) == 0, exit, err = -1 );
+	
+	DebugSNPrintF( s, sizeof( s ), "%X", 0x2345 );
+	require_action( strcmp( s, "2345" ) == 0, exit, err = -1 );
+	
+	DebugSNPrintF( s, sizeof( s ), "%#s", "\05test" );
+	require_action( strcmp( s, "test" ) == 0, exit, err = -1 );
+	
+	DebugSNPrintF( s, sizeof( s ), "%##s", "\03www\05apple\03com" );
+	require_action( strcmp( s, "www.apple.com." ) == 0, exit, err = -1 );
+	
+	DebugSNPrintF( s, sizeof( s ), "%ld", (long) INT32_C( 2147483647 ) );
+	require_action( strcmp( s, "2147483647" ) == 0, exit, err = -1 );
+	
+	DebugSNPrintF( s, sizeof( s ), "%lu", (unsigned long) UINT32_C( 4294967295 ) );
+	require_action( strcmp( s, "4294967295" ) == 0, exit, err = -1 );
+	
+	#if( TYPE_LONGLONG_NATIVE )
+		DebugSNPrintF( s, sizeof( s ), "%lld", (long_long_compat) INT64_C( 9223372036854775807 ) );
+		require_action( strcmp( s, "9223372036854775807" ) == 0, exit, err = -1 );
+		
+		DebugSNPrintF( s, sizeof( s ), "%lld", (long_long_compat) INT64_C( -9223372036854775807 ) );
+		require_action( strcmp( s, "-9223372036854775807" ) == 0, exit, err = -1 );
+		
+		DebugSNPrintF( s, sizeof( s ), "%llu", (unsigned_long_long_compat) UINT64_C( 18446744073709551615 ) );
+		require_action( strcmp( s, "18446744073709551615" ) == 0, exit, err = -1 );
+	#endif
+	
+	DebugSNPrintF( s, sizeof( s ), "%lb", (unsigned long) binary_32( 01111011, 01111011, 01111011, 01111011 ) );
+	require_action( strcmp( s, "1111011011110110111101101111011" ) == 0, exit, err = -1 );
+	
+	DebugSNPrintF( s, sizeof( s ), "%C", 0x41624364 );	// 'AbCd'
+	require_action( strcmp( s, "AbCd" ) == 0, exit, err = -1 );
+	
+	#if( defined( MDNS_DEBUGMSGS ) )
+	{
+		mDNSAddr		maddr;
+		
+		memset( &maddr, 0, sizeof( maddr ) );
+		maddr.type = mDNSAddrType_IPv4;
+		maddr.ip.v4.b[ 0 ] = 127;
+		maddr.ip.v4.b[ 1 ] = 0;
+		maddr.ip.v4.b[ 2 ] = 0;
+		maddr.ip.v4.b[ 3 ] = 1;
+		DebugSNPrintF( s, sizeof( s ), "%#a", &maddr );
+		require_action( strcmp( s, "127.0.0.1" ) == 0, exit, err = -1 );
+		
+		memset( &maddr, 0, sizeof( maddr ) );
+		maddr.type = mDNSAddrType_IPv6;
+		maddr.ip.v6.b[  0 ]	= 0xFE;
+		maddr.ip.v6.b[  1 ]	= 0x80;
+		maddr.ip.v6.b[ 15 ]	= 0x01;
+		DebugSNPrintF( s, sizeof( s ), "%#a", &maddr );
+		require_action( strcmp( s, "FE80:0000:0000:0000:0000:0000:0000:0001" ) == 0, exit, err = -1 );
+	}
+	#endif
+	
+	#if( AF_INET )
+	{
+		struct sockaddr_in		sa4;
+		
+		memset( &sa4, 0, sizeof( sa4 ) );
+		sa4.sin_family 		= AF_INET;
+		p 					= (uint8_t *) &sa4.sin_port;
+		p[ 0 ] 				= (uint8_t)( ( 80 >> 8 ) & 0xFF );
+		p[ 1 ] 				= (uint8_t)(   80        & 0xFF );
+		p 					= (uint8_t *) &sa4.sin_addr.s_addr;
+		p[ 0 ] 				= (uint8_t)( ( INADDR_LOOPBACK >> 24 ) & 0xFF );
+		p[ 1 ] 				= (uint8_t)( ( INADDR_LOOPBACK >> 16 ) & 0xFF );
+		p[ 2 ] 				= (uint8_t)( ( INADDR_LOOPBACK >>  8 ) & 0xFF );
+		p[ 3 ] 				= (uint8_t)(   INADDR_LOOPBACK         & 0xFF );
+		DebugSNPrintF( s, sizeof( s ), "%##a", &sa4 );
+		require_action( strcmp( s, "127.0.0.1:80" ) == 0, exit, err = -1 );
+	}
+	#endif
+	
+	#if( AF_INET6 )
+	{
+		struct sockaddr_in6		sa6;
+		
+		memset( &sa6, 0, sizeof( sa6 ) );
+		sa6.sin6_family 			= AF_INET6;
+		p 							= (uint8_t *) &sa6.sin6_port;
+		p[ 0 ] 						= (uint8_t)( ( 80 >> 8 ) & 0xFF );
+		p[ 1 ] 						= (uint8_t)(   80        & 0xFF );
+		sa6.sin6_addr.s6_addr[  0 ]	= 0xFE;
+		sa6.sin6_addr.s6_addr[  1 ]	= 0x80;
+		sa6.sin6_addr.s6_addr[ 15 ]	= 0x01;
+		sa6.sin6_scope_id			= 2;
+		DebugSNPrintF( s, sizeof( s ), "%##a", &sa6 );
+		require_action( strcmp( s, "[FE80:0000:0000:0000:0000:0000:0000:0001%2]:80" ) == 0, exit, err = -1 );
+	}
+	#endif
+	
+	// Unicode
+
+	DebugSNPrintF(s, sizeof(s), "%.*s", 4, "tes" );
+	require_action( strcmp( s, "tes" ) == 0, exit, err = kResponseErr );
+	
+	DebugSNPrintF(s, sizeof(s), "%.*s", 4, "test" );
+	require_action( strcmp( s, "test" ) == 0, exit, err = kResponseErr );
+	
+	DebugSNPrintF(s, sizeof(s), "%.*s", 4, "testing" );
+	require_action( strcmp( s, "test" ) == 0, exit, err = kResponseErr );
+	
+	DebugSNPrintF(s, sizeof(s), "%.*s", 4, "te\xC3\xA9" );
+	require_action( strcmp( s, "te\xC3\xA9" ) == 0, exit, err = kResponseErr );
+	
+	DebugSNPrintF(s, sizeof(s), "%.*s", 4, "te\xC3\xA9ing" );
+	require_action( strcmp( s, "te\xC3\xA9" ) == 0, exit, err = kResponseErr );
+	
+	DebugSNPrintF(s, sizeof(s), "%.*s", 4, "tes\xC3\xA9ing" );
+	require_action( strcmp( s, "tes" ) == 0, exit, err = kResponseErr );
+	
+	DebugSNPrintF(s, sizeof(s), "%.*s", 4, "t\xed\x9f\xbf" );
+	require_action( strcmp( s, "t\xed\x9f\xbf" ) == 0, exit, err = kResponseErr );
+	
+	DebugSNPrintF(s, sizeof(s), "%.*s", 4, "t\xed\x9f\xbfing" );
+	require_action( strcmp( s, "t\xed\x9f\xbf" ) == 0, exit, err = kResponseErr );
+	
+	DebugSNPrintF(s, sizeof(s), "%.*s", 4, "te\xed\x9f\xbf" );
+	require_action( strcmp( s, "te" ) == 0, exit, err = kResponseErr );
+	
+	DebugSNPrintF(s, sizeof(s), "%.*s", 4, "te\xed\x9f\xbfing" );
+	require_action( strcmp( s, "te" ) == 0, exit, err = kResponseErr );
+	
+	DebugSNPrintF(s, sizeof(s), "%.*s", 7, "te\xC3\xA9\xed\x9f\xbfing" );
+	require_action( strcmp( s, "te\xC3\xA9\xed\x9f\xbf" ) == 0, exit, err = kResponseErr );
+	
+	DebugSNPrintF(s, sizeof(s), "%.*s", 6, "te\xC3\xA9\xed\x9f\xbfing" );
+	require_action( strcmp( s, "te\xC3\xA9" ) == 0, exit, err = kResponseErr );
+	
+	DebugSNPrintF(s, sizeof(s), "%.*s", 5, "te\xC3\xA9\xed\x9f\xbfing" );
+	require_action( strcmp( s, "te\xC3\xA9" ) == 0, exit, err = kResponseErr );
+
+	#if( TARGET_RT_BIG_ENDIAN )
+		DebugSNPrintF( s, sizeof( s ), "%S", "\x00" "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" "\x00" );
+		require_action( strcmp( s, "abcd" ) == 0, exit, err = -1 );
+	#else
+		DebugSNPrintF( s, sizeof( s ), "%S", "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" "\x00" "\x00" );
+		require_action( strcmp( s, "abcd" ) == 0, exit, err = -1 );
+	#endif
+	
+	DebugSNPrintF( s, sizeof( s ), "%S", 
+		"\xFE\xFF" "\x00" "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" "\x00" );	// Big Endian BOM
+	require_action( strcmp( s, "abcd" ) == 0, exit, err = -1 );
+	
+	DebugSNPrintF( s, sizeof( s ), "%S", 
+		"\xFF\xFE" "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" "\x00" "\x00" );	// Little Endian BOM
+	require_action( strcmp( s, "abcd" ) == 0, exit, err = -1 );
+	
+	DebugSNPrintF( s, sizeof( s ), "%#S", "\x00" "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" "\x00" );	// Big Endian
+	require_action( strcmp( s, "abcd" ) == 0, exit, err = -1 );
+	
+	DebugSNPrintF( s, sizeof( s ), "%##S", "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" "\x00" "\x00" );	// Little Endian
+	require_action( strcmp( s, "abcd" ) == 0, exit, err = -1 );
+	
+	DebugSNPrintF( s, sizeof( s ), "%.*S", 
+		4, "\xFE\xFF" "\x00" "a" "\x00" "b" "\x00" "c" "\x00" "d" );	// Big Endian BOM
+	require_action( strcmp( s, "abc" ) == 0, exit, err = -1 );
+	
+	DebugSNPrintF( s, sizeof( s ), "%.*S", 
+		4, "\xFF\xFE" "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" );	// Little Endian BOM
+	require_action( strcmp( s, "abc" ) == 0, exit, err = -1 );
+	
+	#if( TARGET_RT_BIG_ENDIAN )
+		DebugSNPrintF( s, sizeof( s ), "%.*S", 3, "\x00" "a" "\x00" "b" "\x00" "c" "\x00" "d" );
+		require_action( strcmp( s, "abc" ) == 0, exit, err = -1 );
+	#else
+		DebugSNPrintF( s, sizeof( s ), "%.*S", 3, "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" );
+		require_action( strcmp( s, "abc" ) == 0, exit, err = -1 );
+	#endif
+	
+	DebugSNPrintF( s, sizeof( s ), "%#.*S", 3, "\x00" "a" "\x00" "b" "\x00" "c" "\x00" "d" );	// Big Endian
+	require_action( strcmp( s, "abc" ) == 0, exit, err = -1 );
+	
+	DebugSNPrintF( s, sizeof( s ), "%##.*S", 3, "a" "\x00" "b" "\x00" "c" "\x00" "d" "\x00" );	// Little Endian
+	require_action( strcmp( s, "abc" ) == 0, exit, err = -1 );
+	
+	// Misc
+	
+	DebugSNPrintF( s, sizeof( s ), "%U", "\x10\xb8\xa7\x6b" "\xad\x9d" "\xd1\x11" "\x80\xb4" "\x00\xc0\x4f\xd4\x30\xc8" );
+	require_action( strcmp( s, "6ba7b810-9dad-11d1-80b4-00c04fd430c8" ) == 0, exit, err = -1 );
+	
+	DebugSNPrintF( s, sizeof( s ), "%m", 0 );
+	require_action( strcmp( s, "no error" ) == 0, exit, err = -1 );
+	
+	DebugSNPrintF( s, sizeof( s ), "%lm", (long) 0 );
+	require_action( strcmp( s, "no error" ) == 0, exit, err = -1 );
+	
+	DebugSNPrintF( s, sizeof( s ), "\"%H\"", "\x6b\xa7\xb8\x10\x9d\xad\x11\xd1\x80\xb4\x00\xc0\x4f\xd4\x30\xc8", 16, 16 );
+	DebugPrintF( kDebugLevelMax, "%s\n\n", s );
+	
+	DebugSNPrintF( s, sizeof( s ), "\"%H\"", 
+		"\x6b\xa7\xb8\x10\x9d\xad\x11\xd1\x80\xb4\x00\xc0\x4f\xd4\x30\xc8"
+		"\x6b\xa7\xb8\x10\x9d\xad\x11\xd1\x80\xb4\x00\xc0\x4f\xd4\x30\xc8", 
+		32, 32 );
+	DebugPrintF( kDebugLevelMax, "%s\n\n", s );
+	
+	DebugSNPrintF( s, sizeof( s ), "\"%H\"", "\x6b\xa7", 2, 2 );
+	DebugPrintF( kDebugLevelMax, "%s\n\n", s );
+	
+	// Hex Dumps
+	
+	s[ 0 ] = '\0';
+	DebugHexDump( kDebugLevelMax, 0, "My Label", kSizeCString, 0, NULL, 0, data, data, sizeof( data ), 
+		kDebugFlagsNone, s, sizeof( s ) );
+	DebugPrintF( kDebugLevelMax, "%s\n", s );
+	
+	s[ 0 ] = '\0';
+	DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, data, data, sizeof( data ), 
+		kDebugFlagsNoAddress | kDebugFlagsNoOffset, s, sizeof( s ) );
+	DebugPrintF( kDebugLevelMax, "%s\n", s );
+	
+	s[ 0 ] = '\0';
+	DebugHexDump( kDebugLevelMax, 0, "My Label", kSizeCString, 0, NULL, 0, data, data, sizeof( data ), 
+		kDebugFlagsNoAddress | kDebugFlagsNoOffset, s, sizeof( s ) );
+	DebugPrintF( kDebugLevelMax, "%s\n", s );
+	
+	s[ 0 ] = '\0';
+	DebugHexDump( kDebugLevelMax, 0, "My Label", kSizeCString, 0, NULL, 0, data, data, sizeof( data ), 
+		kDebugFlagsNoAddress, s, sizeof( s ) );
+	DebugPrintF( kDebugLevelMax, "%s\n", s );
+	
+	s[ 0 ] = '\0';
+	DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, data, data, sizeof( data ), 
+		kDebugFlagsNoOffset, s, sizeof( s ) );
+	DebugPrintF( kDebugLevelMax, "%s\n", s );
+	
+	s[ 0 ] = '\0';
+	DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, data, data, sizeof( data ), 
+		kDebugFlagsNoAddress, s, sizeof( s ) );
+	DebugPrintF( kDebugLevelMax, "%s\n", s );
+	
+	s[ 0 ] = '\0';
+	DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, data, data, sizeof( data ), 
+		kDebugFlagsNoOffset, s, sizeof( s ) );
+	DebugPrintF( kDebugLevelMax, "%s\n", s );
+	
+	s[ 0 ] = '\0';
+	DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, data, data, sizeof( data ), 
+		kDebugFlagsNoByteCount, s, sizeof( s ) );
+	DebugPrintF( kDebugLevelMax, "%s\n", s );
+	
+	s[ 0 ] = '\0';
+	DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, "\x41\x62\x43\x64", "\x41\x62\x43\x64", 4,	// 'AbCd'
+		kDebugFlagsNoAddress | kDebugFlagsNoOffset | kDebugFlagsNoNewLine |
+		kDebugFlagsNo32BitSeparator | kDebugFlagsNo16ByteHexPad | kDebugFlagsNoByteCount, 
+		s, sizeof( s ) );
+	DebugPrintF( kDebugLevelMax, "%s\n", s );
+	
+	s[ 0 ] = '\0';
+	DebugHexDump( kDebugLevelMax, 0, NULL, 0, 0, NULL, 0, data, data, sizeof( data ), 
+		kDebugFlagsNoAddress | kDebugFlagsNoOffset | kDebugFlagsNoASCII | kDebugFlagsNoNewLine |
+		kDebugFlags16BitSeparator | kDebugFlagsNo32BitSeparator |
+		kDebugFlagsNo16ByteHexPad | kDebugFlagsNoByteCount, s, sizeof( s ) );
+	DebugPrintF( kDebugLevelMax, "%s\n", s );
+	
+	s[ 0 ] = '\0';
+	DebugHexDump( kDebugLevelMax, 8, NULL, 0, 0, NULL, 0, data, data, sizeof( data ), kDebugFlagsNone, s, sizeof( s ) );
+	DebugPrintF( kDebugLevelMax, "%s\n", s );
+	
+	// dlog's
+	
+	dlog( kDebugLevelNotice, "dlog\n" );
+	dlog( kDebugLevelNotice, "dlog integer: %d\n", 123 );
+	dlog( kDebugLevelNotice, "dlog string:  \"%s\"\n", "test string" );
+	dlogmem( kDebugLevelNotice, data, sizeof( data ) );
+	
+	// Done
+	
+	DebugPrintF( kDebugLevelMax, "\n\nALL TESTS DONE\n\n" );
+	err = kNoErr;
+	
+exit:
+	if( err )
+	{
+		DebugPrintF( kDebugLevelMax, "\n\n### TEST FAILED ###\n\n" );
+	}
+	return( err );
+}
+
+#endif	// DEBUG
diff --git a/mdnsresponder/mDNSShared/DebugServices.h b/mdnsresponder/mDNSShared/DebugServices.h
new file mode 100644
index 0000000..d4e5c72
--- /dev/null
+++ b/mdnsresponder/mDNSShared/DebugServices.h
@@ -0,0 +1,1607 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@header		DebugServices
+
+	Debugging Library
+*/
+
+#ifndef	__DEBUG_SERVICES__
+#define	__DEBUG_SERVICES__
+
+#include	<stdarg.h>
+
+#include	"CommonServices.h"
+
+#if( TARGET_OS_VXWORKS )
+	#include	"logLib.h"
+#endif
+
+#if 0
+#pragma mark == Settings ==
+#endif
+
+//===========================================================================================================================
+//	Settings
+//===========================================================================================================================
+
+// General
+
+#if( !defined( DEBUG ) )
+	#define	DEBUG		0
+#endif
+
+#if( defined( NDEBUG ) && DEBUG )
+	#error NDEBUG defined and DEBUG is also enabled...they need to be in-sync
+#endif
+	
+// AssertMacros.h/Debugging.h overrides.
+
+#if( !defined( DEBUG_OVERRIDE_APPLE_MACROS ) )
+	#define	DEBUG_OVERRIDE_APPLE_MACROS		1
+#endif
+
+// Routine name. Uses ISO __func__ where possible. Otherwise, uses the best thing that is available (if anything).
+
+#if( defined( __MWERKS__ ) || ( __GNUC__ > 2 ) || ( ( __GNUC__ == 2 ) && ( __GNUC_MINOR__ >= 9 ) ) )
+	#define	__ROUTINE__					__func__
+#elif( defined( __GNUC__ ) )
+	#define	__ROUTINE__					__PRETTY_FUNCTION__
+#elif( defined( _MSC_VER ) && !defined( _WIN32_WCE ) )
+	#define	__ROUTINE__					__FUNCTION__
+#else
+	#define	__ROUTINE__					""
+#endif
+
+// Variable argument macro support. Use ANSI C99 __VA_ARGS__ where possible. Otherwise, use the next best thing.
+
+#if( defined( __GNUC__ ) )
+	#if( ( __GNUC__ > 3 ) || ( ( __GNUC__ == 3 ) && ( __GNUC_MINOR__ >= 3) ) )
+		#define	DEBUG_C99_VA_ARGS		1
+		#define	DEBUG_GNU_VA_ARGS		0
+	#else
+		#define	DEBUG_C99_VA_ARGS		0
+		#define	DEBUG_GNU_VA_ARGS		1
+	#endif
+#elif( defined( __MWERKS__ ) )
+	#define	DEBUG_C99_VA_ARGS			1
+	#define	DEBUG_GNU_VA_ARGS			0
+#else
+	#define	DEBUG_C99_VA_ARGS			0
+	#define	DEBUG_GNU_VA_ARGS			0
+#endif
+
+#if 0
+#pragma mark == Output ==
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@defined	DEBUG_FPRINTF_ENABLED
+	
+	@abstract	Enables ANSI C fprintf output.
+*/
+
+#if( !defined( DEBUG_FPRINTF_ENABLED ) )
+	#if( !TARGET_API_MAC_OSX_KERNEL && !TARGET_OS_WINDOWS_CE )
+		#define	DEBUG_FPRINTF_ENABLED			1
+	#else
+		#define	DEBUG_FPRINTF_ENABLED			0
+	#endif
+#else
+	#if( TARGET_API_MAC_OSX_KERNEL || TARGET_OS_WINDOWS_CE )
+		#error fprintf enabled, but not supported on Mac OS X kernel or Windows CE
+	#endif
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@defined	DEBUG_MAC_OS_X_IOLOG_ENABLED
+	
+	@abstract	Enables IOLog (Mac OS X Kernel) output.
+*/
+
+#if( !defined( DEBUG_MAC_OS_X_IOLOG_ENABLED ) )
+	#define	DEBUG_MAC_OS_X_IOLOG_ENABLED		TARGET_API_MAC_OSX_KERNEL
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@defined	DEBUG_KPRINTF_ENABLED
+	
+	@abstract	Enables kprintf (Mac OS X Kernel) output.
+*/
+
+#if( !defined( DEBUG_KPRINTF_ENABLED ) )
+	#define	DEBUG_KPRINTF_ENABLED				TARGET_API_MAC_OSX_KERNEL
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@defined	DEBUG_IDEBUG_ENABLED
+	
+	@abstract	Enables iDebug (Mac OS X user and Kernel) output.
+	
+	@discussion
+	
+	For Mac OS X kernel development, iDebug is enabled by default because we can dynamically check for the presence 
+	of iDebug via some exported IOKit symbols. Mac OS X app usage doesn't allow dynamic detection because it relies
+	on statically linking to the iDebugServices.cp file so for Mac OS X app usage, you have to manually enable iDebug.
+*/
+
+#if( !defined( DEBUG_IDEBUG_ENABLED ) )
+	#define	DEBUG_IDEBUG_ENABLED				TARGET_API_MAC_OSX_KERNEL
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@defined	DEBUG_CORE_SERVICE_ASSERTS_ENABLED
+	
+	@abstract	Controls whether Core Services assert handling is enabled. Enabling requires CoreServices framework.
+*/
+
+#if( !defined( DEBUG_CORE_SERVICE_ASSERTS_ENABLED ) )
+	#if( defined( __DEBUGGING__ ) )
+		#define	DEBUG_CORE_SERVICE_ASSERTS_ENABLED		1
+	#else
+		#define	DEBUG_CORE_SERVICE_ASSERTS_ENABLED		0
+	#endif
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@typedef	DebugOutputType
+	
+	@abstract	Type of debug output (i.e. where the output goes).
+*/
+
+typedef uint32_t			DebugOutputType;
+
+#define kDebugOutputTypeNone				0x6E6F6E65U	// 'none' - no params
+#define kDebugOutputTypeCustom				0x63757374U	// 'cust' - 1st param = function ptr, 2nd param = context
+#define kDebugOutputTypeFPrintF				0x66707269U	// 'fpri' - 1st param = DebugOutputTypeFlags [, 2nd param = filename]
+#define kDebugOutputTypeiDebug				0x69646267U	// 'idbg' - no params
+#define kDebugOutputTypeKPrintF				0x6B707266U	// 'kprf' - no params
+#define kDebugOutputTypeMacOSXIOLog			0x696C6F67U	// 'ilog' - no params 
+#define kDebugOutputTypeMacOSXLog			0x786C6F67U	// 'xlog' - no params
+#define kDebugOutputTypeWindowsDebugger		0x77696E64U	// 'wind' - no params
+#define kDebugOutputTypeWindowsEventLog		0x7765766CU	// 'wevl' - 1st param = C-string name, 2nd param = HMODULE or NULL.
+
+// Console meta output kind - Any kind of Console output (in horizontal order of preference):
+// 
+// Mac OS X			= ANSI printf (viewable in Console.app)
+// Mac OS X Kernel	= IOLog (/var/log/system.log) or kprintf (serial).
+// Windows			= ANSI printf (Console window) or OutputDebugString (debugger).
+// Other			= ANSI printf (viewer varies).
+
+#define kDebugOutputTypeMetaConsole			0x434F4E53U	// 'CONS' - no params
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@typedef	DebugOutputTypeFlags
+	
+	@abstract	Flags controlling how the output type is configured.
+	
+	@constant	kDebugOutputTypeFlagsTypeMask	Bit mask for the output type (e.g. stdout, stderr, file, etc.).
+	@constant	kDebugOutputTypeFlagsStdOut		fprintf should go to stdout.
+	@constant	kDebugOutputTypeFlagsStdErr		fprintf should go to stderr.
+	@constant	kDebugOutputTypeFlagsFile		fprintf should go to a specific file (filename passed as va_arg).
+*/
+
+typedef unsigned int		DebugOutputTypeFlags;
+
+#define	kDebugOutputTypeFlagsTypeMask	0xF
+#define	kDebugOutputTypeFlagsStdOut		1
+#define	kDebugOutputTypeFlagsStdErr		2
+#define	kDebugOutputTypeFlagsFile		10
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@typedef	DebugOutputFunctionPtr
+	
+	@abstract	Function ptr for a custom callback to print debug output.
+*/
+
+typedef void ( *DebugOutputFunctionPtr )( char *inData, size_t inSize, void *inContext );
+
+//===========================================================================================================================
+//	Constants
+//===========================================================================================================================
+
+#if 0
+#pragma mark == Flags ==
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@typedef	DebugFlags
+	
+	@abstract	Flags controlling how output is printed.
+*/
+
+typedef uint32_t		DebugFlags;
+
+#define	kDebugFlagsNone					0
+#define	kDebugFlagsNoAddress			( 1 << 0 )
+#define	kDebugFlagsNoOffset				( 1 << 1 )
+#define	kDebugFlags32BitOffset			( 1 << 2 )
+#define	kDebugFlagsNoASCII				( 1 << 3 )
+#define	kDebugFlagsNoNewLine			( 1 << 4 )
+#define	kDebugFlags8BitSeparator		( 1 << 5 )
+#define	kDebugFlags16BitSeparator		( 1 << 6 )
+#define	kDebugFlagsNo32BitSeparator		( 1 << 7 )
+#define	kDebugFlagsNo16ByteHexPad		( 1 << 8 )
+#define	kDebugFlagsNoByteCount			( 1 << 9 )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@enum		DebugTaskLevelFlags
+	
+	@abstract	Flags indicating the task level.
+*/
+
+enum
+{
+	kDebugInterruptLevelShift				= 0, 
+	kDebugInterruptLevelMask		  		= 0x00000007,
+	kDebugInVBLTaskMask			  			= 0x00000010,
+	kDebugInDeferredTaskMask		 		= 0x00000020,
+    kDebugInSecondaryInterruptHandlerMask  	= 0x00000040, 
+	kDebugPageFaultFatalMask 				= 0x00000100, 	// There should be a "kPageFaultFatalMask" in Debugging.h.
+	kDebugMPTaskLevelMask					= 0x00000200, 	// There should be a "kMPTaskLevelMask" in Debugging.h.
+	kDebugInterruptDepthShift				= 16, 
+	kDebugInterruptDepthMask				= 0x00FF0000
+};
+
+#define	DebugExtractTaskLevelInterruptLevel( LEVEL )	\
+	( ( ( LEVEL ) & kDebugInterruptLevelMask ) >> kDebugInterruptLevelShift )
+	
+#define	DebugExtractTaskLevelInterruptDepth( LEVEL )	\
+	( ( ( LEVEL ) & kDebugInterruptDepthMask ) >> kDebugInterruptDepthShift )
+
+#if 0
+#pragma mark == Levels ==
+#endif
+
+//===========================================================================================================================
+//	Constants & Types - Levels
+//===========================================================================================================================
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@typedef	DebugLevel
+	
+	@abstract	Level used to control debug logging.
+*/
+
+typedef int32_t			DebugLevel;
+
+// Levels
+
+#define kDebugLevelMask					0x0000FFFF
+#define kDebugLevelChatty				100
+#define kDebugLevelVerbose				500
+#define kDebugLevelTrace 				800
+#define kDebugLevelInfo 				1000
+#define kDebugLevelNotice				3000
+#define kDebugLevelWarning				5000
+#define kDebugLevelAssert 				6000
+#define kDebugLevelRequire				7000
+#define kDebugLevelError				8000
+#define kDebugLevelCritical				9000
+#define kDebugLevelAlert				10000
+#define kDebugLevelEmergency			11000
+#define kDebugLevelTragic				12000
+#define kDebugLevelMax					0x0000FFFF
+
+// Level Flags
+	
+#define kDebugLevelFlagMask				0xFFFF0000
+#define kDebugLevelFlagStackTrace		0x00010000
+#define kDebugLevelFlagDebugBreak		0x00020000
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@typedef	LogLevel
+	
+	@abstract	Level used to control which events are logged.
+*/
+
+typedef int32_t						LogLevel;
+
+#define	kLogLevelUninitialized		-1L
+#define kLogLevelAll				0L
+#define kLogLevelChatty				100L
+#define kLogLevelVerbose			500L
+#define kLogLevelTrace 				800L
+#define kLogLevelInfo 				1000L
+#define kLogLevelNotice				3000L
+#define kLogLevelWarning			4000L
+#define kLogLevelAssert 			6000L
+#define kLogLevelRequire			7000L
+#define kLogLevelError				8000L
+#define kLogLevelCritical			9000L
+#define kLogLevelAlert				10000L
+#define kLogLevelEmergency			11000L
+#define kLogLevelTragic				12000L
+#define kLogLevelOff				0x0000FFFEL
+
+#if 0
+#pragma mark == Properties ==
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@typedef	DebugPropertyTag
+	
+	@abstract	Tag for properties.
+*/
+
+typedef uint32_t		DebugPropertyTag;
+
+#define	kDebugPropertyTagPrintLevelMin		0x6D696E70U		// 'minp'	Get: 1st param = DebugLevel *
+															//			Set: 1st param = DebugLevel
+
+#define	kDebugPropertyTagPrintLevel			kDebugPropertyTagPrintLevelMin
+
+#define	kDebugPropertyTagPrintLevelMax		0x706D786CU		// 'maxp'	Get: 1st param = DebugLevel *
+															//			Set: 1st param = DebugLevel
+
+#define	kDebugPropertyTagBreakLevel			0x62726B6CU		// 'brkl'	Get: 1st param = DebugLevel *
+															//			Set: 1st param = DebugLevel
+#if 0
+#pragma mark == General macros ==
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@defined	DEBUG_UNUSED
+	
+	@abstract	Macro to mark a paramter as unused to avoid unused parameter warnings.
+	
+	@discussion	
+	
+	There is no universally supported pragma/attribute for indicating a variable is unused. DEBUG_UNUSED lets us
+	indicate a variable is unused in a manner that is supported by most compilers.
+*/
+
+#define	DEBUG_UNUSED( X )			(void)( X )
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@defined	DEBUG_USE_ONLY
+	
+	@abstract	Macro to mark a variable as used only when debugging is enabled.
+	
+	@discussion	
+	
+	Variables are sometimes needed only for debugging. When debugging is turned off, these debug-only variables generate 
+	compiler warnings about unused variables. To eliminate these warnings, use these macros to indicate variables that 
+	are only used for debugging.
+*/
+
+#if( DEBUG )
+	#define	DEBUG_USE_ONLY( X )
+#else
+	#define	DEBUG_USE_ONLY( X )		(void)( X )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@defined	DEBUG_LOCAL
+	
+	@abstract	Macros to make variables and functions static when debugging is off, but extern when debugging is on.
+	
+	@discussion	
+	
+	Rather than using "static" directly, using this macros allows you to access these variables external while 
+	debugging without being penalized for production builds.
+*/
+
+#if( DEBUG )
+	#define	DEBUG_LOCAL
+#else
+	#define	DEBUG_LOCAL			static
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@defined	DEBUG_STATIC
+	
+	@abstract	Macros to make variables and functions static when debugging is off, but extern when debugging is on.
+	
+	@discussion	
+	
+	Rather than using "static" directly, using this macros allows you to access these variables external while 
+	debugging without being penalized for production builds.
+*/
+
+#if( DEBUG )
+	#define	DEBUG_STATIC
+#else
+	#define	DEBUG_STATIC	static
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@defined	DEBUG_EXPORT
+	
+	@abstract	Macros to export variables.
+	
+	@discussion	
+	
+	"__private_extern__" is a hack for IOKit to allow symbols to be exported from compilation units, but 
+	// not exported outside a driver (IOKit uses a lame global namespace for symbols). This still does not 
+	// solve the problem of multiple drivers in the same dependency chain since they share symbols.
+*/
+
+#if( TARGET_API_MAC_OSX_KERNEL )
+	#define	DEBUG_EXPORT		__private_extern__
+#else
+	#define	DEBUG_EXPORT		extern
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@defined	debug_add
+	
+	@abstract	Macro to add (or subtract if negative) a value when debugging is on. Does nothing if debugging is off.
+*/
+
+#if( DEBUG )
+	#define	debug_add( A, B )		( A ) += ( B )
+#else
+	#define	debug_add( A, B )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@defined	debug_perform
+	
+	@abstract	Macro to perform something in debug-only builds.
+*/
+
+#if( DEBUG )
+	#define	debug_perform( X )		do { X; } while( 0 )
+#else
+	#define	debug_perform( X )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@function	translate_errno
+
+	@abstract	Returns 0 if the test success. If the test fails, returns errno if non-zero and othewise the alternate error.
+*/
+
+#define translate_errno( TEST, ERRNO, ALTERNATE_ERROR )		( ( TEST ) ? 0 : ( ERRNO ) ? ( ERRNO ) : ( ALTERNATE_ERROR ) )
+
+#if 0
+#pragma mark == Compile Time macros ==
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@defined	check_compile_time
+	
+	@abstract	Performs a compile-time check of something such as the size of an int.
+	
+	@discussion	
+	
+	This declares an array with a size that is determined by a compile-time expression. If the expression evaluates 
+	to 0, the array has a size of -1, which is illegal and generates a compile-time error.
+	
+	For example:
+	
+	check_compile_time( sizeof( int ) == 4 );
+	
+	Note: This only works with compile-time expressions.
+	Note: This only works in places where extern declarations are allowed (e.g. global scope).
+	
+	References:
+	
+	<http://www.jaggersoft.com/pubs/CVu11_3.html>
+	<http://www.jaggersoft.com/pubs/CVu11_5.html>
+	
+	Note: The following macros differ from the macros on the www.jaggersoft.com web site because those versions do not
+	work with GCC due to GCC allow a zero-length array. Using a -1 condition turned out to be more portable.
+*/
+
+#define	check_compile_time( X )		extern int debug_compile_time_name[ ( X ) ? 1 : -1 ]
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@defined	check_compile_time_code
+	
+	@abstract	Perform a compile-time check, suitable for placement in code, of something such as the size of an int.
+	
+	@discussion	
+	
+	This creates a switch statement with an existing case for 0 and an additional case using the result of a 
+	compile-time expression. A switch statement cannot have two case labels with the same constant so if the
+	compile-time expression evaluates to 0, it is illegal and generates a compile-time error. If the compile-time
+	expression does not evaluate to 0, the resulting value is used as the case label and it compiles without error.
+
+	For example:
+	
+	check_compile_time_code( sizeof( int ) == 4 );
+	
+	Note: This only works with compile-time expressions.
+	Note: This does not work in a global scope so it must be inside a function.
+	
+	References:
+	
+	<http://www.jaggersoft.com/pubs/CVu11_3.html>
+	<http://www.jaggersoft.com/pubs/CVu11_5.html>
+*/
+
+#define	check_compile_time_code( X )	switch( 0 ) { case 0: case X:; }
+
+#if 0
+#pragma mark == check macros ==
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@defined	check
+	
+	@abstract	Check that an expression is true (non-zero).
+	
+	@discussion	
+	
+	If expression evalulates to false, this prints debugging information (actual expression string, file, line number, 
+	function name, etc.) using the default debugging output method.
+				
+	Code inside check() statements is not compiled into production builds. 
+*/
+
+#if( DEBUG_OVERRIDE_APPLE_MACROS )
+	#undef check
+#endif
+#if( !defined( check ) )
+	#if( DEBUG )
+		#define	check( X )																					\
+			do 																								\
+			{																								\
+				if( !( X ) ) 																				\
+				{																							\
+					debug_print_assert( 0, #X, NULL, __FILE__, __LINE__, __ROUTINE__ );						\
+				}																							\
+			} while( 0 )
+	#else
+		#define	check( X )
+	#endif
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@defined	check_string
+	
+	@abstract	Check that an expression is true (non-zero) with an explanation.
+	
+	@discussion	
+	
+	If expression evalulates to false, this prints debugging information (actual expression string, file, line number, 
+	function name, etc.) and a custom explanation string using the default debugging output method.
+				
+	Code inside check_string() statements is not compiled into production builds. 
+*/
+
+#if( DEBUG_OVERRIDE_APPLE_MACROS )
+	#undef check_string
+#endif
+#if( !defined( check_string ) )
+	#if( DEBUG )
+		#define	check_string( X, STR )																		\
+			do 																								\
+			{																								\
+				if( !( X ) ) 																				\
+				{																							\
+					debug_print_assert( 0, #X, STR, __FILE__, __LINE__, __ROUTINE__ );						\
+				}																							\
+																											\
+			}	while( 0 )
+	#else
+		#define	check_string( X, STR )
+	#endif
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@defined	check_noerr
+	
+	@abstract	Check that an error code is noErr (0).
+	
+	@discussion	
+	
+	If the error code is non-0, this prints debugging information (actual expression string, file, line number, 
+	function name, etc.) using the default debugging output method.
+				
+	Code inside check_noerr() statements is not compiled into production builds. 
+*/
+
+#if( DEBUG_OVERRIDE_APPLE_MACROS )
+	#undef check_noerr
+#endif
+#if( !defined( check_noerr ) )
+	#if( DEBUG )
+		#define	check_noerr( ERR )																			\
+			do 																								\
+			{																								\
+				int_least32_t		localErr;																\
+																											\
+				localErr = (int_least32_t)( ERR );															\
+				if( localErr != 0 ) 																		\
+				{																							\
+					debug_print_assert( localErr, NULL, NULL, __FILE__, __LINE__, __ROUTINE__ );			\
+				}																							\
+																											\
+			}	while( 0 )
+	#else
+		#define	check_noerr( ERR )
+	#endif
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@defined	check_noerr_string
+	
+	@abstract	Check that an error code is noErr (0) with an explanation.
+	
+	@discussion	
+	
+	If the error code is non-0, this prints debugging information (actual expression string, file, line number, 
+	function name, etc.) and a custom explanation string using the default debugging output method.
+				
+	Code inside check_noerr_string() statements is not compiled into production builds. 
+*/
+
+#if( DEBUG_OVERRIDE_APPLE_MACROS )
+	#undef check_noerr_string
+#endif
+#if( !defined( check_noerr_string ) )
+	#if( DEBUG )
+		#define	check_noerr_string( ERR, STR )																\
+			do 																								\
+			{																								\
+				int_least32_t		localErr;																\
+																											\
+				localErr = (int_least32_t)( ERR );															\
+				if( localErr != 0 ) 																		\
+				{																							\
+					debug_print_assert( localErr, NULL, STR, __FILE__, __LINE__, __ROUTINE__ );				\
+				}																							\
+																											\
+			}	while( 0 )
+	#else
+		#define	check_noerr_string( ERR, STR )
+	#endif
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@defined	check_translated_errno
+	
+	@abstract	Check a condition and prints errno (if non-zero) to the log.
+	
+	@discussion	
+	
+	Code inside check_translated_errno() statements is not compiled into production builds.
+*/
+
+#if( !defined( check_translated_errno ) )
+	#if( DEBUG )
+		#define	check_translated_errno( TEST, ERRNO, ALTERNATE_ERROR )										\
+			do 																								\
+			{																								\
+				if( !( TEST ) ) 																			\
+				{																							\
+					int_least32_t		localErr;															\
+																											\
+					localErr = (int_least32_t)( ERRNO );													\
+					localErr = ( localErr != 0 ) ? localErr : (int_least32_t)( ALTERNATE_ERROR );			\
+					debug_print_assert( localErr, #TEST, NULL, __FILE__, __LINE__, __ROUTINE__ );			\
+				}																							\
+																											\
+			} 	while( 0 )
+	#else
+		#define	check_translated_errno( TEST, ERRNO, ALTERNATE_ERROR )
+	#endif
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@defined	check_ptr_overlap
+	
+	@abstract	Checks that two ptrs do not overlap.
+*/
+
+#define	check_ptr_overlap( P1, P1_SIZE, P2, P2_SIZE )										\
+	do																						\
+	{																						\
+		check( !( ( (uintptr_t)( P1 ) >=     (uintptr_t)( P2 ) ) && 						\
+				  ( (uintptr_t)( P1 ) <  ( ( (uintptr_t)( P2 ) ) + ( P2_SIZE ) ) ) ) );		\
+		check( !( ( (uintptr_t)( P2 ) >=     (uintptr_t)( P1 ) ) && 						\
+				  ( (uintptr_t)( P2 ) <  ( ( (uintptr_t)( P1 ) ) + ( P1_SIZE ) ) ) ) );		\
+																							\
+	}	while( 0 )
+
+#if 0
+#pragma mark == require macros ==
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@defined	require
+	
+	@abstract	Requires that an expression evaluate to true.
+	
+	@discussion	
+	
+	If expression evalulates to false, this prints debugging information (actual expression string, file, line number, 
+	function name, etc.) using the default debugging output method then jumps to a label.
+*/
+
+#if( DEBUG_OVERRIDE_APPLE_MACROS )
+	#undef require
+#endif
+#if( !defined( require ) )
+	#define	require( X, LABEL )																				\
+		do 																									\
+		{																									\
+			if( !( X ) ) 																					\
+			{																								\
+				debug_print_assert( 0, #X, NULL, __FILE__, __LINE__, __ROUTINE__ );							\
+				goto LABEL;																					\
+			}																								\
+																											\
+		}	while( 0 )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@defined	require_string
+	
+	@abstract	Requires that an expression evaluate to true with an explanation.
+	
+	@discussion	
+	
+	If expression evalulates to false, this prints debugging information (actual expression string, file, line number, 
+	function name, etc.) and a custom explanation string using the default debugging output method then jumps to a label.
+*/
+
+#if( DEBUG_OVERRIDE_APPLE_MACROS )
+	#undef require_string
+#endif
+#if( !defined( require_string ) )
+	#define	require_string( X, LABEL, STR )																	\
+		do 																									\
+		{																									\
+			if( !( X ) ) 																					\
+			{																								\
+				debug_print_assert( 0, #X, STR, __FILE__, __LINE__, __ROUTINE__ );							\
+				goto LABEL;																					\
+			}																								\
+																											\
+		}	while( 0 )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@defined	require_quiet
+	
+	@abstract	Requires that an expression evaluate to true.
+	
+	@discussion	
+	
+	If expression evalulates to false, this jumps to a label. No debugging information is printed.
+*/
+
+#if( DEBUG_OVERRIDE_APPLE_MACROS )
+	#undef require_quiet
+#endif
+#if( !defined( require_quiet ) )
+	#define	require_quiet( X, LABEL )																		\
+		do 																									\
+		{																									\
+			if( !( X ) ) 																					\
+			{																								\
+				goto LABEL;																					\
+			}																								\
+																											\
+		}	while( 0 )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@defined	require_noerr
+	
+	@abstract	Require that an error code is noErr (0).
+	
+	@discussion	
+	
+	If the error code is non-0, this prints debugging information (actual expression string, file, line number, 
+	function name, etc.) using the default debugging output method then jumps to a label.
+*/
+
+#if( DEBUG_OVERRIDE_APPLE_MACROS )
+	#undef require_noerr
+#endif
+#if( !defined( require_noerr ) )
+	#define	require_noerr( ERR, LABEL )																		\
+		do 																									\
+		{																									\
+			int_least32_t		localErr;																	\
+																											\
+			localErr = (int_least32_t)( ERR );																\
+			if( localErr != 0 ) 																			\
+			{																								\
+				debug_print_assert( localErr, NULL, NULL, __FILE__, __LINE__, __ROUTINE__ );				\
+				goto LABEL;																					\
+			}																								\
+																											\
+		}	while( 0 )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@defined	require_noerr_string
+	
+	@abstract	Require that an error code is noErr (0).
+	
+	@discussion	
+	
+	If the error code is non-0, this prints debugging information (actual expression string, file, line number, 
+	function name, etc.), and a custom explanation string using the default debugging output method using the 
+	default debugging output method then jumps to a label.
+*/
+
+#if( DEBUG_OVERRIDE_APPLE_MACROS )
+	#undef require_noerr_string
+#endif
+#if( !defined( require_noerr_string ) )
+	#define	require_noerr_string( ERR, LABEL, STR )															\
+		do 																									\
+		{																									\
+			int_least32_t		localErr;																	\
+																											\
+			localErr = (int_least32_t)( ERR );																\
+			if( localErr != 0 ) 																			\
+			{																								\
+				debug_print_assert( localErr, NULL, STR, __FILE__, __LINE__, __ROUTINE__ );					\
+				goto LABEL;																					\
+			}																								\
+																											\
+		}	while( 0 )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@defined	require_noerr_action_string
+	
+	@abstract	Require that an error code is noErr (0).
+	
+	@discussion	
+	
+	If the error code is non-0, this prints debugging information (actual expression string, file, line number, 
+	function name, etc.), and a custom explanation string using the default debugging output method using the 
+	default debugging output method then executes an action and jumps to a label.
+*/
+
+#if( DEBUG_OVERRIDE_APPLE_MACROS )
+	#undef require_noerr_action_string
+#endif
+#if( !defined( require_noerr_action_string ) )
+	#define	require_noerr_action_string( ERR, LABEL, ACTION, STR )											\
+		do 																									\
+		{																									\
+			int_least32_t		localErr;																	\
+																											\
+			localErr = (int_least32_t)( ERR );																\
+			if( localErr != 0 ) 																			\
+			{																								\
+				debug_print_assert( localErr, NULL, STR, __FILE__, __LINE__, __ROUTINE__ );					\
+				{ ACTION; }																					\
+				goto LABEL;																					\
+			}																								\
+																											\
+		}	while( 0 )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@defined	require_noerr_quiet
+	
+	@abstract	Require that an error code is noErr (0).
+	
+	@discussion	
+	
+	If the error code is non-0, this jumps to a label. No debugging information is printed.
+*/
+
+#if( DEBUG_OVERRIDE_APPLE_MACROS )
+	#undef require_noerr_quiet
+#endif
+#if( !defined( require_noerr_quiet ) )
+	#define	require_noerr_quiet( ERR, LABEL )																\
+		do 																									\
+		{																									\
+			if( ( ERR ) != 0 ) 																				\
+			{																								\
+				goto LABEL;																					\
+			}																								\
+																											\
+		}	while( 0 )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@defined	require_noerr_action
+	
+	@abstract	Require that an error code is noErr (0) with an action to execute otherwise.
+	
+	@discussion	
+	
+	If the error code is non-0, this prints debugging information (actual expression string, file, line number, 
+	function name, etc.) using the default debugging output method then executes an action and jumps to a label.
+*/
+
+#if( DEBUG_OVERRIDE_APPLE_MACROS )
+	#undef require_noerr_action
+#endif
+#if( !defined( require_noerr_action ) )
+	#define	require_noerr_action( ERR, LABEL, ACTION )														\
+		do 																									\
+		{																									\
+			int_least32_t		localErr;																	\
+																											\
+			localErr = (int_least32_t)( ERR );																\
+			if( localErr != 0 ) 																			\
+			{																								\
+				debug_print_assert( localErr, NULL, NULL, __FILE__, __LINE__, __ROUTINE__ );				\
+				{ ACTION; }																					\
+				goto LABEL;																					\
+			}																								\
+																											\
+		}	while( 0 )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@defined	require_noerr_action_quiet
+	
+	@abstract	Require that an error code is noErr (0) with an action to execute otherwise.
+	
+	@discussion	
+	
+	If the error code is non-0, this executes an action and jumps to a label. No debugging information is printed.
+*/
+
+#if( DEBUG_OVERRIDE_APPLE_MACROS )
+	#undef require_noerr_action_quiet
+#endif
+#if( !defined( require_noerr_action_quiet ) )
+	#define	require_noerr_action_quiet( ERR, LABEL, ACTION )												\
+		do 																									\
+		{																									\
+			if( ( ERR ) != 0 ) 																				\
+			{																								\
+				{ ACTION; }																					\
+				goto LABEL;																					\
+			}																								\
+																											\
+		}	while( 0 )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@defined	require_action
+	
+	@abstract	Requires that an expression evaluate to true with an action to execute otherwise.
+	
+	@discussion	
+	
+	If expression evalulates to false, this prints debugging information (actual expression string, file, line number, 
+	function name, etc.) using the default debugging output method then executes an action and jumps to a label.
+*/
+
+#if( DEBUG_OVERRIDE_APPLE_MACROS )
+	#undef require_action
+#endif
+#if( !defined( require_action ) )
+	#define	require_action( X, LABEL, ACTION )																\
+		do 																									\
+		{																									\
+			if( !( X ) ) 																					\
+			{																								\
+				debug_print_assert( 0, #X, NULL, __FILE__, __LINE__, __ROUTINE__ );							\
+				{ ACTION; }																					\
+				goto LABEL;																					\
+			}																								\
+																											\
+		}	while( 0 )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@defined	require_action_quiet
+	
+	@abstract	Requires that an expression evaluate to true with an action to execute otherwise.
+	
+	@discussion	
+	
+	If expression evalulates to false, this executes an action and jumps to a label. No debugging information is printed.
+*/
+
+#if( DEBUG_OVERRIDE_APPLE_MACROS )
+	#undef require_action_quiet
+#endif
+#if( !defined( require_action_quiet ) )
+	#define	require_action_quiet( X, LABEL, ACTION )														\
+		do 																									\
+		{																									\
+			if( !( X ) ) 																					\
+			{																								\
+				{ ACTION; }																					\
+				goto LABEL;																					\
+			}																								\
+																											\
+		}	while( 0 )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@defined	require_action_string
+	
+	@abstract	Requires that an expression evaluate to true with an explanation and action to execute otherwise.
+	
+	@discussion	
+	
+	If expression evalulates to false, this prints debugging information (actual expression string, file, line number, 
+	function name, etc.) and a custom explanation string using the default debugging output method then executes an
+	action and jumps to a label.
+*/
+
+#if( DEBUG_OVERRIDE_APPLE_MACROS )
+	#undef require_action_string
+#endif
+#if( !defined( require_action_string ) )
+	#define	require_action_string( X, LABEL, ACTION, STR )													\
+		do 																									\
+		{																									\
+			if( !( X ) ) 																					\
+			{																								\
+				debug_print_assert( 0, #X, STR, __FILE__, __LINE__, __ROUTINE__ );						\
+				{ ACTION; }																					\
+				goto LABEL;																					\
+			}																								\
+																											\
+		}	while( 0 )
+
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@defined	require_throw
+	
+	@abstract	Requires that an expression evaluates to true or an exception is thrown.
+	
+	@discussion	
+	
+	If the expression evaluates to false, this prints debugging information (actual expression string, file, 
+	line number, function name, etc.) using the default debugging output method then throws an exception.
+*/
+
+#if( defined( __cplusplus ) )
+	#define	require_throw( X )																				\
+		do 																									\
+		{																									\
+			if( !( X ) ) 																					\
+			{																								\
+				debug_print_assert( 0, #X, NULL, __FILE__, __LINE__, __ROUTINE__ );							\
+				throw kUnknownErr;																			\
+			}																								\
+																											\
+		}	while( 0 )
+#endif
+
+#if 0
+#pragma mark == Design-By-Contract macros ==
+#endif
+
+//===========================================================================================================================
+//	Design-By-Contract macros
+//===========================================================================================================================
+
+#define	ensure( X )													check( X )
+#define	ensure_string( X, STR )										check_string( X, STR )
+#define	ensure_noerr( ERR )											check_noerr( ERR )
+#define	ensure_noerr_string( ERR, STR )								check_noerr_string( ERR, STR )
+#define	ensure_translated_errno( TEST, ERRNO, ALTERNATE_ERROR )		check_translated_errno( TEST, ERRNO, ALTERNATE_ERROR )
+
+// Note: Design-By-Contract "require" macros are already defined elsewhere.
+
+#if 0
+#pragma mark == Expect macros ==
+#endif
+
+//===========================================================================================================================
+//	Expect macros
+//===========================================================================================================================
+
+// Expect macros allow code to include runtime checking of things that should not happen in shipping code (e.g. internal 
+// programmer errors, such as a NULL parameter where it is not allowed). Once the code has been verified to work correctly 
+// without asserting, the DEBUG_EXPECT_VERIFIED conditional can be set to eliminate the error checking entirely. It can 
+// also be useful to measure the cost of error checking code by profiling with it enable and with it disabled.
+
+#if( DEBUG_EXPECT_VERIFIED )
+	#define	require_expect
+	#define	require_string_expect
+	#define	require_quiet_expect
+	#define	require_noerr_expect
+	#define	require_noerr_string_expect
+	#define	require_noerr_action_string_expect
+	#define	require_noerr_quiet_expect
+	#define	require_noerr_action_expect
+	#define	require_noerr_action_quiet_expect
+	#define	require_action_expect
+	#define	require_action_quiet_expect
+	#define	require_action_string_expect
+#else
+	#define	require_expect							require
+	#define	require_string_expect					require_string
+	#define	require_quiet_expect					require_quiet
+	#define	require_noerr_expect					require_noerr
+	#define	require_noerr_string_expect				require_noerr_string
+	#define	require_noerr_action_string_expect		require_noerr_action_string
+	#define	require_noerr_quiet_expect				require_noerr_quiet
+	#define	require_noerr_action_expect				require_noerr_action
+	#define	require_noerr_action_quiet_expect		require_noerr_action_quiet
+	#define	require_action_expect					require_action
+	#define	require_action_quiet_expect				require_action_quiet
+	#define	require_action_string_expect			require_action_string
+#endif
+
+#if 0
+#pragma mark == Output macros ==
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@defined	debug_string
+	
+	@abstract	Prints a debugging C string.
+*/
+
+#if( DEBUG_OVERRIDE_APPLE_MACROS )
+	#undef debug_string
+#endif
+#if( !defined( debug_string ) )
+	#if( DEBUG )
+		#define	debug_string( STR )																		\
+			do 																							\
+			{																							\
+				debug_print_assert( 0, NULL, STR, __FILE__, __LINE__, __ROUTINE__ );					\
+																										\
+			}	while( 0 )
+	#else
+		#define	debug_string( STR )
+	#endif
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@defined	debug_print_assert
+	
+	@abstract	Prints an assertion.
+*/
+
+#if( DEBUG )
+	#define	debug_print_assert( ERROR_CODE, ASSERT_STRING, MESSAGE, FILENAME, LINE_NUMBER, FUNCTION )	\
+		DebugPrintAssert( ERROR_CODE, ASSERT_STRING, MESSAGE, FILENAME, LINE_NUMBER, FUNCTION )
+#else
+	#define	debug_print_assert( ERROR_CODE, ASSERT_STRING, MESSAGE, FILENAME, LINE_NUMBER, FUNCTION )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@defined	dlog
+	
+	@abstract	Prints a debug-only message.
+*/
+
+#if( DEBUG )
+	#if( DEBUG_C99_VA_ARGS )
+		#define	dlog( ... )			DebugPrintF( __VA_ARGS__ )
+	#elif( DEBUG_GNU_VA_ARGS )
+		#define	dlog( ARGS... )		DebugPrintF( ## ARGS )			
+	#else
+		#define	dlog				DebugPrintF
+	#endif
+#else
+	#if( DEBUG_C99_VA_ARGS )
+		#define	dlog( ... )
+	#elif( DEBUG_GNU_VA_ARGS )
+		#define	dlog( ARGS... )
+	#else
+		#define	dlog				while( 0 )
+	#endif
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@defined	dlogv
+	
+	@abstract	Prints a debug-only message.
+*/
+
+#if( DEBUG )
+	#define	dlogv( LEVEL, FORMAT, LIST )		DebugPrintFVAList( ( LEVEL ), ( FORMAT ), ( LIST ) )
+#else
+	#define	dlogv( LEVEL, FORMAT, LIST )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@defined	dlogmem
+	
+	@abstract	Prints a debug-only dump of memory.
+*/
+
+#if( DEBUG )
+	#define	dlogmem( LEVEL, PTR, SIZE )		\
+		DebugHexDump( ( LEVEL ), 0, NULL, 0, 0, NULL, 0, ( PTR ), ( PTR ), ( SIZE ), kDebugFlagsNone, NULL, 0 )
+#else
+	#define	dlogmem( LEVEL, PTR, SIZE )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@defined	DebugNSLog
+	
+	@abstract	Debug-only macro for the Cocoa NSLog function.
+*/
+
+#if( DEBUG )
+	#if( DEBUG_C99_VA_ARGS )
+		#define	DebugNSLog( ... )			NSLog( __VA_ARGS__ )
+	#elif( DEBUG_GNU_VA_ARGS )
+		#define	DebugNSLog( ARGS... )		NSLog( ## ARGS )
+	#else
+		#define	DebugNSLog					NSLog
+	#endif
+#else
+	#if( DEBUG_C99_VA_ARGS )
+		#define	DebugNSLog( ... )
+	#elif( DEBUG_GNU_VA_ARGS )
+		#define	DebugNSLog( ARGS... )
+	#else
+		#define	DebugNSLog					while( 0 )
+	#endif
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@defined	DebugLogMsg
+	
+	@abstract	Debug-only macro for the VxWorks logMsg function.
+*/
+
+#if( TARGET_OS_VXWORKS )
+	#if( DEBUG )
+		#define DebugLogMsg( LEVEL, FORMAT, P1, P2, P3, P4, P5, P6 )							\
+			do 																					\
+			{ 																					\
+				if( ( inLevel >= gDebugPrintLevelMin ) || ( inLevel <= gDebugPrintLevelMax ) )	\
+				{																				\
+					logMsg( ( FORMAT ), ( P1 ), ( P2 ), ( P3 ), ( P4 ), ( P5 ), ( P6 ) );		\
+				}																				\
+																								\
+			}	while( 0 )
+	#else
+		#define DebugLogMsg( LEVEL, FORMAT, P1, P2, P3, P4, P5, P6 )
+	#endif
+#else
+	#define	DebugLogMsg		dlog
+#endif
+
+#if 0
+#pragma mark == Routines - General ==
+#endif
+
+#ifdef	__cplusplus
+	extern "C" {
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@function	DebugInitialize
+
+	@abstract	Initializes the debugging library for a specific kind of output.
+
+	@param		inType		
+	@param		varArg		Variable number parameters, controlled by the "inType" parameter.
+*/
+
+#if( DEBUG )
+	DEBUG_EXPORT OSStatus	DebugInitialize( DebugOutputType inType, ... );
+#endif
+
+#if( DEBUG )
+	#if( DEBUG_C99_VA_ARGS )
+		#define	debug_initialize( ... )			DebugInitialize( __VA_ARGS__ )
+	#elif( DEBUG_GNU_VA_ARGS )
+		#define	debug_initialize( ARGS... )		DebugInitialize( ## ARGS )
+	#else
+		#define	debug_initialize				DebugInitialize
+	#endif
+#else
+	#if( DEBUG_C99_VA_ARGS )
+		#define	debug_initialize( ... )
+	#elif( DEBUG_GNU_VA_ARGS )
+		#define	debug_initialize( ARGS... )
+	#else
+		#define	debug_initialize				while( 0 )
+	#endif
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@function	DebugFinalize
+
+	@abstract	Releases any resources used by the debugging library
+*/
+
+#if( DEBUG )
+	DEBUG_EXPORT void		DebugFinalize( void );
+#endif
+
+#if( DEBUG )			  
+	#define	debug_terminate()	DebugFinalize()
+#else
+	#define	debug_terminate()
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@function	DebugGetProperty
+
+	@abstract	Gets the specified property from the debugging library.
+*/
+
+#if( DEBUG )
+	DEBUG_EXPORT OSStatus	DebugGetProperty( DebugPropertyTag inTag, ... );
+#endif
+
+#if( DEBUG )
+	#if( DEBUG_C99_VA_ARGS )
+		#define	debug_get_property( ... )			DebugGetProperty( __VA_ARGS__ )
+	#elif( DEBUG_GNU_VA_ARGS )
+		#define	debug_get_property( ARGS... )		DebugGetProperty( ## ARGS )
+	#else
+		#define	debug_get_property					DebugGetProperty
+	#endif
+#else
+	#if( DEBUG_C99_VA_ARGS )
+		#define	debug_get_property( ... )
+	#elif( DEBUG_GNU_VA_ARGS )
+		#define	debug_get_property( ARGS... )
+	#else
+		#define	debug_get_property					while( 0 )
+	#endif
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@function	DebugSetProperty
+
+	@abstract	Sets the specified property from the debugging library.
+*/
+
+#if( DEBUG )
+	DEBUG_EXPORT OSStatus	DebugSetProperty( DebugPropertyTag inTag, ... );
+#endif
+
+#if( DEBUG )
+	#if( DEBUG_C99_VA_ARGS )
+		#define	debug_set_property( ... )			DebugSetProperty( __VA_ARGS__ )
+	#elif( DEBUG_GNU_VA_ARGS )
+		#define	debug_set_property( ARGS... )		DebugSetProperty( ## ARGS )
+	#else
+		#define	debug_set_property					DebugSetProperty
+	#endif
+#else
+	#if( DEBUG_C99_VA_ARGS )
+		#define	debug_set_property( ... )
+	#elif( DEBUG_GNU_VA_ARGS )
+		#define	debug_set_property( ARGS... )
+	#else
+		#define	debug_set_property					while( 0 )
+	#endif
+#endif
+
+#if 0
+#pragma mark == Routines - Debugging Output ==
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@function	DebugPrintF
+
+	@abstract	Prints a debug message with printf-style formatting.
+	
+	@param		inLevel	Error that generated this assert or noErr.
+	
+	@param		inFormatString
+					C string containing assertion text.
+
+	@param		VAR_ARG
+					Variable number of arguments depending on the format string.
+	
+	@result		Number of bytes printed or -1 on error.
+*/
+
+#if( DEBUG )
+	DEBUG_EXPORT size_t		DebugPrintF( DebugLevel inLevel, const char *inFormat, ... );
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@function	DebugPrintFVAList
+
+	@abstract	va_list version of DebugPrintF. See DebugPrintF for more info.
+*/
+
+#if( DEBUG )
+	DEBUG_EXPORT size_t		DebugPrintFVAList( DebugLevel inLevel, const char *inFormat, va_list inArgs );
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@function	DebugPrintAssert
+
+	@abstract	Prints a message describing the reason the (e.g. an assert failed), an optional error message, 
+				an optional source filename, an optional source line number.
+
+	@param		inErrorCode			Error that generated this assert or noErr.
+	@param		inAssertString		C string containing assertion text.
+	@param		inMessage			C string containing a message about the assert.
+	@param		inFileName			C string containing path of file where the error occurred.
+	@param		inLineNumber		Line number in source file where the error occurred.
+	@param		inFunction			C string containing name of function where assert occurred.
+
+	@discussion
+	
+	Example output:	
+	
+	[ASSERT] assert: "dataPtr != NULL" allocate memory for object failed
+	[ASSERT] where:  "MyFile.c", line 123, ("MyFunction")
+	
+	OR
+	
+	[ASSERT] error:  -6728 (kNoMemoryErr)
+	[ASSERT] where:  "MyFile.c", line 123, ("MyFunction")
+*/
+
+#if( DEBUG )
+	DEBUG_EXPORT void
+		DebugPrintAssert( 
+			int_least32_t	inErrorCode, 
+			const char *	inAssertString, 
+			const char *	inMessage, 
+			const char *	inFilename, 
+			int_least32_t	inLineNumber, 
+			const char *	inFunction );
+#endif
+
+#if 0
+#pragma mark == Routines - Utilities ==
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@function	DebugSNPrintF
+	
+	@abstract	Debugging versions of standard C snprintf with extra features.
+	
+	@param		sbuffer		Buffer to receive result. Null terminated unless the buffer size is 0.
+	@param		buflen		Size of the buffer including space for the null terminator.
+	@param		fmt			printf-style format string.
+	@param		VAR_ARG		Variable number of arguments depending on the format string.
+
+	@result		Number of characters written (minus the null terminator).
+	
+	@discussion	
+	
+	Extra features over the standard C snprintf:
+	<pre>
+		64-bit support for %d (%lld), %i (%lli), %u (%llu), %o (%llo), %x (%llx), and %b (%llb).
+		%@   - Cocoa/CoreFoundation object. Param is the object. Strings are used directly. Others use CFCopyDescription.
+		%a   - Network Address: %.4a=IPv4, %.6a=Ethernet, %.8a Fibre Channel, %.16a=IPv6. Arg=ptr to network address.
+		%#a  - IPv4 or IPv6 mDNSAddr. Arg=ptr to mDNSAddr.
+		%##a - IPv4 (if AF_INET defined) or IPv6 (if AF_INET6 defined) sockaddr. Arg=ptr to sockaddr.
+		%b   - Binary representation of integer (e.g. 01101011). Modifiers and arg=the same as %d, %x, etc.
+		%C   - Mac-style FourCharCode (e.g. 'APPL'). Arg=32-bit value to print as a Mac-style FourCharCode.
+		%H   - Hex Dump (e.g. "\x6b\xa7" -> "6B A7"). 1st arg=ptr, 2nd arg=size, 3rd arg=max size.
+		%#H  - Hex Dump & ASCII (e.g. "\x41\x62" -> "6B A7 'Ab'"). 1st arg=ptr, 2nd arg=size, 3rd arg=max size.
+		%m   - Error Message (e.g. 0 -> "kNoErr"). Modifiers and error code arg=the same as %d, %x, etc.
+		%#s  - Pascal-style length-prefixed string. Arg=ptr to string.
+		%##s - DNS label-sequence name. Arg=ptr to name.
+		%S   - UTF-16 string, 0x0000 terminated. Host order if no BOM. Precision is UTF-16 count. Precision includes BOM.
+		%#S  - Big Endian UTF-16 string (unless BOM overrides). Otherwise, the same as %S.
+		%##S - Little Endian UTF-16 string (unless BOM overrides). Otherwise, the same as %S.
+		%U   - Universally Unique Identifier (UUID) (e.g. 6ba7b810-9dad-11d1-80b4-00c04fd430c8). Arg=ptr to 16-byte UUID.
+	</pre>
+*/
+
+#if( DEBUG )
+	DEBUG_EXPORT size_t DebugSNPrintF(char *sbuffer, size_t buflen, const char *fmt, ...);
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@function	DebugSNPrintFVAList
+
+	@abstract	va_list version of DebugSNPrintF. See DebugSNPrintF for more info.
+*/
+
+#if( DEBUG )
+	DEBUG_EXPORT size_t DebugSNPrintFVAList(char *sbuffer, size_t buflen, const char *fmt, va_list arg);
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@function	DebugGetErrorString
+
+	@abstract	Gets an error string from an error code.
+
+	@param		inStatus		Error code to get the string for.
+	@param		inBuffer		Optional buffer to copy the string to for non-static strings. May be null.
+	@param		inBufferSize	Size of optional buffer. May be 0.
+	
+	@result		C string containing error string for the error code. Guaranteed to be a valid, static string. If a 
+				buffer is supplied, the return value will always be a pointer to the supplied buffer, which will 
+				contain the best available description of the error code. If a buffer is not supplied, the return
+				value will be the best available description of the error code that can be represented as a static 
+				string. This allows code that cannot use a temporary buffer to hold the result to still get a useful 
+				error string in most cases, but also allows code that can use a temporary buffer to get the best 
+				available description.
+*/
+
+#if( DEBUG )
+	DEBUG_EXPORT const char *	DebugGetErrorString( int_least32_t inErrorCode, char *inBuffer, size_t inBufferSize );
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@function	DebugHexDump
+
+	@abstract	Hex dumps data to a string or to the output device.
+*/
+
+#if( DEBUG )
+	DEBUG_EXPORT size_t
+		DebugHexDump( 
+			DebugLevel		inLevel, 
+			int				inIndent, 
+			const char * 	inLabel, 
+			size_t 			inLabelSize, 
+			int				inLabelMinWidth, 
+			const char *	inType, 
+			size_t 			inTypeSize, 
+			const void *	inDataStart, 
+			const void *	inData, 
+			size_t 			inDataSize, 
+			DebugFlags	 	inFlags, 
+			char *			outBuffer, 
+			size_t			inBufferSize );
+#endif
+
+#if( DEBUG )
+	#define	dloghex( LEVEL, INDENT, LABEL, LABEL_SIZE, LABEL_MIN_SIZE, TYPE, TYPE_SIZE, DATA_START, DATA, DATA_SIZE, FLAGS, BUFFER, BUFFER_SIZE )	\
+		DebugHexDump( ( LEVEL ), (INDENT), ( LABEL ), ( LABEL_SIZE ), ( LABEL_MIN_SIZE ), ( TYPE ), ( TYPE_SIZE ), 								\
+			( DATA_START ), ( DATA ), ( DATA_SIZE ), ( FLAGS ), ( BUFFER ), ( BUFFER_SIZE ) )
+#else
+	#define	dloghex( LEVEL, INDENT, LABEL, LABEL_SIZE, LABEL_MIN_SIZE, TYPE, TYPE_SIZE, DATA_START, DATA, DATA_SIZE, FLAGS, BUFFER, BUFFER_SIZE )
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@function	DebugTaskLevel
+
+	@abstract	Returns the current task level.
+
+	@result		Current task level
+	
+	@discussion	
+	
+	Bit masks to isolate portions of the result (note that some masks may also need bit shifts to right justify):
+	<pre>
+		kDebugInterruptLevelMask				- Indicates the current interrupt level (> 0 means interrupt time).
+		kDebugInVBLTaskMask						- Indicates if a VBL task is currently being executed.
+		kDebugInDeferredTaskMask				- Indicates if a Deferred Task is currently being executed.
+		kDebugInSecondaryInterruptHandlerMask	- Indicates if a Secondary Interrupt Handler is currently being executed.
+		kDebugPageFaultFatalMask				- Indicates if it is unsafe to cause a page fault (worse than interrupt time).
+		kDebugMPTaskLevelMask					- Indicates if being called from an MP task.
+		kDebugInterruptDepthMask				- 0 means task level, 1 means in interrupt, > 1 means in nested interrupt.
+	</pre>
+	
+	Helpers:
+	<pre>
+		DebugExtractTaskLevelInterruptDepth()   - Macro to extract interrupt depth from task level value.
+	</pre>
+*/
+
+#if( DEBUG )
+	DEBUG_EXPORT uint32_t	DebugTaskLevel( void );
+#endif
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@function	DebugServicesTest
+
+	@abstract	Unit test.
+*/
+
+#if( DEBUG )
+	DEBUG_EXPORT OSStatus	DebugServicesTest( void );
+#endif
+
+#ifdef	__cplusplus
+	}
+#endif
+
+#endif	// __DEBUG_SERVICES__
diff --git a/mdnsresponder/mDNSShared/GenLinkedList.c b/mdnsresponder/mDNSShared/GenLinkedList.c
new file mode 100644
index 0000000..6e371b9
--- /dev/null
+++ b/mdnsresponder/mDNSShared/GenLinkedList.c
@@ -0,0 +1,319 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+
+ 	File:		GenLinkedList.c
+
+ 	Contains:	implementation of generic linked lists.
+
+ 	Version:	1.0
+ 	Tabs:		4 spaces
+ */
+
+#include "GenLinkedList.h"
+
+
+// Return the link pointer contained within element e at offset o.
+#define		GETLINK( e, o)			( *(void**)((char*) (e) + (o)) )
+
+// Assign the link pointer l to element e at offset o.
+#define		ASSIGNLINK( e, l, o)	( *((void**)((char*) (e) + (o))) = (l))
+
+
+//		GenLinkedList		/////////////////////////////////////////////////////////////
+
+void		InitLinkedList( GenLinkedList *pList, size_t linkOffset)
+/* Initialize the block of memory pointed to by pList as a linked list. */
+{
+	pList->Head = NULL;
+	pList->Tail = NULL;
+	pList->LinkOffset = linkOffset;
+}
+
+
+void		AddToTail( GenLinkedList *pList, void *elem)
+/* Add a linked list element to the tail of the list. */
+{
+	if ( pList->Tail) {
+		ASSIGNLINK( pList->Tail, elem, pList->LinkOffset);
+	} else
+		pList->Head = elem;
+	ASSIGNLINK( elem, NULL, pList->LinkOffset);
+
+	pList->Tail = elem;
+}
+
+
+void		AddToHead( GenLinkedList *pList, void *elem)
+/* Add a linked list element to the head of the list. */
+{
+	ASSIGNLINK( elem, pList->Head, pList->LinkOffset);
+	if ( pList->Tail == NULL) 
+		pList->Tail = elem;
+
+	pList->Head = elem;
+}
+
+
+int		RemoveFromList( GenLinkedList *pList, void *elem)
+/* Remove a linked list element from the list. Return 0 if it was not found. */
+/* If the element is removed, its link will be set to NULL. */
+{
+void	*iElem, *lastElem;
+
+	for ( iElem = pList->Head, lastElem = NULL; iElem; iElem = GETLINK( iElem, pList->LinkOffset)) {
+		if ( iElem == elem) {
+			if ( lastElem) {		// somewhere past the head
+				ASSIGNLINK( lastElem, GETLINK( elem, pList->LinkOffset), pList->LinkOffset);
+			} else {				// at the head
+				pList->Head = GETLINK( elem, pList->LinkOffset);
+			}
+			if ( pList->Tail == elem)
+				pList->Tail = lastElem ? lastElem : NULL;
+			ASSIGNLINK( elem, NULL, pList->LinkOffset);			// maybe catch a stale reference bug.
+			return 1;
+		}
+		lastElem = iElem;
+	}
+	
+	return 0;
+}
+
+
+int			ReplaceElem( GenLinkedList *pList, void *elemInList, void *newElem)
+/* Replace an element in the list with a new element, in the same position. */
+{
+void	*iElem, *lastElem;
+
+	if ( elemInList == NULL || newElem == NULL)
+		return 0;
+
+	for ( iElem = pList->Head, lastElem = NULL; iElem; iElem = GETLINK( iElem, pList->LinkOffset)) 
+	{
+		if ( iElem == elemInList)
+		{
+			ASSIGNLINK( newElem, GETLINK( elemInList, pList->LinkOffset), pList->LinkOffset);
+			if ( lastElem)		// somewhere past the head
+			{
+				ASSIGNLINK( lastElem, newElem, pList->LinkOffset);
+			} 
+			else				// at the head
+			{
+				pList->Head = newElem;
+			}
+			if ( pList->Tail == elemInList)
+				pList->Tail = newElem;
+			return 1;
+		}
+		lastElem = iElem;
+	}
+
+	return 0;
+}
+
+
+//		GenDoubleLinkedList		/////////////////////////////////////////////////////////
+
+void		InitDoubleLinkedList( GenDoubleLinkedList *pList, size_t fwdLinkOffset,
+									  size_t backLinkOffset)
+/* Initialize the block of memory pointed to by pList as a double linked list. */
+{
+	pList->Head = NULL;
+	pList->Tail = NULL;
+	pList->FwdLinkOffset = fwdLinkOffset;
+	pList->BackLinkOffset = backLinkOffset;
+}
+
+
+void			DLLAddToHead( GenDoubleLinkedList *pList, void *elem)
+/* Add a linked list element to the head of the list. */
+{
+void		*pNext;
+
+	pNext = pList->Head;
+
+	// fix up the forward links
+	ASSIGNLINK( elem, pList->Head, pList->FwdLinkOffset);
+	pList->Head = elem;
+
+	// fix up the backward links
+	if ( pNext) {
+		ASSIGNLINK( pNext, elem, pList->BackLinkOffset);
+	} else
+		pList->Tail = elem;
+	ASSIGNLINK( elem, NULL, pList->BackLinkOffset);
+}
+
+
+void			DLLRemoveFromList( GenDoubleLinkedList *pList, void *elem)
+/* Remove a linked list element from the list. */
+/* When the element is removed, its link will be set to NULL. */
+{
+void		*pNext, *pPrev;
+
+	pNext = GETLINK( elem, pList->FwdLinkOffset);
+	pPrev = GETLINK( elem, pList->BackLinkOffset);
+
+	// fix up the forward links
+	if ( pPrev)
+		ASSIGNLINK( pPrev, pNext, pList->FwdLinkOffset);
+	else
+		pList->Head = pNext;
+		
+	// fix up the backward links
+	if ( pNext)	
+		ASSIGNLINK( pNext, pPrev, pList->BackLinkOffset);
+	else
+		pList->Tail = pPrev;
+
+	ASSIGNLINK( elem, NULL, pList->FwdLinkOffset);
+	ASSIGNLINK( elem, NULL, pList->BackLinkOffset);
+}
+
+
+//		GenLinkedOffsetList		/////////////////////////////////////////////////////
+
+// Extract the Next offset from element
+#define		GETOFFSET( e, o)	( *(size_t*)((char*) (e) + (o)) )
+
+static void		AssignOffsetLink( void *elem, void *link, size_t linkOffset);
+
+
+static void	AssignOffsetLink( void *elem, void *link, size_t linkOffset)
+// Assign link to elem as an offset from elem. Assign 0 to elem if link is NULL.
+{
+	GETOFFSET( elem, linkOffset) = link ? (size_t) link - (size_t) elem : 0;
+}
+
+
+void		*GetHeadPtr( GenLinkedOffsetList *pList)
+/* Return a pointer to the head element of a list, or NULL if none. */
+{
+	return pList->Head ? ( (char*) (pList) + pList->Head) : NULL;
+}
+
+
+void		*GetTailPtr( GenLinkedOffsetList *pList)
+/* Return a pointer to the tail element of a list, or NULL if none. */
+{
+	return pList->Tail ? ( (char*) (pList) + pList->Tail) : NULL;
+}
+
+
+void		*GetOffsetLink( GenLinkedOffsetList *pList, void *elem)
+/* Return the link pointer contained within element e for pList, or NULL if it is 0. */
+{
+size_t		nextOffset;
+
+	nextOffset = GETOFFSET( elem, pList->LinkOffset);
+
+	return nextOffset ? (char*) elem + nextOffset : NULL;
+}
+
+
+void		InitLinkedOffsetList( GenLinkedOffsetList *pList, size_t linkOffset)
+/* Initialize the block of memory pointed to by pList as a linked list. */
+{
+	pList->Head = 0;
+	pList->Tail = 0;
+	pList->LinkOffset = linkOffset;
+}
+
+
+void		OffsetAddToTail( GenLinkedOffsetList *pList, void *elem)
+/* Add a linked list element to the tail of the list. */
+{
+	if ( pList->Tail) {
+		AssignOffsetLink( GetTailPtr( pList), elem, pList->LinkOffset);
+	} else
+		pList->Head = (size_t) elem - (size_t) pList;
+	AssignOffsetLink( elem, NULL, pList->LinkOffset);
+
+	pList->Tail = (size_t) elem - (size_t) pList;
+}
+
+
+void		OffsetAddToHead( GenLinkedOffsetList *pList, void *elem)
+/* Add a linked list element to the head of the list. */
+{
+	AssignOffsetLink( elem, GetHeadPtr( pList), pList->LinkOffset);
+	if ( pList->Tail == 0) 
+		pList->Tail = (size_t) elem - (size_t) pList;
+
+	pList->Head = (size_t) elem - (size_t) pList;
+}
+
+
+int		OffsetRemoveFromList( GenLinkedOffsetList *pList, void *elem)
+/* Remove a linked list element from the list. Return 0 if it was not found. */
+/* If the element is removed, its link will be set to NULL. */
+{
+void	*iElem, *lastElem;
+
+	for ( iElem = GetHeadPtr( pList), lastElem = NULL; iElem; 
+		  iElem = GetOffsetLink( pList, iElem))
+	{
+		if ( iElem == elem) {
+			if ( lastElem) {		// somewhere past the head
+				AssignOffsetLink( lastElem, GetOffsetLink( pList, elem), pList->LinkOffset);
+			} else {				// at the head
+				iElem = GetOffsetLink( pList, elem);
+				pList->Head = iElem ? (size_t) iElem - (size_t) pList : 0;
+			}
+			if ( GetTailPtr( pList) == elem)
+				pList->Tail = lastElem ? (size_t) lastElem - (size_t) pList : 0;
+			AssignOffsetLink( elem, NULL, pList->LinkOffset);	// maybe catch a stale reference bug.
+			return 1;
+		}
+		lastElem = iElem;
+	}
+
+	return 0;
+}
+
+
+int			OffsetReplaceElem( GenLinkedOffsetList *pList, void *elemInList, void *newElem)
+/* Replace an element in the list with a new element, in the same position. */
+{
+void	*iElem, *lastElem;
+
+	if ( elemInList == NULL || newElem == NULL)
+		return 0;
+
+	for ( iElem = GetHeadPtr( pList), lastElem = NULL; iElem; 
+		  iElem = GetOffsetLink( pList, iElem))
+	{
+		if ( iElem == elemInList)
+		{
+			AssignOffsetLink( newElem, GetOffsetLink( pList, elemInList), pList->LinkOffset);
+			if ( lastElem)		// somewhere past the head
+			{
+				AssignOffsetLink( lastElem, newElem, pList->LinkOffset);
+			} 
+			else				// at the head
+			{
+				pList->Head = (size_t) newElem - (size_t) pList;
+			}
+			if ( GetTailPtr( pList) == elemInList)
+				pList->Tail = (size_t) newElem - (size_t) pList;
+			return 1;
+		}
+		lastElem = iElem;
+	}
+
+	return 0;
+}
+
+
diff --git a/mdnsresponder/mDNSShared/GenLinkedList.h b/mdnsresponder/mDNSShared/GenLinkedList.h
new file mode 100644
index 0000000..4e17708
--- /dev/null
+++ b/mdnsresponder/mDNSShared/GenLinkedList.h
@@ -0,0 +1,90 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+#ifndef __GenLinkedList__
+#define __GenLinkedList__
+
+
+#include <stddef.h>
+
+
+struct	GenLinkedList
+{
+	void		*Head,
+				*Tail;
+	size_t		LinkOffset;
+};
+typedef struct GenLinkedList	GenLinkedList;
+
+
+void		InitLinkedList( GenLinkedList *pList, size_t linkOffset);
+
+void		AddToHead( GenLinkedList *pList, void *elem);
+void		AddToTail( GenLinkedList *pList, void *elem);
+
+int		RemoveFromList( GenLinkedList *pList, void *elem);
+
+int		ReplaceElem( GenLinkedList *pList, void *elemInList, void *newElem);
+
+
+
+struct	GenDoubleLinkedList
+{
+	void		*Head,
+				*Tail;
+	size_t		FwdLinkOffset,
+				BackLinkOffset;
+};
+typedef struct GenDoubleLinkedList	GenDoubleLinkedList;
+
+
+void		InitDoubleLinkedList( GenDoubleLinkedList *pList, size_t fwdLinkOffset,
+									  size_t backLinkOffset);
+
+void		DLLAddToHead( GenDoubleLinkedList *pList, void *elem);
+
+void		DLLRemoveFromList( GenDoubleLinkedList *pList, void *elem);
+
+
+
+/* A GenLinkedOffsetList is like a GenLinkedList that stores the *Next field as a signed */
+/* offset from the address of the beginning of the element, rather than as a pointer. */
+
+struct	GenLinkedOffsetList
+{
+	size_t		Head,
+				Tail;
+	size_t		LinkOffset;
+};
+typedef struct GenLinkedOffsetList	GenLinkedOffsetList;
+
+
+void		InitLinkedOffsetList( GenLinkedOffsetList *pList, size_t linkOffset);
+
+void		*GetHeadPtr( GenLinkedOffsetList *pList);
+void		*GetTailPtr( GenLinkedOffsetList *pList);
+void		*GetOffsetLink( GenLinkedOffsetList *pList, void *elem);
+
+void		OffsetAddToHead( GenLinkedOffsetList *pList, void *elem);
+void		OffsetAddToTail( GenLinkedOffsetList *pList, void *elem);
+
+int		OffsetRemoveFromList( GenLinkedOffsetList *pList, void *elem);
+
+int		OffsetReplaceElem( GenLinkedOffsetList *pList, void *elemInList, void *newElem);
+
+
+#endif //	__GenLinkedList__
diff --git a/mdnsresponder/mDNSShared/PlatformCommon.c b/mdnsresponder/mDNSShared/PlatformCommon.c
new file mode 100644
index 0000000..2fb530e
--- /dev/null
+++ b/mdnsresponder/mDNSShared/PlatformCommon.c
@@ -0,0 +1,220 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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 <stdio.h>				// Needed for fopen() etc.
+#include <unistd.h>				// Needed for close()
+#include <string.h>				// Needed for strlen() etc.
+#include <errno.h>				// Needed for errno etc.
+#include <sys/socket.h>			// Needed for socket() etc.
+#include <netinet/in.h>			// Needed for sockaddr_in
+#include <syslog.h>
+
+#include "mDNSEmbeddedAPI.h"	// Defines the interface provided to the client layer above
+#include "DNSCommon.h"
+#include "PlatformCommon.h"
+
+#ifdef __ANDROID__
+#include <android/log.h>
+#endif
+
+#ifdef NOT_HAVE_SOCKLEN_T
+    typedef unsigned int socklen_t;
+#endif
+
+// Bind a UDP socket to find the source address to a destination
+mDNSexport void mDNSPlatformSourceAddrForDest(mDNSAddr *const src, const mDNSAddr *const dst)
+	{
+	union { struct sockaddr s; struct sockaddr_in a4; struct sockaddr_in6 a6; } addr;
+	socklen_t len = sizeof(addr);
+	socklen_t inner_len = 0;
+	int sock = socket(AF_INET, SOCK_DGRAM, 0);
+	src->type = mDNSAddrType_None;
+	if (sock == -1) return;
+	if (dst->type == mDNSAddrType_IPv4)
+		{
+		inner_len = sizeof(addr.a4);
+		#ifndef NOT_HAVE_SA_LEN
+		addr.a4.sin_len         = inner_len;
+		#endif
+		addr.a4.sin_family      = AF_INET;
+		addr.a4.sin_port        = 1;	// Not important, any port will do
+		addr.a4.sin_addr.s_addr = dst->ip.v4.NotAnInteger;
+		}
+	else if (dst->type == mDNSAddrType_IPv6)
+		{
+		inner_len = sizeof(addr.a6);
+		#ifndef NOT_HAVE_SA_LEN
+		addr.a6.sin6_len      = inner_len;
+		#endif
+		addr.a6.sin6_family   = AF_INET6;
+		addr.a6.sin6_flowinfo = 0;
+		addr.a6.sin6_port     = 1;	// Not important, any port will do
+		addr.a6.sin6_addr     = *(struct in6_addr*)&dst->ip.v6;
+		addr.a6.sin6_scope_id = 0;
+		}
+	else return;
+
+	if ((connect(sock, &addr.s, inner_len)) < 0)
+		{ LogMsg("mDNSPlatformSourceAddrForDest: connect %#a failed errno %d (%s)", dst, errno, strerror(errno)); goto exit; }
+
+	if ((getsockname(sock, &addr.s, &len)) < 0)
+		{ LogMsg("mDNSPlatformSourceAddrForDest: getsockname failed errno %d (%s)", errno, strerror(errno)); goto exit; }
+
+	src->type = dst->type;
+	if (dst->type == mDNSAddrType_IPv4) src->ip.v4.NotAnInteger = addr.a4.sin_addr.s_addr;
+	else                                src->ip.v6 = *(mDNSv6Addr*)&addr.a6.sin6_addr;
+exit:
+	close(sock);
+	}
+
+// dst must be at least MAX_ESCAPED_DOMAIN_NAME bytes, and option must be less than 32 bytes in length
+mDNSlocal mDNSBool GetConfigOption(char *dst, const char *option, FILE *f)
+	{
+	char buf[32+1+MAX_ESCAPED_DOMAIN_NAME];	// Option name, one space, option value
+	unsigned int len = strlen(option);
+	if (len + 1 + MAX_ESCAPED_DOMAIN_NAME > sizeof(buf)-1) { LogMsg("GetConfigOption: option %s too long", option); return mDNSfalse; }
+	fseek(f, 0, SEEK_SET);  // set position to beginning of stream
+	while (fgets(buf, sizeof(buf), f))		// Read at most sizeof(buf)-1 bytes from file, and append '\0' C-string terminator
+		{
+		if (!strncmp(buf, option, len))
+			{
+			strncpy(dst, buf + len + 1, MAX_ESCAPED_DOMAIN_NAME-1);
+			if (dst[MAX_ESCAPED_DOMAIN_NAME-1]) dst[MAX_ESCAPED_DOMAIN_NAME-1] = '\0';
+			len = strlen(dst);
+			if (len && dst[len-1] == '\n') dst[len-1] = '\0';  // chop newline
+			return mDNStrue;
+			}
+		}
+	debugf("Option %s not set", option);
+	return mDNSfalse;
+	}
+
+mDNSexport void ReadDDNSSettingsFromConfFile(mDNS *const m, const char *const filename, domainname *const hostname, domainname *const domain, mDNSBool *DomainDiscoveryDisabled)
+	{
+	char buf[MAX_ESCAPED_DOMAIN_NAME] = "";
+	mStatus err;
+	FILE *f = fopen(filename, "r");
+
+    if (hostname)                 hostname->c[0] = 0;
+    if (domain)                   domain->c[0] = 0;
+	if (DomainDiscoveryDisabled) *DomainDiscoveryDisabled = mDNSfalse;
+
+	if (f)
+		{
+		if (DomainDiscoveryDisabled && GetConfigOption(buf, "DomainDiscoveryDisabled", f) && !strcasecmp(buf, "true")) *DomainDiscoveryDisabled = mDNStrue;
+		if (hostname && GetConfigOption(buf, "hostname", f) && !MakeDomainNameFromDNSNameString(hostname, buf)) goto badf;
+		if (domain && GetConfigOption(buf, "zone", f) && !MakeDomainNameFromDNSNameString(domain, buf)) goto badf;
+		buf[0] = 0;
+		GetConfigOption(buf, "secret-64", f);  // failure means no authentication
+		fclose(f);
+		f = NULL;
+		}
+	else
+		{
+		if (errno != ENOENT) LogMsg("ERROR: Config file exists, but cannot be opened.");
+		return;
+		}
+
+	if (domain && domain->c[0] && buf[0])
+		{
+		DomainAuthInfo *info = (DomainAuthInfo*)mDNSPlatformMemAllocate(sizeof(*info));
+		// for now we assume keyname = service reg domain and we use same key for service and hostname registration
+		err = mDNS_SetSecretForDomain(m, info, domain, domain, buf, NULL, 0, NULL);
+		if (err) LogMsg("ERROR: mDNS_SetSecretForDomain returned %d for domain %##s", err, domain->c);
+		}
+
+	return;
+
+	badf:
+	LogMsg("ERROR: malformatted config file");
+	if (f) fclose(f);
+	}
+
+#if MDNS_DEBUGMSGS
+mDNSexport void mDNSPlatformWriteDebugMsg(const char *msg)
+	{
+#ifdef __ANDROID__
+	__android_log_print(ANDROID_LOG_DEBUG, "mdns", "%s", msg);
+#else
+	fprintf(stderr,"%s\n", msg);
+	fflush(stderr);
+#endif
+	}
+#endif
+
+mDNSexport void mDNSPlatformWriteLogMsg(const char *ident, const char *buffer, mDNSLogLevel_t loglevel)
+	{
+#if APPLE_OSX_mDNSResponder && LogTimeStamps
+	extern mDNS mDNSStorage;
+	extern mDNSu32 mDNSPlatformClockDivisor;
+	mDNSs32 t = mDNSStorage.timenow ? mDNSStorage.timenow : mDNSPlatformClockDivisor ? mDNS_TimeNow_NoLock(&mDNSStorage) : 0;
+	int ms = ((t < 0) ? -t : t) % 1000;
+#endif
+
+	if (mDNS_DebugMode)	// In debug mode we write to stderr
+		{
+#if APPLE_OSX_mDNSResponder && LogTimeStamps
+		if (ident && ident[0] && mDNSPlatformClockDivisor)
+			fprintf(stderr,"%8d.%03d: %s\n", (int)(t/1000), ms, buffer);
+		else
+#endif
+			fprintf(stderr,"%s\n", buffer);
+		fflush(stderr);
+		}
+	else				// else, in production mode, we write to syslog
+		{
+		static int log_inited = 0;
+		
+		int syslog_level = LOG_ERR;
+		switch (loglevel) 
+			{
+			case MDNS_LOG_MSG:       syslog_level = LOG_ERR;     break;
+			case MDNS_LOG_OPERATION: syslog_level = LOG_WARNING; break;
+			case MDNS_LOG_SPS:       syslog_level = LOG_NOTICE;  break;
+			case MDNS_LOG_INFO:      syslog_level = LOG_INFO;    break;
+			case MDNS_LOG_DEBUG:     syslog_level = LOG_DEBUG;   break;
+			default:
+			fprintf(stderr, "Unknown loglevel %d, assuming LOG_ERR\n", loglevel);
+			fflush(stderr);
+			}
+		
+		if (!log_inited) { openlog(ident, LOG_CONS, LOG_DAEMON); log_inited++; }
+
+#if APPLE_OSX_mDNSResponder && LogTimeStamps
+		if (ident && ident[0] && mDNSPlatformClockDivisor)
+			syslog(syslog_level, "%8d.%03d: %s", (int)(t/1000), ms, buffer);
+		else
+#endif
+#ifdef __ANDROID__
+		switch (loglevel)
+			{
+			case MDNS_LOG_DEBUG:     syslog_level = ANDROID_LOG_DEBUG;  break;
+#if MDNS_DEBUGMSGS > 0
+			case MDNS_LOG_OPERATION: syslog_level = ANDROID_LOG_WARN;   break;
+			case MDNS_LOG_SPS:       syslog_level = ANDROID_LOG_DEBUG;  break;
+			case MDNS_LOG_INFO:      syslog_level = ANDROID_LOG_INFO;   break;
+			default:                 syslog_level = ANDROID_LOG_ERROR;  break;
+#else
+			default:		return;
+#endif
+			}
+		__android_log_print(syslog_level, "mdns", "%s", buffer);
+#else
+			syslog(syslog_level, "%s", buffer);
+#endif
+		}
+	}
diff --git a/mdnsresponder/mDNSShared/PlatformCommon.h b/mdnsresponder/mDNSShared/PlatformCommon.h
new file mode 100644
index 0000000..631e3d7
--- /dev/null
+++ b/mdnsresponder/mDNSShared/PlatformCommon.h
@@ -0,0 +1,18 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+extern void ReadDDNSSettingsFromConfFile(mDNS *const m, const char *const filename, domainname *const hostname, domainname *const domain, mDNSBool *DomainDiscoveryDisabled);
diff --git a/mdnsresponder/mDNSShared/dns-sd.1 b/mdnsresponder/mDNSShared/dns-sd.1
new file mode 100644
index 0000000..da9f37a
--- /dev/null
+++ b/mdnsresponder/mDNSShared/dns-sd.1
@@ -0,0 +1,194 @@
+.\" -*- tab-width: 4 -*-
+.\" 
+.\" Copyright (c) 2004 Apple Computer, Inc. All Rights Reserved.
+.\" 
+.\" Licensed 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.
+.\"
+.Dd April 2004              \" Date
+.Dt dns-sd 1                \" Document Title
+.Os Darwin                  \" Operating System
+.\"
+.Sh NAME
+.Nm dns-sd
+.Nd Multicast DNS (mDNS) & DNS Service Discovery (DNS-SD) Test Tool \" For whatis
+.\" 
+.Sh SYNOPSIS
+.Nm Fl R Ar name type domain port Op Ar key=value ...
+.Pp
+.Nm Fl B Ar      type domain
+.Pp
+.Nm Fl L Ar name type domain
+.\"
+.Sh DESCRIPTION
+The
+.Nm
+command is a network diagnostic tool, much like
+.Xr ping 8
+or
+.Xr traceroute 8 .
+However, unlike those tools, most of its functionality is not implemented in the
+.Nm
+executable itself, but in library code that is available to any application.
+The library API that
+.Nm
+uses is documented in
+.Pa /usr/include/dns_sd.h .
+The
+.Nm
+command replaces the older
+.Xr mDNS 1
+command.
+.Pp
+The
+.Nm
+command is primarily intended for interactive use.
+Because its command-line arguments and output format are subject to change,
+invoking it from a shell script will generally be fragile. Additionally,
+the asynchronous nature of DNS Service Discovery does
+not lend itself easily to script-oriented programming. For example,
+calls like "browse" never complete; the action of performing a "browse"
+sets in motion machinery to notify the client whenever instances of
+that service type appear or disappear from the network. These
+notifications continue to be delivered indefinitely, for minutes,
+hours, or even days, as services come and go, until the client
+explicitly terminates the call. This style of asynchronous interaction
+works best with applications that are either multi-threaded, or use a
+main event-handling loop to receive keystrokes, network data, and other
+asynchronous event notifications as they happen.
+.br
+If you wish to perform DNS Service Discovery operations from a
+scripting language, then the best way to do this is not to execute the
+.Nm
+command and then attempt to decipher the textual output, but instead to
+directly call the DNS-SD APIs using a binding for your chosen language.
+.br
+For example, if you are programming in Ruby, then you can
+directly call DNS-SD APIs using the dnssd package documented at
+.Pa <http://rubyforge.org/projects/dnssd/> .
+.br
+Similar bindings for other languages are also in development.
+.Pp
+.Bl -tag -width R
+.It Nm Fl R Ar name type domain port Op Ar key=value ...
+register (advertise) a service in the specified
+.Ar domain 
+with the given
+.Ar name
+and
+.Ar type
+as listening (on the current machine) on
+.Ar port.
+.Pp
+.Ar name
+can be arbitrary unicode text, containing any legal unicode characters
+(including dots, spaces, slashes, colons, etc. without restriction),
+up to 63 UTF-8 bytes long.
+.Ar type
+must be of the form "_app-proto._tcp" or "_app-proto._udp", where
+"app-proto" is an application protocol name registered at
+.Pa http://www.dns-sd.org/ServiceTypes.html .
+.Pp
+.Ar domain
+is the domain in which to register the service.
+In current implementations, only the local multicast domain "local" is
+supported. In the future, registering will be supported in any arbitrary
+domain that has a working DNS Update server [RFC 2136]. The
+.Ar domain
+"." is a synonym for "pick a sensible default" which today
+means "local".
+.Pp
+.Ar port
+is a number from 0 to 65535, and is the TCP or UDP port number upon
+which the service is listening.
+.Pp 
+Additional attributes of the service may optionally be described by
+key/value pairs, which are stored in the advertised service's DNS TXT
+record. Allowable keys and values are listed with the service
+registration at
+.Pa http://www.dns-sd.org/ServiceTypes.html .
+.It Nm Fl B Ar type domain
+browse for instances of service
+.Ar type
+in
+.Ar domain .
+.Pp
+For valid 
+.Ar type Ns s
+see
+.Pa http://www.dns-sd.org/ServiceTypes.html
+as described above. Omitting the
+.Ar domain
+or using "." means "pick a sensible default."
+.It Nm Fl L Ar name type domain
+look up and display the information necessary to contact and use the
+named service: the hostname of the machine where that service is
+available, the port number on which the service is listening, and (if
+present) TXT record attributes describing properties of the service.
+.Pp
+Note that in a typical application, browsing happens rarely, while lookup
+(or "resolving") happens every time the service is used. For example, a
+user browses the network to pick a default printer fairly rarely, but once
+a default printer has been picked, that named service is resolved to its
+current IP address and port number every time the user presses Cmd-P to
+print.
+.El
+.Sh EXAMPLES
+.Pp
+To advertise the existence of LPR printing service on port 515 on this
+machine, such that it will be discovered by the Mac OS X printing software
+and other DNS-SD compatible printing clients, use:
+.Pp
+.Dl Nm Fl R Ns \ \&"My Test\&" _printer._tcp. \&. 515 pdl=application/postscript
+.Pp
+For this registration to be useful, you need to actually have LPR service
+available on port 515. Advertising a service that does not exist is not
+very useful, and will be confusing and annoying to other people on the
+network.
+.Pp
+Similarly, to advertise a web page being served by an HTTP
+server on port 80 on this machine, such that it will show up in the
+Bonjour list in Safari and other DNS-SD compatible Web clients, use:
+.Pp
+.Dl Nm Fl R Ns \ \&"My Test\&" _http._tcp \&. 80 path=/path-to-page.html
+.Pp
+To find the advertised web pages on the local network (the same list that
+Safari shows), use:
+.Pp
+.Dl Nm Fl B Ns \ _http._tcp
+.Pp
+While that command is running, in another window, try the
+.Nm Fl R
+example given above to advertise a web page, and you should see the
+"Add" event reported to the
+.Nm Fl B
+window. Now press Ctrl-C in the
+.Nm Fl R
+window and you should see the "Remove" event reported to the
+.Nm Fl B
+window.
+.Pp
+.Sh FILES
+.Pa /usr/bin/dns-sd \" Pathname
+.\"
+.Sh SEE ALSO
+.Xr mDNS 1
+.Xr mDNSResponder 8
+.\"
+.Sh BUGS
+.Nm
+bugs are tracked in Apple Radar component "mDNSResponder".
+.\"
+.Sh HISTORY
+The
+.Nm
+command first appeared in Mac OS X 10.4 (Tiger).
diff --git a/mdnsresponder/mDNSShared/dns_sd.h b/mdnsresponder/mDNSShared/dns_sd.h
new file mode 100644
index 0000000..f013691
--- /dev/null
+++ b/mdnsresponder/mDNSShared/dns_sd.h
@@ -0,0 +1,2452 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2003-2004, Apple Computer, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of its
+ *     contributors may be used to endorse or promote products derived from this
+ *     software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+
+/*! @header     DNS Service Discovery
+ *
+ * @discussion  This section describes the functions, callbacks, and data structures
+ *              that make up the DNS Service Discovery API.
+ *
+ *              The DNS Service Discovery API is part of Bonjour, Apple's implementation
+ *              of zero-configuration networking (ZEROCONF).
+ *
+ *              Bonjour allows you to register a network service, such as a
+ *              printer or file server, so that it can be found by name or browsed
+ *              for by service type and domain. Using Bonjour, applications can
+ *              discover what services are available on the network, along with
+ *              all the information -- such as name, IP address, and port --
+ *              necessary to access a particular service.
+ *
+ *              In effect, Bonjour combines the functions of a local DNS server and
+ *              AppleTalk. Bonjour allows applications to provide user-friendly printer
+ *              and server browsing, among other things, over standard IP networks.
+ *              This behavior is a result of combining protocols such as multicast and
+ *              DNS to add new functionality to the network (such as multicast DNS).
+ *
+ *              Bonjour gives applications easy access to services over local IP
+ *              networks without requiring the service or the application to support
+ *              an AppleTalk or a Netbeui stack, and without requiring a DNS server
+ *              for the local network.
+ */
+
+
+/* _DNS_SD_H contains the mDNSResponder version number for this header file, formatted as follows:
+ *   Major part of the build number * 10000 +
+ *   minor part of the build number *   100
+ * For example, Mac OS X 10.4.9 has mDNSResponder-108.4, which would be represented as
+ * version 1080400. This allows C code to do simple greater-than and less-than comparisons:
+ * e.g. an application that requires the DNSServiceGetProperty() call (new in mDNSResponder-126) can check:
+ *
+ *   #if _DNS_SD_H+0 >= 1260000
+ *   ... some C code that calls DNSServiceGetProperty() ...
+ *   #endif
+ *
+ * The version defined in this header file symbol allows for compile-time
+ * checking, so that C code building with earlier versions of the header file
+ * can avoid compile errors trying to use functions that aren't even defined
+ * in those earlier versions. Similar checks may also be performed at run-time:
+ *  => weak linking -- to avoid link failures if run with an earlier
+ *     version of the library that's missing some desired symbol, or
+ *  => DNSServiceGetProperty(DaemonVersion) -- to verify whether the running daemon
+ *     ("system service" on Windows) meets some required minimum functionality level.
+ */
+
+#ifndef _DNS_SD_H
+#define _DNS_SD_H 3201080
+
+#ifdef  __cplusplus
+    extern "C" {
+#endif
+
+/* Set to 1 if libdispatch is supported
+ * Note: May also be set by project and/or Makefile
+ */
+#ifndef _DNS_SD_LIBDISPATCH
+#define _DNS_SD_LIBDISPATCH 0
+#endif /* ndef _DNS_SD_LIBDISPATCH */
+
+/* standard calling convention under Win32 is __stdcall */
+/* Note: When compiling Intel EFI (Extensible Firmware Interface) under MS Visual Studio, the */
+/* _WIN32 symbol is defined by the compiler even though it's NOT compiling code for Windows32 */
+#if defined(_WIN32) && !defined(EFI32) && !defined(EFI64)
+#define DNSSD_API __stdcall
+#else
+#define DNSSD_API
+#endif
+
+/* stdint.h does not exist on FreeBSD 4.x; its types are defined in sys/types.h instead */
+#if defined(__FreeBSD__) && (__FreeBSD__ < 5)
+#include <sys/types.h>
+
+/* Likewise, on Sun, standard integer types are in sys/types.h */
+#elif defined(__sun__)
+#include <sys/types.h>
+
+/* EFI does not have stdint.h, or anything else equivalent */
+#elif defined(EFI32) || defined(EFI64) || defined(EFIX64)
+#include "Tiano.h"
+#if !defined(_STDINT_H_)
+typedef UINT8       uint8_t;
+typedef INT8        int8_t;
+typedef UINT16      uint16_t;
+typedef INT16       int16_t;
+typedef UINT32      uint32_t;
+typedef INT32       int32_t;
+#endif
+/* Windows has its own differences */
+#elif defined(_WIN32)
+#include <windows.h>
+#define _UNUSED
+#ifndef _MSL_STDINT_H
+typedef UINT8       uint8_t;
+typedef INT8        int8_t;
+typedef UINT16      uint16_t;
+typedef INT16       int16_t;
+typedef UINT32      uint32_t;
+typedef INT32       int32_t;
+#endif
+
+/* All other Posix platforms use stdint.h */
+#else
+#include <stdint.h>
+#endif
+
+#if _DNS_SD_LIBDISPATCH
+#include <dispatch/dispatch.h>
+#endif
+
+/* DNSServiceRef, DNSRecordRef
+ *
+ * Opaque internal data types.
+ * Note: client is responsible for serializing access to these structures if
+ * they are shared between concurrent threads.
+ */
+
+typedef struct _DNSServiceRef_t *DNSServiceRef;
+typedef struct _DNSRecordRef_t *DNSRecordRef;
+
+struct sockaddr;
+
+/*! @enum General flags
+ * Most DNS-SD API functions and callbacks include a DNSServiceFlags parameter.
+ * As a general rule, any given bit in the 32-bit flags field has a specific fixed meaning,
+ * regardless of the function or callback being used. For any given function or callback,
+ * typically only a subset of the possible flags are meaningful, and all others should be zero.
+ * The discussion section for each API call describes which flags are valid for that call
+ * and callback. In some cases, for a particular call, it may be that no flags are currently
+ * defined, in which case the DNSServiceFlags parameter exists purely to allow future expansion.
+ * In all cases, developers should expect that in future releases, it is possible that new flag
+ * values will be defined, and write code with this in mind. For example, code that tests
+ *     if (flags == kDNSServiceFlagsAdd) ...
+ * will fail if, in a future release, another bit in the 32-bit flags field is also set.
+ * The reliable way to test whether a particular bit is set is not with an equality test,
+ * but with a bitwise mask:
+ *     if (flags & kDNSServiceFlagsAdd) ...
+ */
+enum
+    {
+    kDNSServiceFlagsMoreComing          = 0x1,
+    /* MoreComing indicates to a callback that at least one more result is
+     * queued and will be delivered following immediately after this one.
+     * When the MoreComing flag is set, applications should not immediately
+     * update their UI, because this can result in a great deal of ugly flickering
+     * on the screen, and can waste a great deal of CPU time repeatedly updating
+     * the screen with content that is then immediately erased, over and over.
+     * Applications should wait until until MoreComing is not set, and then
+     * update their UI when no more changes are imminent.
+     * When MoreComing is not set, that doesn't mean there will be no more
+     * answers EVER, just that there are no more answers immediately
+     * available right now at this instant. If more answers become available
+     * in the future they will be delivered as usual.
+     */
+
+    kDNSServiceFlagsAdd                 = 0x2,
+    kDNSServiceFlagsDefault             = 0x4,
+    /* Flags for domain enumeration and browse/query reply callbacks.
+     * "Default" applies only to enumeration and is only valid in
+     * conjunction with "Add". An enumeration callback with the "Add"
+     * flag NOT set indicates a "Remove", i.e. the domain is no longer
+     * valid.
+     */
+
+    kDNSServiceFlagsNoAutoRename        = 0x8,
+    /* Flag for specifying renaming behavior on name conflict when registering
+     * non-shared records. By default, name conflicts are automatically handled
+     * by renaming the service. NoAutoRename overrides this behavior - with this
+     * flag set, name conflicts will result in a callback. The NoAutorename flag
+     * is only valid if a name is explicitly specified when registering a service
+     * (i.e. the default name is not used.)
+     */
+
+    kDNSServiceFlagsShared              = 0x10,
+    kDNSServiceFlagsUnique              = 0x20,
+    /* Flag for registering individual records on a connected
+     * DNSServiceRef. Shared indicates that there may be multiple records
+     * with this name on the network (e.g. PTR records). Unique indicates that the
+     * record's name is to be unique on the network (e.g. SRV records).
+     */
+
+    kDNSServiceFlagsBrowseDomains       = 0x40,
+    kDNSServiceFlagsRegistrationDomains = 0x80,
+    /* Flags for specifying domain enumeration type in DNSServiceEnumerateDomains.
+     * BrowseDomains enumerates domains recommended for browsing, RegistrationDomains
+     * enumerates domains recommended for registration.
+     */
+
+    kDNSServiceFlagsLongLivedQuery      = 0x100,
+    /* Flag for creating a long-lived unicast query for the DNSServiceQueryRecord call. */
+
+    kDNSServiceFlagsAllowRemoteQuery    = 0x200,
+    /* Flag for creating a record for which we will answer remote queries
+     * (queries from hosts more than one hop away; hosts not directly connected to the local link).
+     */
+
+    kDNSServiceFlagsForceMulticast      = 0x400,
+    /* Flag for signifying that a query or registration should be performed exclusively via multicast
+     * DNS, even for a name in a domain (e.g. foo.apple.com.) that would normally imply unicast DNS.
+     */
+
+    kDNSServiceFlagsForce               = 0x800,
+    /* Flag for signifying a "stronger" variant of an operation.
+     * Currently defined only for DNSServiceReconfirmRecord(), where it forces a record to
+     * be removed from the cache immediately, instead of querying for a few seconds before
+     * concluding that the record is no longer valid and then removing it. This flag should
+     * be used with caution because if a service browsing PTR record is indeed still valid
+     * on the network, forcing its removal will result in a user-interface flap -- the
+     * discovered service instance will disappear, and then re-appear moments later.
+     */
+
+    kDNSServiceFlagsReturnIntermediates = 0x1000,
+    /* Flag for returning intermediate results.
+     * For example, if a query results in an authoritative NXDomain (name does not exist)
+     * then that result is returned to the client. However the query is not implicitly
+     * cancelled -- it remains active and if the answer subsequently changes
+     * (e.g. because a VPN tunnel is subsequently established) then that positive
+     * result will still be returned to the client.
+     * Similarly, if a query results in a CNAME record, then in addition to following
+     * the CNAME referral, the intermediate CNAME result is also returned to the client.
+     * When this flag is not set, NXDomain errors are not returned, and CNAME records
+     * are followed silently without informing the client of the intermediate steps.
+     * (In earlier builds this flag was briefly calledkDNSServiceFlagsReturnCNAME)
+     */
+
+    kDNSServiceFlagsNonBrowsable        = 0x2000,
+    /* A service registered with the NonBrowsable flag set can be resolved using
+     * DNSServiceResolve(), but will not be discoverable using DNSServiceBrowse().
+     * This is for cases where the name is actually a GUID; it is found by other means;
+     * there is no end-user benefit to browsing to find a long list of opaque GUIDs.
+     * Using the NonBrowsable flag creates SRV+TXT without the cost of also advertising
+     * an associated PTR record.
+     */
+
+    kDNSServiceFlagsShareConnection     = 0x4000,
+    /* For efficiency, clients that perform many concurrent operations may want to use a
+     * single Unix Domain Socket connection with the background daemon, instead of having a
+     * separate connection for each independent operation. To use this mode, clients first
+     * call DNSServiceCreateConnection(&MainRef) to initialize the main DNSServiceRef.
+     * For each subsequent operation that is to share that same connection, the client copies
+     * the MainRef, and then passes the address of that copy, setting the ShareConnection flag
+     * to tell the library that this DNSServiceRef is not a typical uninitialized DNSServiceRef;
+     * it's a copy of an existing DNSServiceRef whose connection information should be reused.
+     *
+     * For example:
+     *
+     * DNSServiceErrorType error;
+     * DNSServiceRef MainRef;
+     * error = DNSServiceCreateConnection(&MainRef);
+     * if (error) ...
+     * DNSServiceRef BrowseRef = MainRef;  // Important: COPY the primary DNSServiceRef first...
+     * error = DNSServiceBrowse(&BrowseRef, kDNSServiceFlagsShareConnection, ...); // then use the copy
+     * if (error) ...
+     * ...
+     * DNSServiceRefDeallocate(BrowseRef); // Terminate the browse operation
+     * DNSServiceRefDeallocate(MainRef);   // Terminate the shared connection
+     *
+     * Notes:
+     *
+     * 1. Collective kDNSServiceFlagsMoreComing flag
+     * When callbacks are invoked using a shared DNSServiceRef, the
+     * kDNSServiceFlagsMoreComing flag applies collectively to *all* active
+     * operations sharing the same parent DNSServiceRef. If the MoreComing flag is
+     * set it means that there are more results queued on this parent DNSServiceRef,
+     * but not necessarily more results for this particular callback function. 
+     * The implication of this for client programmers is that when a callback
+     * is invoked with the MoreComing flag set, the code should update its
+     * internal data structures with the new result, and set a variable indicating
+     * that its UI needs to be updated. Then, later when a callback is eventually
+     * invoked with the MoreComing flag not set, the code should update *all*
+     * stale UI elements related to that shared parent DNSServiceRef that need
+     * updating, not just the UI elements related to the particular callback
+     * that happened to be the last one to be invoked.
+     *
+     * 2. Canceling operations and kDNSServiceFlagsMoreComing
+     * Whenever you cancel any operation for which you had deferred UI updates
+     * waiting because of a kDNSServiceFlagsMoreComing flag, you should perform
+     * those deferred UI updates. This is because, after cancelling the operation,
+     * you can no longer wait for a callback *without* MoreComing set, to tell
+     * you do perform your deferred UI updates (the operation has been canceled,
+     * so there will be no more callbacks). An implication of the collective
+     * kDNSServiceFlagsMoreComing flag for shared connections is that this
+     * guideline applies more broadly -- any time you cancel an operation on
+     * a shared connection, you should perform all deferred UI updates for all
+     * operations sharing that connection. This is because the MoreComing flag
+     * might have been referring to events coming for the operation you canceled,
+     * which will now not be coming because the operation has been canceled.
+     *
+     * 3. Only share DNSServiceRef's created with DNSServiceCreateConnection
+     * Calling DNSServiceCreateConnection(&ref) creates a special shareable DNSServiceRef.
+     * DNSServiceRef's created by other calls like DNSServiceBrowse() or DNSServiceResolve()
+     * cannot be shared by copying them and using kDNSServiceFlagsShareConnection.
+     *
+     * 4. Don't Double-Deallocate
+     * Calling DNSServiceRefDeallocate(ref) for a particular operation's DNSServiceRef terminates
+     * just that operation. Calling DNSServiceRefDeallocate(ref) for the main shared DNSServiceRef
+     * (the parent DNSServiceRef, originally created by DNSServiceCreateConnection(&ref))
+     * automatically terminates the shared connection and all operations that were still using it.
+     * After doing this, DO NOT then attempt to deallocate any remaining subordinate DNSServiceRef's.
+     * The memory used by those subordinate DNSServiceRef's has already been freed, so any attempt
+     * to do a DNSServiceRefDeallocate (or any other operation) on them will result in accesses
+     * to freed memory, leading to crashes or other equally undesirable results.
+     *
+     * 5. Thread Safety
+     * The dns_sd.h API does not presuppose any particular threading model, and consequently
+     * does no locking of its own (which would require linking some specific threading library).
+     * If client code calls API routines on the same DNSServiceRef concurrently
+     * from multiple threads, it is the client's responsibility to use a mutext
+     * lock or take similar appropriate precautions to serialize those calls.
+     */
+
+    kDNSServiceFlagsSuppressUnusable    = 0x8000,
+	/*
+	 * This flag is meaningful only in DNSServiceQueryRecord which suppresses unusable queries on the
+	 * wire. If "hostname" is a wide-area unicast DNS hostname (i.e. not a ".local." name)
+	 * but this host has no routable IPv6 address, then the call will not try to look up IPv6 addresses
+	 * for "hostname", since any addresses it found would be unlikely to be of any use anyway. Similarly,
+	 * if this host has no routable IPv4 address, the call will not try to look up IPv4 addresses for
+	 * "hostname".
+	 */
+
+    kDNSServiceFlagsTimeout            = 0x10000,
+	/*
+	 * When kDNServiceFlagsTimeout is passed to DNSServiceQueryRecord or DNSServiceGetAddrInfo, the query is
+	 * stopped after a certain number of seconds have elapsed. The time at which the query will be stopped
+	 * is determined by the system and cannot be configured by the user. The query will be stopped irrespective
+	 * of whether a response was given earlier or not. When the query is stopped, the callback will be called
+	 * with an error code of kDNSServiceErr_Timeout and a NULL sockaddr will be returned for DNSServiceGetAddrInfo
+	 * and zero length rdata will be returned for DNSServiceQueryRecord.
+	 */
+
+    kDNSServiceFlagsIncludeP2P          = 0x20000,
+	/*
+	 * Include P2P interfaces when kDNSServiceInterfaceIndexAny is specified.
+	 * By default, specifying kDNSServiceInterfaceIndexAny does not include P2P interfaces.
+	 */
+	kDNSServiceFlagsWakeOnResolve      = 0x40000
+	/*
+	 * This flag is meaningful only in DNSServiceResolve. When set, it tries to send a magic packet
+	 * to wake up the client.
+	 */
+    };
+
+/* Possible protocols for DNSServiceNATPortMappingCreate(). */
+enum
+    {
+    kDNSServiceProtocol_IPv4 = 0x01,
+    kDNSServiceProtocol_IPv6 = 0x02,
+    /* 0x04 and 0x08 reserved for future internetwork protocols */
+    
+    kDNSServiceProtocol_UDP  = 0x10,
+    kDNSServiceProtocol_TCP  = 0x20
+    /* 0x40 and 0x80 reserved for future transport protocols, e.g. SCTP [RFC 2960]
+     * or DCCP [RFC 4340]. If future NAT gateways are created that support port
+     * mappings for these protocols, new constants will be defined here.
+     */
+    };
+
+/*
+ * The values for DNS Classes and Types are listed in RFC 1035, and are available
+ * on every OS in its DNS header file. Unfortunately every OS does not have the
+ * same header file containing DNS Class and Type constants, and the names of
+ * the constants are not consistent. For example, BIND 8 uses "T_A",
+ * BIND 9 uses "ns_t_a", Windows uses "DNS_TYPE_A", etc.
+ * For this reason, these constants are also listed here, so that code using
+ * the DNS-SD programming APIs can use these constants, so that the same code
+ * can compile on all our supported platforms.
+ */
+
+enum
+    {
+    kDNSServiceClass_IN       = 1       /* Internet */
+    };
+
+enum
+    {
+    kDNSServiceType_A          = 1,      /* Host address. */
+    kDNSServiceType_NS         = 2,      /* Authoritative server. */
+    kDNSServiceType_MD         = 3,      /* Mail destination. */
+    kDNSServiceType_MF         = 4,      /* Mail forwarder. */
+    kDNSServiceType_CNAME      = 5,      /* Canonical name. */
+    kDNSServiceType_SOA        = 6,      /* Start of authority zone. */
+    kDNSServiceType_MB         = 7,      /* Mailbox domain name. */
+    kDNSServiceType_MG         = 8,      /* Mail group member. */
+    kDNSServiceType_MR         = 9,      /* Mail rename name. */
+    kDNSServiceType_NULL       = 10,     /* Null resource record. */
+    kDNSServiceType_WKS        = 11,     /* Well known service. */
+    kDNSServiceType_PTR        = 12,     /* Domain name pointer. */
+    kDNSServiceType_HINFO      = 13,     /* Host information. */
+    kDNSServiceType_MINFO      = 14,     /* Mailbox information. */
+    kDNSServiceType_MX         = 15,     /* Mail routing information. */
+    kDNSServiceType_TXT        = 16,     /* One or more text strings (NOT "zero or more..."). */
+    kDNSServiceType_RP         = 17,     /* Responsible person. */
+    kDNSServiceType_AFSDB      = 18,     /* AFS cell database. */
+    kDNSServiceType_X25        = 19,     /* X_25 calling address. */
+    kDNSServiceType_ISDN       = 20,     /* ISDN calling address. */
+    kDNSServiceType_RT         = 21,     /* Router. */
+    kDNSServiceType_NSAP       = 22,     /* NSAP address. */
+    kDNSServiceType_NSAP_PTR   = 23,     /* Reverse NSAP lookup (deprecated). */
+    kDNSServiceType_SIG        = 24,     /* Security signature. */
+    kDNSServiceType_KEY        = 25,     /* Security key. */
+    kDNSServiceType_PX         = 26,     /* X.400 mail mapping. */
+    kDNSServiceType_GPOS       = 27,     /* Geographical position (withdrawn). */
+    kDNSServiceType_AAAA       = 28,     /* IPv6 Address. */
+    kDNSServiceType_LOC        = 29,     /* Location Information. */
+    kDNSServiceType_NXT        = 30,     /* Next domain (security). */
+    kDNSServiceType_EID        = 31,     /* Endpoint identifier. */
+    kDNSServiceType_NIMLOC     = 32,     /* Nimrod Locator. */
+    kDNSServiceType_SRV        = 33,     /* Server Selection. */
+    kDNSServiceType_ATMA       = 34,     /* ATM Address */
+    kDNSServiceType_NAPTR      = 35,     /* Naming Authority PoinTeR */
+    kDNSServiceType_KX         = 36,     /* Key Exchange */
+    kDNSServiceType_CERT       = 37,     /* Certification record */
+    kDNSServiceType_A6         = 38,     /* IPv6 Address (deprecated) */
+    kDNSServiceType_DNAME      = 39,     /* Non-terminal DNAME (for IPv6) */
+    kDNSServiceType_SINK       = 40,     /* Kitchen sink (experimental) */
+    kDNSServiceType_OPT        = 41,     /* EDNS0 option (meta-RR) */
+    kDNSServiceType_APL        = 42,     /* Address Prefix List */
+    kDNSServiceType_DS         = 43,     /* Delegation Signer */
+    kDNSServiceType_SSHFP      = 44,     /* SSH Key Fingerprint */
+    kDNSServiceType_IPSECKEY   = 45,     /* IPSECKEY */
+    kDNSServiceType_RRSIG      = 46,     /* RRSIG */
+    kDNSServiceType_NSEC       = 47,     /* Denial of Existence */
+    kDNSServiceType_DNSKEY     = 48,     /* DNSKEY */
+    kDNSServiceType_DHCID      = 49,     /* DHCP Client Identifier */
+    kDNSServiceType_NSEC3      = 50,     /* Hashed Authenticated Denial of Existence */
+    kDNSServiceType_NSEC3PARAM = 51,     /* Hashed Authenticated Denial of Existence */
+
+    kDNSServiceType_HIP        = 55,     /* Host Identity Protocol */
+
+    kDNSServiceType_SPF        = 99,     /* Sender Policy Framework for E-Mail */
+    kDNSServiceType_UINFO      = 100,    /* IANA-Reserved */
+    kDNSServiceType_UID        = 101,    /* IANA-Reserved */
+    kDNSServiceType_GID        = 102,    /* IANA-Reserved */
+    kDNSServiceType_UNSPEC     = 103,    /* IANA-Reserved */
+
+    kDNSServiceType_TKEY       = 249,    /* Transaction key */
+    kDNSServiceType_TSIG       = 250,    /* Transaction signature. */
+    kDNSServiceType_IXFR       = 251,    /* Incremental zone transfer. */
+    kDNSServiceType_AXFR       = 252,    /* Transfer zone of authority. */
+    kDNSServiceType_MAILB      = 253,    /* Transfer mailbox records. */
+    kDNSServiceType_MAILA      = 254,    /* Transfer mail agent records. */
+    kDNSServiceType_ANY        = 255     /* Wildcard match. */
+    };
+
+/* possible error code values */
+enum
+    {
+    kDNSServiceErr_NoError                   = 0,
+    kDNSServiceErr_Unknown                   = -65537,  /* 0xFFFE FFFF */
+    kDNSServiceErr_NoSuchName                = -65538,
+    kDNSServiceErr_NoMemory                  = -65539,
+    kDNSServiceErr_BadParam                  = -65540,
+    kDNSServiceErr_BadReference              = -65541,
+    kDNSServiceErr_BadState                  = -65542,
+    kDNSServiceErr_BadFlags                  = -65543,
+    kDNSServiceErr_Unsupported               = -65544,
+    kDNSServiceErr_NotInitialized            = -65545,
+    kDNSServiceErr_AlreadyRegistered         = -65547,
+    kDNSServiceErr_NameConflict              = -65548,
+    kDNSServiceErr_Invalid                   = -65549,
+    kDNSServiceErr_Firewall                  = -65550,
+    kDNSServiceErr_Incompatible              = -65551,  /* client library incompatible with daemon */
+    kDNSServiceErr_BadInterfaceIndex         = -65552,
+    kDNSServiceErr_Refused                   = -65553,
+    kDNSServiceErr_NoSuchRecord              = -65554,
+    kDNSServiceErr_NoAuth                    = -65555,
+    kDNSServiceErr_NoSuchKey                 = -65556,
+    kDNSServiceErr_NATTraversal              = -65557,
+    kDNSServiceErr_DoubleNAT                 = -65558,
+    kDNSServiceErr_BadTime                   = -65559,  /* Codes up to here existed in Tiger */
+    kDNSServiceErr_BadSig                    = -65560,
+    kDNSServiceErr_BadKey                    = -65561,
+    kDNSServiceErr_Transient                 = -65562,
+    kDNSServiceErr_ServiceNotRunning         = -65563,  /* Background daemon not running */
+    kDNSServiceErr_NATPortMappingUnsupported = -65564,  /* NAT doesn't support NAT-PMP or UPnP */
+    kDNSServiceErr_NATPortMappingDisabled    = -65565,  /* NAT supports NAT-PMP or UPnP but it's disabled by the administrator */
+    kDNSServiceErr_NoRouter                  = -65566,  /* No router currently configured (probably no network connectivity) */
+    kDNSServiceErr_PollingMode               = -65567,
+    kDNSServiceErr_Timeout                   = -65568
+
+    /* mDNS Error codes are in the range
+     * FFFE FF00 (-65792) to FFFE FFFF (-65537) */
+    };
+
+/* Maximum length, in bytes, of a service name represented as a */
+/* literal C-String, including the terminating NULL at the end. */
+
+#define kDNSServiceMaxServiceName 64
+
+/* Maximum length, in bytes, of a domain name represented as an *escaped* C-String */
+/* including the final trailing dot, and the C-String terminating NULL at the end. */
+
+#define kDNSServiceMaxDomainName 1009
+
+/*
+ * Notes on DNS Name Escaping
+ *   -- or --
+ * "Why is kDNSServiceMaxDomainName 1009, when the maximum legal domain name is 256 bytes?"
+ *
+ * All strings used in the DNS-SD APIs are UTF-8 strings. Apart from the exceptions noted below,
+ * the APIs expect the strings to be properly escaped, using the conventional DNS escaping rules:
+ *
+ *   '\\' represents a single literal '\' in the name
+ *   '\.' represents a single literal '.' in the name
+ *   '\ddd', where ddd is a three-digit decimal value from 000 to 255,
+ *        represents a single literal byte with that value.
+ *   A bare unescaped '.' is a label separator, marking a boundary between domain and subdomain.
+ *
+ * The exceptions, that do not use escaping, are the routines where the full
+ * DNS name of a resource is broken, for convenience, into servicename/regtype/domain.
+ * In these routines, the "servicename" is NOT escaped. It does not need to be, since
+ * it is, by definition, just a single literal string. Any characters in that string
+ * represent exactly what they are. The "regtype" portion is, technically speaking,
+ * escaped, but since legal regtypes are only allowed to contain letters, digits,
+ * and hyphens, there is nothing to escape, so the issue is moot. The "domain"
+ * portion is also escaped, though most domains in use on the public Internet
+ * today, like regtypes, don't contain any characters that need to be escaped.
+ * As DNS-SD becomes more popular, rich-text domains for service discovery will
+ * become common, so software should be written to cope with domains with escaping.
+ *
+ * The servicename may be up to 63 bytes of UTF-8 text (not counting the C-String
+ * terminating NULL at the end). The regtype is of the form _service._tcp or
+ * _service._udp, where the "service" part is 1-15 characters, which may be
+ * letters, digits, or hyphens. The domain part of the three-part name may be
+ * any legal domain, providing that the resulting servicename+regtype+domain
+ * name does not exceed 256 bytes.
+ *
+ * For most software, these issues are transparent. When browsing, the discovered
+ * servicenames should simply be displayed as-is. When resolving, the discovered
+ * servicename/regtype/domain are simply passed unchanged to DNSServiceResolve().
+ * When a DNSServiceResolve() succeeds, the returned fullname is already in
+ * the correct format to pass to standard system DNS APIs such as res_query().
+ * For converting from servicename/regtype/domain to a single properly-escaped
+ * full DNS name, the helper function DNSServiceConstructFullName() is provided.
+ *
+ * The following (highly contrived) example illustrates the escaping process.
+ * Suppose you have an service called "Dr. Smith\Dr. Johnson", of type "_ftp._tcp"
+ * in subdomain "4th. Floor" of subdomain "Building 2" of domain "apple.com."
+ * The full (escaped) DNS name of this service's SRV record would be:
+ * Dr\.\032Smith\\Dr\.\032Johnson._ftp._tcp.4th\.\032Floor.Building\0322.apple.com.
+ */
+
+
+/*
+ * Constants for specifying an interface index
+ *
+ * Specific interface indexes are identified via a 32-bit unsigned integer returned
+ * by the if_nametoindex() family of calls.
+ *
+ * If the client passes 0 for interface index, that means "do the right thing",
+ * which (at present) means, "if the name is in an mDNS local multicast domain
+ * (e.g. 'local.', '254.169.in-addr.arpa.', '{8,9,A,B}.E.F.ip6.arpa.') then multicast
+ * on all applicable interfaces, otherwise send via unicast to the appropriate
+ * DNS server." Normally, most clients will use 0 for interface index to
+ * automatically get the default sensible behaviour.
+ *
+ * If the client passes a positive interface index, then for multicast names that
+ * indicates to do the operation only on that one interface. For unicast names the
+ * interface index is ignored unless kDNSServiceFlagsForceMulticast is also set.
+ *
+ * If the client passes kDNSServiceInterfaceIndexLocalOnly when registering
+ * a service, then that service will be found *only* by other local clients
+ * on the same machine that are browsing using kDNSServiceInterfaceIndexLocalOnly
+ * or kDNSServiceInterfaceIndexAny.
+ * If a client has a 'private' service, accessible only to other processes
+ * running on the same machine, this allows the client to advertise that service
+ * in a way such that it does not inadvertently appear in service lists on
+ * all the other machines on the network.
+ *
+ * If the client passes kDNSServiceInterfaceIndexLocalOnly when browsing
+ * then it will find *all* records registered on that same local machine.
+ * Clients explicitly wishing to discover *only* LocalOnly services can
+ * accomplish this by inspecting the interfaceIndex of each service reported
+ * to their DNSServiceBrowseReply() callback function, and discarding those
+ * where the interface index is not kDNSServiceInterfaceIndexLocalOnly.
+ *
+ * kDNSServiceInterfaceIndexP2P is meaningful only in Browse, QueryRecord,
+ * and Resolve operations. It should not be used in other DNSService APIs.
+ *
+ * - If kDNSServiceInterfaceIndexP2P is passed to DNSServiceBrowse or
+ *   DNSServiceQueryRecord, it restricts the operation to P2P.
+ *
+ * - If kDNSServiceInterfaceIndexP2P is passed to DNSServiceResolve, it is
+ *   mapped internally to kDNSServiceInterfaceIndexAny, because resolving
+ *   a P2P service may create and/or enable an interface whose index is not
+ *   known a priori. The resolve callback will indicate the index of the
+ *   interface via which the service can be accessed.
+ *
+ * If applications pass kDNSServiceInterfaceIndexAny to DNSServiceBrowse
+ * or DNSServiceQueryRecord, they must set the kDNSServiceFlagsIncludeP2P flag
+ * to include P2P. In this case, if a service instance or the record being queried 
+ * is found over P2P, the resulting ADD event will indicate kDNSServiceInterfaceIndexP2P 
+ * as the interface index.
+ */
+
+#define kDNSServiceInterfaceIndexAny 0
+#define kDNSServiceInterfaceIndexLocalOnly ((uint32_t)-1)
+#define kDNSServiceInterfaceIndexUnicast   ((uint32_t)-2)
+#define kDNSServiceInterfaceIndexP2P       ((uint32_t)-3)
+
+typedef uint32_t DNSServiceFlags;
+typedef uint32_t DNSServiceProtocol;
+typedef int32_t  DNSServiceErrorType;
+
+
+/*********************************************************************************************
+ *
+ * Version checking
+ *
+ *********************************************************************************************/
+
+/* DNSServiceGetProperty() Parameters:
+ *
+ * property:        The requested property.
+ *                  Currently the only property defined is kDNSServiceProperty_DaemonVersion.
+ *
+ * result:          Place to store result.
+ *                  For retrieving DaemonVersion, this should be the address of a uint32_t.
+ *
+ * size:            Pointer to uint32_t containing size of the result location.
+ *                  For retrieving DaemonVersion, this should be sizeof(uint32_t).
+ *                  On return the uint32_t is updated to the size of the data returned.
+ *                  For DaemonVersion, the returned size is always sizeof(uint32_t), but
+ *                  future properties could be defined which return variable-sized results.
+ *
+ * return value:    Returns kDNSServiceErr_NoError on success, or kDNSServiceErr_ServiceNotRunning
+ *                  if the daemon (or "system service" on Windows) is not running.
+ */
+
+DNSServiceErrorType DNSSD_API DNSServiceGetProperty
+    (
+    const char *property,  /* Requested property (i.e. kDNSServiceProperty_DaemonVersion) */
+    void       *result,    /* Pointer to place to store result */
+    uint32_t   *size       /* size of result location */
+    );
+
+/*
+ * When requesting kDNSServiceProperty_DaemonVersion, the result pointer must point
+ * to a 32-bit unsigned integer, and the size parameter must be set to sizeof(uint32_t).
+ *
+ * On return, the 32-bit unsigned integer contains the version number, formatted as follows:
+ *   Major part of the build number * 10000 +
+ *   minor part of the build number *   100
+ *
+ * For example, Mac OS X 10.4.9 has mDNSResponder-108.4, which would be represented as
+ * version 1080400. This allows applications to do simple greater-than and less-than comparisons:
+ * e.g. an application that requires at least mDNSResponder-108.4 can check:
+ *
+ *   if (version >= 1080400) ...
+ *
+ * Example usage:
+ *
+ * uint32_t version;
+ * uint32_t size = sizeof(version);
+ * DNSServiceErrorType err = DNSServiceGetProperty(kDNSServiceProperty_DaemonVersion, &version, &size);
+ * if (!err) printf("Bonjour version is %d.%d\n", version / 10000, version / 100 % 100);
+ */
+
+#define kDNSServiceProperty_DaemonVersion "DaemonVersion"
+
+
+/*********************************************************************************************
+ *
+ * Unix Domain Socket access, DNSServiceRef deallocation, and data processing functions
+ *
+ *********************************************************************************************/
+
+/* DNSServiceRefSockFD()
+ *
+ * Access underlying Unix domain socket for an initialized DNSServiceRef.
+ * The DNS Service Discovery implementation uses this socket to communicate between the client and
+ * the mDNSResponder daemon. The application MUST NOT directly read from or write to this socket.
+ * Access to the socket is provided so that it can be used as a kqueue event source, a CFRunLoop
+ * event source, in a select() loop, etc. When the underlying event management subsystem (kqueue/
+ * select/CFRunLoop etc.) indicates to the client that data is available for reading on the
+ * socket, the client should call DNSServiceProcessResult(), which will extract the daemon's
+ * reply from the socket, and pass it to the appropriate application callback. By using a run
+ * loop or select(), results from the daemon can be processed asynchronously. Alternatively,
+ * a client can choose to fork a thread and have it loop calling "DNSServiceProcessResult(ref);"
+ * If DNSServiceProcessResult() is called when no data is available for reading on the socket, it
+ * will block until data does become available, and then process the data and return to the caller.
+ * When data arrives on the socket, the client is responsible for calling DNSServiceProcessResult(ref)
+ * in a timely fashion -- if the client allows a large backlog of data to build up the daemon
+ * may terminate the connection.
+ *
+ * sdRef:           A DNSServiceRef initialized by any of the DNSService calls.
+ *
+ * return value:    The DNSServiceRef's underlying socket descriptor, or -1 on
+ *                  error.
+ */
+
+int DNSSD_API DNSServiceRefSockFD(DNSServiceRef sdRef);
+
+
+/* DNSServiceProcessResult()
+ *
+ * Read a reply from the daemon, calling the appropriate application callback. This call will
+ * block until the daemon's response is received. Use DNSServiceRefSockFD() in
+ * conjunction with a run loop or select() to determine the presence of a response from the
+ * server before calling this function to process the reply without blocking. Call this function
+ * at any point if it is acceptable to block until the daemon's response arrives. Note that the
+ * client is responsible for ensuring that DNSServiceProcessResult() is called whenever there is
+ * a reply from the daemon - the daemon may terminate its connection with a client that does not
+ * process the daemon's responses.
+ *
+ * sdRef:           A DNSServiceRef initialized by any of the DNSService calls
+ *                  that take a callback parameter.
+ *
+ * return value:    Returns kDNSServiceErr_NoError on success, otherwise returns
+ *                  an error code indicating the specific failure that occurred.
+ */
+
+DNSServiceErrorType DNSSD_API DNSServiceProcessResult(DNSServiceRef sdRef);
+
+
+/* DNSServiceRefDeallocate()
+ *
+ * Terminate a connection with the daemon and free memory associated with the DNSServiceRef.
+ * Any services or records registered with this DNSServiceRef will be deregistered. Any
+ * Browse, Resolve, or Query operations called with this reference will be terminated.
+ *
+ * Note: If the reference's underlying socket is used in a run loop or select() call, it should
+ * be removed BEFORE DNSServiceRefDeallocate() is called, as this function closes the reference's
+ * socket.
+ *
+ * Note: If the reference was initialized with DNSServiceCreateConnection(), any DNSRecordRefs
+ * created via this reference will be invalidated by this call - the resource records are
+ * deregistered, and their DNSRecordRefs may not be used in subsequent functions. Similarly,
+ * if the reference was initialized with DNSServiceRegister, and an extra resource record was
+ * added to the service via DNSServiceAddRecord(), the DNSRecordRef created by the Add() call
+ * is invalidated when this function is called - the DNSRecordRef may not be used in subsequent
+ * functions.
+ *
+ * Note: This call is to be used only with the DNSServiceRef defined by this API. It is
+ * not compatible with dns_service_discovery_ref objects defined in the legacy Mach-based
+ * DNSServiceDiscovery.h API.
+ *
+ * sdRef:           A DNSServiceRef initialized by any of the DNSService calls.
+ *
+ */
+
+void DNSSD_API DNSServiceRefDeallocate(DNSServiceRef sdRef);
+
+
+/*********************************************************************************************
+ *
+ * Domain Enumeration
+ *
+ *********************************************************************************************/
+
+/* DNSServiceEnumerateDomains()
+ *
+ * Asynchronously enumerate domains available for browsing and registration.
+ *
+ * The enumeration MUST be cancelled via DNSServiceRefDeallocate() when no more domains
+ * are to be found.
+ *
+ * Note that the names returned are (like all of DNS-SD) UTF-8 strings,
+ * and are escaped using standard DNS escaping rules.
+ * (See "Notes on DNS Name Escaping" earlier in this file for more details.)
+ * A graphical browser displaying a hierarchical tree-structured view should cut
+ * the names at the bare dots to yield individual labels, then de-escape each
+ * label according to the escaping rules, and then display the resulting UTF-8 text.
+ *
+ * DNSServiceDomainEnumReply Callback Parameters:
+ *
+ * sdRef:           The DNSServiceRef initialized by DNSServiceEnumerateDomains().
+ *
+ * flags:           Possible values are:
+ *                  kDNSServiceFlagsMoreComing
+ *                  kDNSServiceFlagsAdd
+ *                  kDNSServiceFlagsDefault
+ *
+ * interfaceIndex:  Specifies the interface on which the domain exists. (The index for a given
+ *                  interface is determined via the if_nametoindex() family of calls.)
+ *
+ * errorCode:       Will be kDNSServiceErr_NoError (0) on success, otherwise indicates
+ *                  the failure that occurred (other parameters are undefined if errorCode is nonzero).
+ *
+ * replyDomain:     The name of the domain.
+ *
+ * context:         The context pointer passed to DNSServiceEnumerateDomains.
+ *
+ */
+
+typedef void (DNSSD_API *DNSServiceDomainEnumReply)
+    (
+    DNSServiceRef                       sdRef,
+    DNSServiceFlags                     flags,
+    uint32_t                            interfaceIndex,
+    DNSServiceErrorType                 errorCode,
+    const char                          *replyDomain,
+    void                                *context
+    );
+
+
+/* DNSServiceEnumerateDomains() Parameters:
+ *
+ * sdRef:           A pointer to an uninitialized DNSServiceRef. If the call succeeds
+ *                  then it initializes the DNSServiceRef, returns kDNSServiceErr_NoError,
+ *                  and the enumeration operation will run indefinitely until the client
+ *                  terminates it by passing this DNSServiceRef to DNSServiceRefDeallocate().
+ *
+ * flags:           Possible values are:
+ *                  kDNSServiceFlagsBrowseDomains to enumerate domains recommended for browsing.
+ *                  kDNSServiceFlagsRegistrationDomains to enumerate domains recommended
+ *                  for registration.
+ *
+ * interfaceIndex:  If non-zero, specifies the interface on which to look for domains.
+ *                  (the index for a given interface is determined via the if_nametoindex()
+ *                  family of calls.) Most applications will pass 0 to enumerate domains on
+ *                  all interfaces. See "Constants for specifying an interface index" for more details.
+ *
+ * callBack:        The function to be called when a domain is found or the call asynchronously
+ *                  fails.
+ *
+ * context:         An application context pointer which is passed to the callback function
+ *                  (may be NULL).
+ *
+ * return value:    Returns kDNSServiceErr_NoError on success (any subsequent, asynchronous
+ *                  errors are delivered to the callback), otherwise returns an error code indicating
+ *                  the error that occurred (the callback is not invoked and the DNSServiceRef
+ *                  is not initialized).
+ */
+
+DNSServiceErrorType DNSSD_API DNSServiceEnumerateDomains
+    (
+    DNSServiceRef                       *sdRef,
+    DNSServiceFlags                     flags,
+    uint32_t                            interfaceIndex,
+    DNSServiceDomainEnumReply           callBack,
+    void                                *context  /* may be NULL */
+    );
+
+
+/*********************************************************************************************
+ *
+ *  Service Registration
+ *
+ *********************************************************************************************/
+
+/* Register a service that is discovered via Browse() and Resolve() calls.
+ *
+ * DNSServiceRegisterReply() Callback Parameters:
+ *
+ * sdRef:           The DNSServiceRef initialized by DNSServiceRegister().
+ *
+ * flags:           When a name is successfully registered, the callback will be
+ *                  invoked with the kDNSServiceFlagsAdd flag set. When Wide-Area
+ *                  DNS-SD is in use, it is possible for a single service to get
+ *                  more than one success callback (e.g. one in the "local" multicast
+ *                  DNS domain, and another in a wide-area unicast DNS domain).
+ *                  If a successfully-registered name later suffers a name conflict
+ *                  or similar problem and has to be deregistered, the callback will
+ *                  be invoked with the kDNSServiceFlagsAdd flag not set. The callback
+ *                  is *not* invoked in the case where the caller explicitly terminates
+ *                  the service registration by calling DNSServiceRefDeallocate(ref);
+ *
+ * errorCode:       Will be kDNSServiceErr_NoError on success, otherwise will
+ *                  indicate the failure that occurred (including name conflicts,
+ *                  if the kDNSServiceFlagsNoAutoRename flag was used when registering.)
+ *                  Other parameters are undefined if errorCode is nonzero.
+ *
+ * name:            The service name registered (if the application did not specify a name in
+ *                  DNSServiceRegister(), this indicates what name was automatically chosen).
+ *
+ * regtype:         The type of service registered, as it was passed to the callout.
+ *
+ * domain:          The domain on which the service was registered (if the application did not
+ *                  specify a domain in DNSServiceRegister(), this indicates the default domain
+ *                  on which the service was registered).
+ *
+ * context:         The context pointer that was passed to the callout.
+ *
+ */
+
+typedef void (DNSSD_API *DNSServiceRegisterReply)
+    (
+    DNSServiceRef                       sdRef,
+    DNSServiceFlags                     flags,
+    DNSServiceErrorType                 errorCode,
+    const char                          *name,
+    const char                          *regtype,
+    const char                          *domain,
+    void                                *context
+    );
+
+
+/* DNSServiceRegister() Parameters:
+ *
+ * sdRef:           A pointer to an uninitialized DNSServiceRef. If the call succeeds
+ *                  then it initializes the DNSServiceRef, returns kDNSServiceErr_NoError,
+ *                  and the registration will remain active indefinitely until the client
+ *                  terminates it by passing this DNSServiceRef to DNSServiceRefDeallocate().
+ *
+ * interfaceIndex:  If non-zero, specifies the interface on which to register the service
+ *                  (the index for a given interface is determined via the if_nametoindex()
+ *                  family of calls.) Most applications will pass 0 to register on all
+ *                  available interfaces. See "Constants for specifying an interface index" for more details.
+ *
+ * flags:           Indicates the renaming behavior on name conflict (most applications
+ *                  will pass 0). See flag definitions above for details.
+ *
+ * name:            If non-NULL, specifies the service name to be registered.
+ *                  Most applications will not specify a name, in which case the computer
+ *                  name is used (this name is communicated to the client via the callback).
+ *                  If a name is specified, it must be 1-63 bytes of UTF-8 text.
+ *                  If the name is longer than 63 bytes it will be automatically truncated
+ *                  to a legal length, unless the NoAutoRename flag is set,
+ *                  in which case kDNSServiceErr_BadParam will be returned.
+ *
+ * regtype:         The service type followed by the protocol, separated by a dot
+ *                  (e.g. "_ftp._tcp"). The service type must be an underscore, followed
+ *                  by 1-15 characters, which may be letters, digits, or hyphens.
+ *                  The transport protocol must be "_tcp" or "_udp". New service types
+ *                  should be registered at <http://www.dns-sd.org/ServiceTypes.html>.
+ *
+ *                  Additional subtypes of the primary service type (where a service
+ *                  type has defined subtypes) follow the primary service type in a
+ *                  comma-separated list, with no additional spaces, e.g.
+ *                      "_primarytype._tcp,_subtype1,_subtype2,_subtype3"
+ *                  Subtypes provide a mechanism for filtered browsing: A client browsing
+ *                  for "_primarytype._tcp" will discover all instances of this type;
+ *                  a client browsing for "_primarytype._tcp,_subtype2" will discover only
+ *                  those instances that were registered with "_subtype2" in their list of
+ *                  registered subtypes.
+ *
+ *                  The subtype mechanism can be illustrated with some examples using the
+ *                  dns-sd command-line tool:
+ *
+ *                  % dns-sd -R Simple _test._tcp "" 1001 &
+ *                  % dns-sd -R Better _test._tcp,HasFeatureA "" 1002 &
+ *                  % dns-sd -R Best   _test._tcp,HasFeatureA,HasFeatureB "" 1003 &
+ *
+ *                  Now:
+ *                  % dns-sd -B _test._tcp             # will find all three services
+ *                  % dns-sd -B _test._tcp,HasFeatureA # finds "Better" and "Best"
+ *                  % dns-sd -B _test._tcp,HasFeatureB # finds only "Best"
+ *
+ *                  Subtype labels may be up to 63 bytes long, and may contain any eight-
+ *                  bit byte values, including zero bytes. However, due to the nature of
+ *                  using a C-string-based API, conventional DNS escaping must be used for
+ *                  dots ('.'), commas (','), backslashes ('\') and zero bytes, as shown below:
+ *                  
+ *                  % dns-sd -R Test '_test._tcp,s\.one,s\,two,s\\three,s\000four' local 123
+ *
+ * domain:          If non-NULL, specifies the domain on which to advertise the service.
+ *                  Most applications will not specify a domain, instead automatically
+ *                  registering in the default domain(s).
+ *
+ * host:            If non-NULL, specifies the SRV target host name. Most applications
+ *                  will not specify a host, instead automatically using the machine's
+ *                  default host name(s). Note that specifying a non-NULL host does NOT
+ *                  create an address record for that host - the application is responsible
+ *                  for ensuring that the appropriate address record exists, or creating it
+ *                  via DNSServiceRegisterRecord().
+ *
+ * port:            The port, in network byte order, on which the service accepts connections.
+ *                  Pass 0 for a "placeholder" service (i.e. a service that will not be discovered
+ *                  by browsing, but will cause a name conflict if another client tries to
+ *                  register that same name). Most clients will not use placeholder services.
+ *
+ * txtLen:          The length of the txtRecord, in bytes. Must be zero if the txtRecord is NULL.
+ *
+ * txtRecord:       The TXT record rdata. A non-NULL txtRecord MUST be a properly formatted DNS
+ *                  TXT record, i.e. <length byte> <data> <length byte> <data> ...
+ *                  Passing NULL for the txtRecord is allowed as a synonym for txtLen=1, txtRecord="",
+ *                  i.e. it creates a TXT record of length one containing a single empty string.
+ *                  RFC 1035 doesn't allow a TXT record to contain *zero* strings, so a single empty
+ *                  string is the smallest legal DNS TXT record.
+ *                  As with the other parameters, the DNSServiceRegister call copies the txtRecord
+ *                  data; e.g. if you allocated the storage for the txtRecord parameter with malloc()
+ *                  then you can safely free that memory right after the DNSServiceRegister call returns.
+ *
+ * callBack:        The function to be called when the registration completes or asynchronously
+ *                  fails. The client MAY pass NULL for the callback -  The client will NOT be notified
+ *                  of the default values picked on its behalf, and the client will NOT be notified of any
+ *                  asynchronous errors (e.g. out of memory errors, etc.) that may prevent the registration
+ *                  of the service. The client may NOT pass the NoAutoRename flag if the callback is NULL.
+ *                  The client may still deregister the service at any time via DNSServiceRefDeallocate().
+ *
+ * context:         An application context pointer which is passed to the callback function
+ *                  (may be NULL).
+ *
+ * return value:    Returns kDNSServiceErr_NoError on success (any subsequent, asynchronous
+ *                  errors are delivered to the callback), otherwise returns an error code indicating
+ *                  the error that occurred (the callback is never invoked and the DNSServiceRef
+ *                  is not initialized).
+ */
+
+DNSServiceErrorType DNSSD_API DNSServiceRegister
+    (
+    DNSServiceRef                       *sdRef,
+    DNSServiceFlags                     flags,
+    uint32_t                            interfaceIndex,
+    const char                          *name,         /* may be NULL */
+    const char                          *regtype,
+    const char                          *domain,       /* may be NULL */
+    const char                          *host,         /* may be NULL */
+    uint16_t                            port,          /* In network byte order */
+    uint16_t                            txtLen,
+    const void                          *txtRecord,    /* may be NULL */
+    DNSServiceRegisterReply             callBack,      /* may be NULL */
+    void                                *context       /* may be NULL */
+    );
+
+
+/* DNSServiceAddRecord()
+ *
+ * Add a record to a registered service. The name of the record will be the same as the
+ * registered service's name.
+ * The record can later be updated or deregistered by passing the RecordRef initialized
+ * by this function to DNSServiceUpdateRecord() or DNSServiceRemoveRecord().
+ *
+ * Note that the DNSServiceAddRecord/UpdateRecord/RemoveRecord are *NOT* thread-safe
+ * with respect to a single DNSServiceRef. If you plan to have multiple threads
+ * in your program simultaneously add, update, or remove records from the same
+ * DNSServiceRef, then it's the caller's responsibility to use a mutext lock
+ * or take similar appropriate precautions to serialize those calls.
+ *
+ * Parameters;
+ *
+ * sdRef:           A DNSServiceRef initialized by DNSServiceRegister().
+ *
+ * RecordRef:       A pointer to an uninitialized DNSRecordRef. Upon succesfull completion of this
+ *                  call, this ref may be passed to DNSServiceUpdateRecord() or DNSServiceRemoveRecord().
+ *                  If the above DNSServiceRef is passed to DNSServiceRefDeallocate(), RecordRef is also
+ *                  invalidated and may not be used further.
+ *
+ * flags:           Currently ignored, reserved for future use.
+ *
+ * rrtype:          The type of the record (e.g. kDNSServiceType_TXT, kDNSServiceType_SRV, etc)
+ *
+ * rdlen:           The length, in bytes, of the rdata.
+ *
+ * rdata:           The raw rdata to be contained in the added resource record.
+ *
+ * ttl:             The time to live of the resource record, in seconds.
+ *                  Most clients should pass 0 to indicate that the system should
+ *                  select a sensible default value.
+ *
+ * return value:    Returns kDNSServiceErr_NoError on success, otherwise returns an
+ *                  error code indicating the error that occurred (the RecordRef is not initialized).
+ */
+
+DNSServiceErrorType DNSSD_API DNSServiceAddRecord
+    (
+    DNSServiceRef                       sdRef,
+    DNSRecordRef                        *RecordRef,
+    DNSServiceFlags                     flags,
+    uint16_t                            rrtype,
+    uint16_t                            rdlen,
+    const void                          *rdata,
+    uint32_t                            ttl
+    );
+
+
+/* DNSServiceUpdateRecord
+ *
+ * Update a registered resource record. The record must either be:
+ *   - The primary txt record of a service registered via DNSServiceRegister()
+ *   - A record added to a registered service via DNSServiceAddRecord()
+ *   - An individual record registered by DNSServiceRegisterRecord()
+ *
+ * Parameters:
+ *
+ * sdRef:           A DNSServiceRef that was initialized by DNSServiceRegister()
+ *                  or DNSServiceCreateConnection().
+ *
+ * RecordRef:       A DNSRecordRef initialized by DNSServiceAddRecord, or NULL to update the
+ *                  service's primary txt record.
+ *
+ * flags:           Currently ignored, reserved for future use.
+ *
+ * rdlen:           The length, in bytes, of the new rdata.
+ *
+ * rdata:           The new rdata to be contained in the updated resource record.
+ *
+ * ttl:             The time to live of the updated resource record, in seconds.
+ *                  Most clients should pass 0 to indicate that the system should
+ *                  select a sensible default value.
+ *
+ * return value:    Returns kDNSServiceErr_NoError on success, otherwise returns an
+ *                  error code indicating the error that occurred.
+ */
+
+DNSServiceErrorType DNSSD_API DNSServiceUpdateRecord
+    (
+    DNSServiceRef                       sdRef,
+    DNSRecordRef                        RecordRef,     /* may be NULL */
+    DNSServiceFlags                     flags,
+    uint16_t                            rdlen,
+    const void                          *rdata,
+    uint32_t                            ttl
+    );
+
+
+/* DNSServiceRemoveRecord
+ *
+ * Remove a record previously added to a service record set via DNSServiceAddRecord(), or deregister
+ * an record registered individually via DNSServiceRegisterRecord().
+ *
+ * Parameters:
+ *
+ * sdRef:           A DNSServiceRef initialized by DNSServiceRegister() (if the
+ *                  record being removed was registered via DNSServiceAddRecord()) or by
+ *                  DNSServiceCreateConnection() (if the record being removed was registered via
+ *                  DNSServiceRegisterRecord()).
+ *
+ * recordRef:       A DNSRecordRef initialized by a successful call to DNSServiceAddRecord()
+ *                  or DNSServiceRegisterRecord().
+ *
+ * flags:           Currently ignored, reserved for future use.
+ *
+ * return value:    Returns kDNSServiceErr_NoError on success, otherwise returns an
+ *                  error code indicating the error that occurred.
+ */
+
+DNSServiceErrorType DNSSD_API DNSServiceRemoveRecord
+    (
+    DNSServiceRef                 sdRef,
+    DNSRecordRef                  RecordRef,
+    DNSServiceFlags               flags
+    );
+
+
+/*********************************************************************************************
+ *
+ *  Service Discovery
+ *
+ *********************************************************************************************/
+
+/* Browse for instances of a service.
+ *
+ * DNSServiceBrowseReply() Parameters:
+ *
+ * sdRef:           The DNSServiceRef initialized by DNSServiceBrowse().
+ *
+ * flags:           Possible values are kDNSServiceFlagsMoreComing and kDNSServiceFlagsAdd.
+ *                  See flag definitions for details.
+ *
+ * interfaceIndex:  The interface on which the service is advertised. This index should
+ *                  be passed to DNSServiceResolve() when resolving the service.
+ *
+ * errorCode:       Will be kDNSServiceErr_NoError (0) on success, otherwise will
+ *                  indicate the failure that occurred. Other parameters are undefined if
+ *                  the errorCode is nonzero.
+ *
+ * serviceName:     The discovered service name. This name should be displayed to the user,
+ *                  and stored for subsequent use in the DNSServiceResolve() call.
+ *
+ * regtype:         The service type, which is usually (but not always) the same as was passed
+ *                  to DNSServiceBrowse(). One case where the discovered service type may
+ *                  not be the same as the requested service type is when using subtypes:
+ *                  The client may want to browse for only those ftp servers that allow
+ *                  anonymous connections. The client will pass the string "_ftp._tcp,_anon"
+ *                  to DNSServiceBrowse(), but the type of the service that's discovered
+ *                  is simply "_ftp._tcp". The regtype for each discovered service instance
+ *                  should be stored along with the name, so that it can be passed to
+ *                  DNSServiceResolve() when the service is later resolved.
+ *
+ * domain:          The domain of the discovered service instance. This may or may not be the
+ *                  same as the domain that was passed to DNSServiceBrowse(). The domain for each
+ *                  discovered service instance should be stored along with the name, so that
+ *                  it can be passed to DNSServiceResolve() when the service is later resolved.
+ *
+ * context:         The context pointer that was passed to the callout.
+ *
+ */
+
+typedef void (DNSSD_API *DNSServiceBrowseReply)
+    (
+    DNSServiceRef                       sdRef,
+    DNSServiceFlags                     flags,
+    uint32_t                            interfaceIndex,
+    DNSServiceErrorType                 errorCode,
+    const char                          *serviceName,
+    const char                          *regtype,
+    const char                          *replyDomain,
+    void                                *context
+    );
+
+
+/* DNSServiceBrowse() Parameters:
+ *
+ * sdRef:           A pointer to an uninitialized DNSServiceRef. If the call succeeds
+ *                  then it initializes the DNSServiceRef, returns kDNSServiceErr_NoError,
+ *                  and the browse operation will run indefinitely until the client
+ *                  terminates it by passing this DNSServiceRef to DNSServiceRefDeallocate().
+ *
+ * flags:           Currently ignored, reserved for future use.
+ *
+ * interfaceIndex:  If non-zero, specifies the interface on which to browse for services
+ *                  (the index for a given interface is determined via the if_nametoindex()
+ *                  family of calls.) Most applications will pass 0 to browse on all available
+ *                  interfaces. See "Constants for specifying an interface index" for more details.
+ *
+ * regtype:         The service type being browsed for followed by the protocol, separated by a
+ *                  dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp".
+ *                  A client may optionally specify a single subtype to perform filtered browsing:
+ *                  e.g. browsing for "_primarytype._tcp,_subtype" will discover only those
+ *                  instances of "_primarytype._tcp" that were registered specifying "_subtype"
+ *                  in their list of registered subtypes.
+ *
+ * domain:          If non-NULL, specifies the domain on which to browse for services.
+ *                  Most applications will not specify a domain, instead browsing on the
+ *                  default domain(s).
+ *
+ * callBack:        The function to be called when an instance of the service being browsed for
+ *                  is found, or if the call asynchronously fails.
+ *
+ * context:         An application context pointer which is passed to the callback function
+ *                  (may be NULL).
+ *
+ * return value:    Returns kDNSServiceErr_NoError on success (any subsequent, asynchronous
+ *                  errors are delivered to the callback), otherwise returns an error code indicating
+ *                  the error that occurred (the callback is not invoked and the DNSServiceRef
+ *                  is not initialized).
+ */
+
+DNSServiceErrorType DNSSD_API DNSServiceBrowse
+    (
+    DNSServiceRef                       *sdRef,
+    DNSServiceFlags                     flags,
+    uint32_t                            interfaceIndex,
+    const char                          *regtype,
+    const char                          *domain,    /* may be NULL */
+    DNSServiceBrowseReply               callBack,
+    void                                *context    /* may be NULL */
+    );
+
+
+/* DNSServiceResolve()
+ *
+ * Resolve a service name discovered via DNSServiceBrowse() to a target host name, port number, and
+ * txt record.
+ *
+ * Note: Applications should NOT use DNSServiceResolve() solely for txt record monitoring - use
+ * DNSServiceQueryRecord() instead, as it is more efficient for this task.
+ *
+ * Note: When the desired results have been returned, the client MUST terminate the resolve by calling
+ * DNSServiceRefDeallocate().
+ *
+ * Note: DNSServiceResolve() behaves correctly for typical services that have a single SRV record
+ * and a single TXT record. To resolve non-standard services with multiple SRV or TXT records,
+ * DNSServiceQueryRecord() should be used.
+ *
+ * DNSServiceResolveReply Callback Parameters:
+ *
+ * sdRef:           The DNSServiceRef initialized by DNSServiceResolve().
+ *
+ * flags:           Possible values: kDNSServiceFlagsMoreComing
+ *
+ * interfaceIndex:  The interface on which the service was resolved.
+ *
+ * errorCode:       Will be kDNSServiceErr_NoError (0) on success, otherwise will
+ *                  indicate the failure that occurred. Other parameters are undefined if
+ *                  the errorCode is nonzero.
+ *
+ * fullname:        The full service domain name, in the form <servicename>.<protocol>.<domain>.
+ *                  (This name is escaped following standard DNS rules, making it suitable for
+ *                  passing to standard system DNS APIs such as res_query(), or to the
+ *                  special-purpose functions included in this API that take fullname parameters.
+ *                  See "Notes on DNS Name Escaping" earlier in this file for more details.)
+ *
+ * hosttarget:      The target hostname of the machine providing the service. This name can
+ *                  be passed to functions like gethostbyname() to identify the host's IP address.
+ *
+ * port:            The port, in network byte order, on which connections are accepted for this service.
+ *
+ * txtLen:          The length of the txt record, in bytes.
+ *
+ * txtRecord:       The service's primary txt record, in standard txt record format.
+ *
+ * context:         The context pointer that was passed to the callout.
+ *
+ * NOTE: In earlier versions of this header file, the txtRecord parameter was declared "const char *"
+ * This is incorrect, since it contains length bytes which are values in the range 0 to 255, not -128 to +127.
+ * Depending on your compiler settings, this change may cause signed/unsigned mismatch warnings.
+ * These should be fixed by updating your own callback function definition to match the corrected
+ * function signature using "const unsigned char *txtRecord". Making this change may also fix inadvertent
+ * bugs in your callback function, where it could have incorrectly interpreted a length byte with value 250
+ * as being -6 instead, with various bad consequences ranging from incorrect operation to software crashes.
+ * If you need to maintain portable code that will compile cleanly with both the old and new versions of
+ * this header file, you should update your callback function definition to use the correct unsigned value,
+ * and then in the place where you pass your callback function to DNSServiceResolve(), use a cast to eliminate
+ * the compiler warning, e.g.:
+ *   DNSServiceResolve(sd, flags, index, name, regtype, domain, (DNSServiceResolveReply)MyCallback, context);
+ * This will ensure that your code compiles cleanly without warnings (and more importantly, works correctly)
+ * with both the old header and with the new corrected version.
+ *
+ */
+
+typedef void (DNSSD_API *DNSServiceResolveReply)
+    (
+    DNSServiceRef                       sdRef,
+    DNSServiceFlags                     flags,
+    uint32_t                            interfaceIndex,
+    DNSServiceErrorType                 errorCode,
+    const char                          *fullname,
+    const char                          *hosttarget,
+    uint16_t                            port,        /* In network byte order */
+    uint16_t                            txtLen,
+    const unsigned char                 *txtRecord,
+    void                                *context
+    );
+
+
+/* DNSServiceResolve() Parameters
+ *
+ * sdRef:           A pointer to an uninitialized DNSServiceRef. If the call succeeds
+ *                  then it initializes the DNSServiceRef, returns kDNSServiceErr_NoError,
+ *                  and the resolve operation will run indefinitely until the client
+ *                  terminates it by passing this DNSServiceRef to DNSServiceRefDeallocate().
+ *
+ * flags:           Specifying kDNSServiceFlagsForceMulticast will cause query to be
+ *                  performed with a link-local mDNS query, even if the name is an
+ *                  apparently non-local name (i.e. a name not ending in ".local.")
+ *
+ * interfaceIndex:  The interface on which to resolve the service. If this resolve call is
+ *                  as a result of a currently active DNSServiceBrowse() operation, then the
+ *                  interfaceIndex should be the index reported in the DNSServiceBrowseReply
+ *                  callback. If this resolve call is using information previously saved
+ *                  (e.g. in a preference file) for later use, then use interfaceIndex 0, because
+ *                  the desired service may now be reachable via a different physical interface.
+ *                  See "Constants for specifying an interface index" for more details.
+ *
+ * name:            The name of the service instance to be resolved, as reported to the
+ *                  DNSServiceBrowseReply() callback.
+ *
+ * regtype:         The type of the service instance to be resolved, as reported to the
+ *                  DNSServiceBrowseReply() callback.
+ *
+ * domain:          The domain of the service instance to be resolved, as reported to the
+ *                  DNSServiceBrowseReply() callback.
+ *
+ * callBack:        The function to be called when a result is found, or if the call
+ *                  asynchronously fails.
+ *
+ * context:         An application context pointer which is passed to the callback function
+ *                  (may be NULL).
+ *
+ * return value:    Returns kDNSServiceErr_NoError on success (any subsequent, asynchronous
+ *                  errors are delivered to the callback), otherwise returns an error code indicating
+ *                  the error that occurred (the callback is never invoked and the DNSServiceRef
+ *                  is not initialized).
+ */
+
+DNSServiceErrorType DNSSD_API DNSServiceResolve
+    (
+    DNSServiceRef                       *sdRef,
+    DNSServiceFlags                     flags,
+    uint32_t                            interfaceIndex,
+    const char                          *name,
+    const char                          *regtype,
+    const char                          *domain,
+    DNSServiceResolveReply              callBack,
+    void                                *context  /* may be NULL */
+    );
+
+
+/*********************************************************************************************
+ *
+ *  Querying Individual Specific Records
+ *
+ *********************************************************************************************/
+
+/* DNSServiceQueryRecord
+ *
+ * Query for an arbitrary DNS record.
+ *
+ * DNSServiceQueryRecordReply() Callback Parameters:
+ *
+ * sdRef:           The DNSServiceRef initialized by DNSServiceQueryRecord().
+ *
+ * flags:           Possible values are kDNSServiceFlagsMoreComing and
+ *                  kDNSServiceFlagsAdd. The Add flag is NOT set for PTR records
+ *                  with a ttl of 0, i.e. "Remove" events.
+ *
+ * interfaceIndex:  The interface on which the query was resolved (the index for a given
+ *                  interface is determined via the if_nametoindex() family of calls).
+ *                  See "Constants for specifying an interface index" for more details.
+ *
+ * errorCode:       Will be kDNSServiceErr_NoError on success, otherwise will
+ *                  indicate the failure that occurred. Other parameters are undefined if
+ *                  errorCode is nonzero.
+ *
+ * fullname:        The resource record's full domain name.
+ *
+ * rrtype:          The resource record's type (e.g. kDNSServiceType_PTR, kDNSServiceType_SRV, etc)
+ *
+ * rrclass:         The class of the resource record (usually kDNSServiceClass_IN).
+ *
+ * rdlen:           The length, in bytes, of the resource record rdata.
+ *
+ * rdata:           The raw rdata of the resource record.
+ *
+ * ttl:             If the client wishes to cache the result for performance reasons,
+ *                  the TTL indicates how long the client may legitimately hold onto
+ *                  this result, in seconds. After the TTL expires, the client should
+ *                  consider the result no longer valid, and if it requires this data
+ *                  again, it should be re-fetched with a new query. Of course, this
+ *                  only applies to clients that cancel the asynchronous operation when
+ *                  they get a result. Clients that leave the asynchronous operation
+ *                  running can safely assume that the data remains valid until they
+ *                  get another callback telling them otherwise.
+ *
+ * context:         The context pointer that was passed to the callout.
+ *
+ */
+
+typedef void (DNSSD_API *DNSServiceQueryRecordReply)
+    (
+    DNSServiceRef                       sdRef,
+    DNSServiceFlags                     flags,
+    uint32_t                            interfaceIndex,
+    DNSServiceErrorType                 errorCode,
+    const char                          *fullname,
+    uint16_t                            rrtype,
+    uint16_t                            rrclass,
+    uint16_t                            rdlen,
+    const void                          *rdata,
+    uint32_t                            ttl,
+    void                                *context
+    );
+
+
+/* DNSServiceQueryRecord() Parameters:
+ *
+ * sdRef:           A pointer to an uninitialized DNSServiceRef. If the call succeeds
+ *                  then it initializes the DNSServiceRef, returns kDNSServiceErr_NoError,
+ *                  and the query operation will run indefinitely until the client
+ *                  terminates it by passing this DNSServiceRef to DNSServiceRefDeallocate().
+ *
+ * flags:           kDNSServiceFlagsForceMulticast or kDNSServiceFlagsLongLivedQuery.
+ *                  Pass kDNSServiceFlagsLongLivedQuery to create a "long-lived" unicast
+ *                  query in a non-local domain. Without setting this flag, unicast queries
+ *                  will be one-shot - that is, only answers available at the time of the call
+ *                  will be returned. By setting this flag, answers (including Add and Remove
+ *                  events) that become available after the initial call is made will generate
+ *                  callbacks. This flag has no effect on link-local multicast queries.
+ *
+ * interfaceIndex:  If non-zero, specifies the interface on which to issue the query
+ *                  (the index for a given interface is determined via the if_nametoindex()
+ *                  family of calls.) Passing 0 causes the name to be queried for on all
+ *                  interfaces. See "Constants for specifying an interface index" for more details.
+ *
+ * fullname:        The full domain name of the resource record to be queried for.
+ *
+ * rrtype:          The numerical type of the resource record to be queried for
+ *                  (e.g. kDNSServiceType_PTR, kDNSServiceType_SRV, etc)
+ *
+ * rrclass:         The class of the resource record (usually kDNSServiceClass_IN).
+ *
+ * callBack:        The function to be called when a result is found, or if the call
+ *                  asynchronously fails.
+ *
+ * context:         An application context pointer which is passed to the callback function
+ *                  (may be NULL).
+ *
+ * return value:    Returns kDNSServiceErr_NoError on success (any subsequent, asynchronous
+ *                  errors are delivered to the callback), otherwise returns an error code indicating
+ *                  the error that occurred (the callback is never invoked and the DNSServiceRef
+ *                  is not initialized).
+ */
+
+DNSServiceErrorType DNSSD_API DNSServiceQueryRecord
+    (
+    DNSServiceRef                       *sdRef,
+    DNSServiceFlags                     flags,
+    uint32_t                            interfaceIndex,
+    const char                          *fullname,
+    uint16_t                            rrtype,
+    uint16_t                            rrclass,
+    DNSServiceQueryRecordReply          callBack,
+    void                                *context  /* may be NULL */
+    );
+
+
+/*********************************************************************************************
+ *
+ *  Unified lookup of both IPv4 and IPv6 addresses for a fully qualified hostname
+ *
+ *********************************************************************************************/
+
+/* DNSServiceGetAddrInfo
+ *
+ * Queries for the IP address of a hostname by using either Multicast or Unicast DNS.
+ *
+ * DNSServiceGetAddrInfoReply() parameters:
+ *
+ * sdRef:           The DNSServiceRef initialized by DNSServiceGetAddrInfo().
+ *
+ * flags:           Possible values are kDNSServiceFlagsMoreComing and
+ *                  kDNSServiceFlagsAdd.
+ *
+ * interfaceIndex:  The interface to which the answers pertain.
+ *
+ * errorCode:       Will be kDNSServiceErr_NoError on success, otherwise will
+ *                  indicate the failure that occurred.  Other parameters are
+ *                  undefined if errorCode is nonzero.
+ *
+ * hostname:        The fully qualified domain name of the host to be queried for.
+ *
+ * address:         IPv4 or IPv6 address.
+ *
+ * ttl:             If the client wishes to cache the result for performance reasons,
+ *                  the TTL indicates how long the client may legitimately hold onto
+ *                  this result, in seconds. After the TTL expires, the client should
+ *                  consider the result no longer valid, and if it requires this data
+ *                  again, it should be re-fetched with a new query. Of course, this
+ *                  only applies to clients that cancel the asynchronous operation when
+ *                  they get a result. Clients that leave the asynchronous operation
+ *                  running can safely assume that the data remains valid until they
+ *                  get another callback telling them otherwise.
+ *
+ * context:         The context pointer that was passed to the callout.
+ *
+ */
+
+typedef void (DNSSD_API *DNSServiceGetAddrInfoReply)
+    (
+    DNSServiceRef                    sdRef,
+    DNSServiceFlags                  flags,
+    uint32_t                         interfaceIndex,
+    DNSServiceErrorType              errorCode,
+    const char                       *hostname,
+    const struct sockaddr            *address,
+    uint32_t                         ttl,
+    void                             *context
+    );
+
+
+/* DNSServiceGetAddrInfo() Parameters:
+ *
+ * sdRef:           A pointer to an uninitialized DNSServiceRef. If the call succeeds then it
+ *                  initializes the DNSServiceRef, returns kDNSServiceErr_NoError, and the query
+ *                  begins and will last indefinitely until the client terminates the query
+ *                  by passing this DNSServiceRef to DNSServiceRefDeallocate().
+ *
+ * flags:           kDNSServiceFlagsForceMulticast or kDNSServiceFlagsLongLivedQuery.
+ *                  Pass kDNSServiceFlagsLongLivedQuery to create a "long-lived" unicast
+ *                  query in a non-local domain. Without setting this flag, unicast queries
+ *                  will be one-shot - that is, only answers available at the time of the call
+ *                  will be returned. By setting this flag, answers (including Add and Remove
+ *                  events) that become available after the initial call is made will generate
+ *                  callbacks. This flag has no effect on link-local multicast queries.
+ *
+ * interfaceIndex:  The interface on which to issue the query.  Passing 0 causes the query to be
+ *                  sent on all active interfaces via Multicast or the primary interface via Unicast.
+ *
+ * protocol:        Pass in kDNSServiceProtocol_IPv4 to look up IPv4 addresses, or kDNSServiceProtocol_IPv6
+ *                  to look up IPv6 addresses, or both to look up both kinds. If neither flag is
+ *                  set, the system will apply an intelligent heuristic, which is (currently)
+ *                  that it will attempt to look up both, except:
+ *
+ *                   * If "hostname" is a wide-area unicast DNS hostname (i.e. not a ".local." name)
+ *                     but this host has no routable IPv6 address, then the call will not try to
+ *                     look up IPv6 addresses for "hostname", since any addresses it found would be
+ *                     unlikely to be of any use anyway. Similarly, if this host has no routable
+ *                     IPv4 address, the call will not try to look up IPv4 addresses for "hostname".
+ *
+ * hostname:        The fully qualified domain name of the host to be queried for.
+ *
+ * callBack:        The function to be called when the query succeeds or fails asynchronously.
+ *
+ * context:         An application context pointer which is passed to the callback function
+ *                  (may be NULL).
+ *
+ * return value:    Returns kDNSServiceErr_NoError on success (any subsequent, asynchronous
+ *                  errors are delivered to the callback), otherwise returns an error code indicating
+ *                  the error that occurred.
+ */
+
+DNSServiceErrorType DNSSD_API DNSServiceGetAddrInfo
+    (
+    DNSServiceRef                    *sdRef,
+    DNSServiceFlags                  flags,
+    uint32_t                         interfaceIndex,
+    DNSServiceProtocol               protocol,
+    const char                       *hostname,
+    DNSServiceGetAddrInfoReply       callBack,
+    void                             *context          /* may be NULL */
+    );
+
+
+/*********************************************************************************************
+ *
+ *  Special Purpose Calls:
+ *  DNSServiceCreateConnection(), DNSServiceRegisterRecord(), DNSServiceReconfirmRecord()
+ *  (most applications will not use these)
+ *
+ *********************************************************************************************/
+
+/* DNSServiceCreateConnection()
+ *
+ * Create a connection to the daemon allowing efficient registration of
+ * multiple individual records.
+ *
+ * Parameters:
+ *
+ * sdRef:           A pointer to an uninitialized DNSServiceRef. Deallocating
+ *                  the reference (via DNSServiceRefDeallocate()) severs the
+ *                  connection and deregisters all records registered on this connection.
+ *
+ * return value:    Returns kDNSServiceErr_NoError on success, otherwise returns
+ *                  an error code indicating the specific failure that occurred (in which
+ *                  case the DNSServiceRef is not initialized).
+ */
+
+DNSServiceErrorType DNSSD_API DNSServiceCreateConnection(DNSServiceRef *sdRef);
+
+
+/* DNSServiceRegisterRecord
+ *
+ * Register an individual resource record on a connected DNSServiceRef.
+ *
+ * Note that name conflicts occurring for records registered via this call must be handled
+ * by the client in the callback.
+ *
+ * DNSServiceRegisterRecordReply() parameters:
+ *
+ * sdRef:           The connected DNSServiceRef initialized by
+ *                  DNSServiceCreateConnection().
+ *
+ * RecordRef:       The DNSRecordRef initialized by DNSServiceRegisterRecord(). If the above
+ *                  DNSServiceRef is passed to DNSServiceRefDeallocate(), this DNSRecordRef is
+ *                  invalidated, and may not be used further.
+ *
+ * flags:           Currently unused, reserved for future use.
+ *
+ * errorCode:       Will be kDNSServiceErr_NoError on success, otherwise will
+ *                  indicate the failure that occurred (including name conflicts.)
+ *                  Other parameters are undefined if errorCode is nonzero.
+ *
+ * context:         The context pointer that was passed to the callout.
+ *
+ */
+
+ typedef void (DNSSD_API *DNSServiceRegisterRecordReply)
+    (
+    DNSServiceRef                       sdRef,
+    DNSRecordRef                        RecordRef,
+    DNSServiceFlags                     flags,
+    DNSServiceErrorType                 errorCode,
+    void                                *context
+    );
+
+
+/* DNSServiceRegisterRecord() Parameters:
+ *
+ * sdRef:           A DNSServiceRef initialized by DNSServiceCreateConnection().
+ *
+ * RecordRef:       A pointer to an uninitialized DNSRecordRef. Upon succesfull completion of this
+ *                  call, this ref may be passed to DNSServiceUpdateRecord() or DNSServiceRemoveRecord().
+ *                  (To deregister ALL records registered on a single connected DNSServiceRef
+ *                  and deallocate each of their corresponding DNSServiceRecordRefs, call
+ *                  DNSServiceRefDeallocate()).
+ *
+ * flags:           Possible values are kDNSServiceFlagsShared or kDNSServiceFlagsUnique
+ *                  (see flag type definitions for details).
+ *
+ * interfaceIndex:  If non-zero, specifies the interface on which to register the record
+ *                  (the index for a given interface is determined via the if_nametoindex()
+ *                  family of calls.) Passing 0 causes the record to be registered on all interfaces.
+ *                  See "Constants for specifying an interface index" for more details.
+ *
+ * fullname:        The full domain name of the resource record.
+ *
+ * rrtype:          The numerical type of the resource record (e.g. kDNSServiceType_PTR, kDNSServiceType_SRV, etc)
+ *
+ * rrclass:         The class of the resource record (usually kDNSServiceClass_IN)
+ *
+ * rdlen:           Length, in bytes, of the rdata.
+ *
+ * rdata:           A pointer to the raw rdata, as it is to appear in the DNS record.
+ *
+ * ttl:             The time to live of the resource record, in seconds.
+ *                  Most clients should pass 0 to indicate that the system should
+ *                  select a sensible default value.
+ *
+ * callBack:        The function to be called when a result is found, or if the call
+ *                  asynchronously fails (e.g. because of a name conflict.)
+ *
+ * context:         An application context pointer which is passed to the callback function
+ *                  (may be NULL).
+ *
+ * return value:    Returns kDNSServiceErr_NoError on success (any subsequent, asynchronous
+ *                  errors are delivered to the callback), otherwise returns an error code indicating
+ *                  the error that occurred (the callback is never invoked and the DNSRecordRef is
+ *                  not initialized).
+ */
+
+DNSServiceErrorType DNSSD_API DNSServiceRegisterRecord
+    (
+    DNSServiceRef                       sdRef,
+    DNSRecordRef                        *RecordRef,
+    DNSServiceFlags                     flags,
+    uint32_t                            interfaceIndex,
+    const char                          *fullname,
+    uint16_t                            rrtype,
+    uint16_t                            rrclass,
+    uint16_t                            rdlen,
+    const void                          *rdata,
+    uint32_t                            ttl,
+    DNSServiceRegisterRecordReply       callBack,
+    void                                *context    /* may be NULL */
+    );
+
+
+/* DNSServiceReconfirmRecord
+ *
+ * Instruct the daemon to verify the validity of a resource record that appears
+ * to be out of date (e.g. because TCP connection to a service's target failed.)
+ * Causes the record to be flushed from the daemon's cache (as well as all other
+ * daemons' caches on the network) if the record is determined to be invalid.
+ * Use this routine conservatively. Reconfirming a record necessarily consumes
+ * network bandwidth, so this should not be done indiscriminately.
+ *
+ * Parameters:
+ *
+ * flags:           Pass kDNSServiceFlagsForce to force immediate deletion of record,
+ *                  instead of after some number of reconfirmation queries have gone unanswered.
+ *
+ * interfaceIndex:  Specifies the interface of the record in question.
+ *                  The caller must specify the interface.
+ *                  This API (by design) causes increased network traffic, so it requires
+ *                  the caller to be precise about which record should be reconfirmed.
+ *                  It is not possible to pass zero for the interface index to perform
+ *                  a "wildcard" reconfirmation, where *all* matching records are reconfirmed.
+ *
+ * fullname:        The resource record's full domain name.
+ *
+ * rrtype:          The resource record's type (e.g. kDNSServiceType_PTR, kDNSServiceType_SRV, etc)
+ *
+ * rrclass:         The class of the resource record (usually kDNSServiceClass_IN).
+ *
+ * rdlen:           The length, in bytes, of the resource record rdata.
+ *
+ * rdata:           The raw rdata of the resource record.
+ *
+ */
+
+DNSServiceErrorType DNSSD_API DNSServiceReconfirmRecord
+    (
+    DNSServiceFlags                    flags,
+    uint32_t                           interfaceIndex,
+    const char                         *fullname,
+    uint16_t                           rrtype,
+    uint16_t                           rrclass,
+    uint16_t                           rdlen,
+    const void                         *rdata
+    );
+
+
+/*********************************************************************************************
+ *
+ *  NAT Port Mapping
+ *
+ *********************************************************************************************/
+
+/* DNSServiceNATPortMappingCreate
+ *
+ * Request a port mapping in the NAT gateway, which maps a port on the local machine
+ * to an external port on the NAT. The NAT should support either the NAT-PMP or the UPnP IGD
+ * protocol for this API to create a successful mapping.
+ *
+ * The port mapping will be renewed indefinitely until the client process exits, or
+ * explicitly terminates the port mapping request by calling DNSServiceRefDeallocate().
+ * The client callback will be invoked, informing the client of the NAT gateway's
+ * external IP address and the external port that has been allocated for this client.
+ * The client should then record this external IP address and port using whatever
+ * directory service mechanism it is using to enable peers to connect to it.
+ * (Clients advertising services using Wide-Area DNS-SD DO NOT need to use this API
+ * -- when a client calls DNSServiceRegister() NAT mappings are automatically created
+ * and the external IP address and port for the service are recorded in the global DNS.
+ * Only clients using some directory mechanism other than Wide-Area DNS-SD need to use
+ * this API to explicitly map their own ports.)
+ *
+ * It's possible that the client callback could be called multiple times, for example
+ * if the NAT gateway's IP address changes, or if a configuration change results in a
+ * different external port being mapped for this client. Over the lifetime of any long-lived
+ * port mapping, the client should be prepared to handle these notifications of changes
+ * in the environment, and should update its recorded address and/or port as appropriate.
+ *
+ * NOTE: There are two unusual aspects of how the DNSServiceNATPortMappingCreate API works,
+ * which were intentionally designed to help simplify client code:
+ *
+ *  1. It's not an error to request a NAT mapping when the machine is not behind a NAT gateway.
+ *     In other NAT mapping APIs, if you request a NAT mapping and the machine is not behind a NAT
+ *     gateway, then the API returns an error code -- it can't get you a NAT mapping if there's no
+ *     NAT gateway. The DNSServiceNATPortMappingCreate API takes a different view. Working out
+ *     whether or not you need a NAT mapping can be tricky and non-obvious, particularly on
+ *     a machine with multiple active network interfaces. Rather than make every client recreate
+ *     this logic for deciding whether a NAT mapping is required, the PortMapping API does that
+ *     work for you. If the client calls the PortMapping API when the machine already has a
+ *     routable public IP address, then instead of complaining about it and giving an error,
+ *     the PortMapping API just invokes your callback, giving the machine's public address
+ *     and your own port number. This means you don't need to write code to work out whether
+ *     your client needs to call the PortMapping API -- just call it anyway, and if it wasn't
+ *     necessary, no harm is done:
+ *
+ *     - If the machine already has a routable public IP address, then your callback
+ *       will just be invoked giving your own address and port.
+ *     - If a NAT mapping is required and obtained, then your callback will be invoked
+ *       giving you the external address and port.
+ *     - If a NAT mapping is required but not obtained from the local NAT gateway,
+ *       or the machine has no network connectivity, then your callback will be
+ *       invoked giving zero address and port.
+ *
+ *  2. In other NAT mapping APIs, if a laptop computer is put to sleep and woken up on a new
+ *     network, it's the client's job to notice this, and work out whether a NAT mapping
+ *     is required on the new network, and make a new NAT mapping request if necessary.
+ *     The DNSServiceNATPortMappingCreate API does this for you, automatically.
+ *     The client just needs to make one call to the PortMapping API, and its callback will
+ *     be invoked any time the mapping state changes. This property complements point (1) above.
+ *     If the client didn't make a NAT mapping request just because it determined that one was
+ *     not required at that particular moment in time, the client would then have to monitor
+ *     for network state changes to determine if a NAT port mapping later became necessary.
+ *     By unconditionally making a NAT mapping request, even when a NAT mapping not to be
+ *     necessary, the PortMapping API will then begin monitoring network state changes on behalf of
+ *     the client, and if a NAT mapping later becomes necessary, it will automatically create a NAT
+ *     mapping and inform the client with a new callback giving the new address and port information.
+ *
+ * DNSServiceNATPortMappingReply() parameters:
+ *
+ * sdRef:           The DNSServiceRef initialized by DNSServiceNATPortMappingCreate().
+ *
+ * flags:           Currently unused, reserved for future use.
+ *
+ * interfaceIndex:  The interface through which the NAT gateway is reached.
+ *
+ * errorCode:       Will be kDNSServiceErr_NoError on success.
+ *                  Will be kDNSServiceErr_DoubleNAT when the NAT gateway is itself behind one or
+ *                  more layers of NAT, in which case the other parameters have the defined values.
+ *                  For other failures, will indicate the failure that occurred, and the other
+ *                  parameters are undefined.
+ *
+ * externalAddress: Four byte IPv4 address in network byte order.
+ *
+ * protocol:        Will be kDNSServiceProtocol_UDP or kDNSServiceProtocol_TCP or both.
+ *
+ * internalPort:    The port on the local machine that was mapped.
+ *
+ * externalPort:    The actual external port in the NAT gateway that was mapped.
+ *                  This is likely to be different than the requested external port.
+ *
+ * ttl:             The lifetime of the NAT port mapping created on the gateway.
+ *                  This controls how quickly stale mappings will be garbage-collected
+ *                  if the client machine crashes, suffers a power failure, is disconnected
+ *                  from the network, or suffers some other unfortunate demise which
+ *                  causes it to vanish without explicitly removing its NAT port mapping.
+ *                  It's possible that the ttl value will differ from the requested ttl value.
+ *
+ * context:         The context pointer that was passed to the callout.
+ *
+ */
+
+typedef void (DNSSD_API *DNSServiceNATPortMappingReply)
+    (
+    DNSServiceRef                    sdRef,
+    DNSServiceFlags                  flags,
+    uint32_t                         interfaceIndex,
+    DNSServiceErrorType              errorCode,
+    uint32_t                         externalAddress,   /* four byte IPv4 address in network byte order */
+    DNSServiceProtocol               protocol,
+    uint16_t                         internalPort,      /* In network byte order */
+    uint16_t                         externalPort,      /* In network byte order and may be different than the requested port */
+    uint32_t                         ttl,               /* may be different than the requested ttl */
+    void                             *context
+    );
+
+
+/* DNSServiceNATPortMappingCreate() Parameters:
+ *
+ * sdRef:           A pointer to an uninitialized DNSServiceRef. If the call succeeds then it
+ *                  initializes the DNSServiceRef, returns kDNSServiceErr_NoError, and the nat
+ *                  port mapping will last indefinitely until the client terminates the port
+ *                  mapping request by passing this DNSServiceRef to DNSServiceRefDeallocate().
+ *
+ * flags:           Currently ignored, reserved for future use.
+ *
+ * interfaceIndex:  The interface on which to create port mappings in a NAT gateway. Passing 0 causes
+ *                  the port mapping request to be sent on the primary interface.
+ *
+ * protocol:        To request a port mapping, pass in kDNSServiceProtocol_UDP, or kDNSServiceProtocol_TCP,
+ *                  or (kDNSServiceProtocol_UDP | kDNSServiceProtocol_TCP) to map both.
+ *                  The local listening port number must also be specified in the internalPort parameter.
+ *                  To just discover the NAT gateway's external IP address, pass zero for protocol,
+ *                  internalPort, externalPort and ttl.
+ *
+ * internalPort:    The port number in network byte order on the local machine which is listening for packets.
+ *
+ * externalPort:    The requested external port in network byte order in the NAT gateway that you would
+ *                  like to map to the internal port. Pass 0 if you don't care which external port is chosen for you.
+ *
+ * ttl:             The requested renewal period of the NAT port mapping, in seconds.
+ *                  If the client machine crashes, suffers a power failure, is disconnected from
+ *                  the network, or suffers some other unfortunate demise which causes it to vanish
+ *                  unexpectedly without explicitly removing its NAT port mappings, then the NAT gateway
+ *                  will garbage-collect old stale NAT port mappings when their lifetime expires.
+ *                  Requesting a short TTL causes such orphaned mappings to be garbage-collected
+ *                  more promptly, but consumes system resources and network bandwidth with
+ *                  frequent renewal packets to keep the mapping from expiring.
+ *                  Requesting a long TTL is more efficient on the network, but in the event of the
+ *                  client vanishing, stale NAT port mappings will not be garbage-collected as quickly.
+ *                  Most clients should pass 0 to use a system-wide default value.
+ *
+ * callBack:        The function to be called when the port mapping request succeeds or fails asynchronously.
+ *
+ * context:         An application context pointer which is passed to the callback function
+ *                  (may be NULL).
+ *
+ * return value:    Returns kDNSServiceErr_NoError on success (any subsequent, asynchronous
+ *                  errors are delivered to the callback), otherwise returns an error code indicating
+ *                  the error that occurred.
+ *
+ *                  If you don't actually want a port mapped, and are just calling the API
+ *                  because you want to find out the NAT's external IP address (e.g. for UI
+ *                  display) then pass zero for protocol, internalPort, externalPort and ttl.
+ */
+
+DNSServiceErrorType DNSSD_API DNSServiceNATPortMappingCreate
+    (
+    DNSServiceRef                    *sdRef,
+    DNSServiceFlags                  flags,
+    uint32_t                         interfaceIndex,
+    DNSServiceProtocol               protocol,          /* TCP and/or UDP          */
+    uint16_t                         internalPort,      /* network byte order      */
+    uint16_t                         externalPort,      /* network byte order      */
+    uint32_t                         ttl,               /* time to live in seconds */
+    DNSServiceNATPortMappingReply    callBack,
+    void                             *context           /* may be NULL             */
+    );
+
+
+typedef void (DNSSD_API *DNSHostnameChangedReply)
+    (
+    DNSServiceRef                    sdRef,
+    DNSServiceFlags                  flags,
+    DNSServiceErrorType              errorCode,
+    const char                       *hostname,
+    void                             *context
+    );
+
+DNSServiceErrorType DNSSD_API DNSSetHostname
+    (
+    DNSServiceRef                    *sdRef,
+    DNSServiceFlags                  flags,
+    const char                       *hostname,
+    DNSHostnameChangedReply          callBack,
+    void                             *context
+    );
+
+/*********************************************************************************************
+ *
+ *  General Utility Functions
+ *
+ *********************************************************************************************/
+
+/* DNSServiceConstructFullName()
+ *
+ * Concatenate a three-part domain name (as returned by the above callbacks) into a
+ * properly-escaped full domain name. Note that callbacks in the above functions ALREADY ESCAPE
+ * strings where necessary.
+ *
+ * Parameters:
+ *
+ * fullName:        A pointer to a buffer that where the resulting full domain name is to be written.
+ *                  The buffer must be kDNSServiceMaxDomainName (1009) bytes in length to
+ *                  accommodate the longest legal domain name without buffer overrun.
+ *
+ * service:         The service name - any dots or backslashes must NOT be escaped.
+ *                  May be NULL (to construct a PTR record name, e.g.
+ *                  "_ftp._tcp.apple.com.").
+ *
+ * regtype:         The service type followed by the protocol, separated by a dot
+ *                  (e.g. "_ftp._tcp").
+ *
+ * domain:          The domain name, e.g. "apple.com.". Literal dots or backslashes,
+ *                  if any, must be escaped, e.g. "1st\. Floor.apple.com."
+ *
+ * return value:    Returns kDNSServiceErr_NoError (0) on success, kDNSServiceErr_BadParam on error.
+ *
+ */
+
+DNSServiceErrorType DNSSD_API DNSServiceConstructFullName
+    (
+    char                            * const fullName,
+    const char                      * const service,      /* may be NULL */
+    const char                      * const regtype,
+    const char                      * const domain
+    );
+
+
+/*********************************************************************************************
+ *
+ *   TXT Record Construction Functions
+ *
+ *********************************************************************************************/
+
+/*
+ * A typical calling sequence for TXT record construction is something like:
+ *
+ * Client allocates storage for TXTRecord data (e.g. declare buffer on the stack)
+ * TXTRecordCreate();
+ * TXTRecordSetValue();
+ * TXTRecordSetValue();
+ * TXTRecordSetValue();
+ * ...
+ * DNSServiceRegister( ... TXTRecordGetLength(), TXTRecordGetBytesPtr() ... );
+ * TXTRecordDeallocate();
+ * Explicitly deallocate storage for TXTRecord data (if not allocated on the stack)
+ */
+
+
+/* TXTRecordRef
+ *
+ * Opaque internal data type.
+ * Note: Represents a DNS-SD TXT record.
+ */
+
+typedef union _TXTRecordRef_t { char PrivateData[16]; char *ForceNaturalAlignment; } TXTRecordRef;
+
+
+/* TXTRecordCreate()
+ *
+ * Creates a new empty TXTRecordRef referencing the specified storage.
+ *
+ * If the buffer parameter is NULL, or the specified storage size is not
+ * large enough to hold a key subsequently added using TXTRecordSetValue(),
+ * then additional memory will be added as needed using malloc().
+ *
+ * On some platforms, when memory is low, malloc() may fail. In this
+ * case, TXTRecordSetValue() will return kDNSServiceErr_NoMemory, and this
+ * error condition will need to be handled as appropriate by the caller.
+ *
+ * You can avoid the need to handle this error condition if you ensure
+ * that the storage you initially provide is large enough to hold all
+ * the key/value pairs that are to be added to the record.
+ * The caller can precompute the exact length required for all of the
+ * key/value pairs to be added, or simply provide a fixed-sized buffer
+ * known in advance to be large enough.
+ * A no-value (key-only) key requires  (1 + key length) bytes.
+ * A key with empty value requires     (1 + key length + 1) bytes.
+ * A key with non-empty value requires (1 + key length + 1 + value length).
+ * For most applications, DNS-SD TXT records are generally
+ * less than 100 bytes, so in most cases a simple fixed-sized
+ * 256-byte buffer will be more than sufficient.
+ * Recommended size limits for DNS-SD TXT Records are discussed in
+ * <http://files.dns-sd.org/draft-cheshire-dnsext-dns-sd.txt>
+ *
+ * Note: When passing parameters to and from these TXT record APIs,
+ * the key name does not include the '=' character. The '=' character
+ * is the separator between the key and value in the on-the-wire
+ * packet format; it is not part of either the key or the value.
+ *
+ * txtRecord:       A pointer to an uninitialized TXTRecordRef.
+ *
+ * bufferLen:       The size of the storage provided in the "buffer" parameter.
+ *
+ * buffer:          Optional caller-supplied storage used to hold the TXTRecord data.
+ *                  This storage must remain valid for as long as
+ *                  the TXTRecordRef.
+ */
+
+void DNSSD_API TXTRecordCreate
+    (
+    TXTRecordRef     *txtRecord,
+    uint16_t         bufferLen,
+    void             *buffer
+    );
+
+
+/* TXTRecordDeallocate()
+ *
+ * Releases any resources allocated in the course of preparing a TXT Record
+ * using TXTRecordCreate()/TXTRecordSetValue()/TXTRecordRemoveValue().
+ * Ownership of the buffer provided in TXTRecordCreate() returns to the client.
+ *
+ * txtRecord:           A TXTRecordRef initialized by calling TXTRecordCreate().
+ *
+ */
+
+void DNSSD_API TXTRecordDeallocate
+    (
+    TXTRecordRef     *txtRecord
+    );
+
+
+/* TXTRecordSetValue()
+ *
+ * Adds a key (optionally with value) to a TXTRecordRef. If the "key" already
+ * exists in the TXTRecordRef, then the current value will be replaced with
+ * the new value.
+ * Keys may exist in four states with respect to a given TXT record:
+ *  - Absent (key does not appear at all)
+ *  - Present with no value ("key" appears alone)
+ *  - Present with empty value ("key=" appears in TXT record)
+ *  - Present with non-empty value ("key=value" appears in TXT record)
+ * For more details refer to "Data Syntax for DNS-SD TXT Records" in
+ * <http://files.dns-sd.org/draft-cheshire-dnsext-dns-sd.txt>
+ *
+ * txtRecord:       A TXTRecordRef initialized by calling TXTRecordCreate().
+ *
+ * key:             A null-terminated string which only contains printable ASCII
+ *                  values (0x20-0x7E), excluding '=' (0x3D). Keys should be
+ *                  9 characters or fewer (not counting the terminating null).
+ *
+ * valueSize:       The size of the value.
+ *
+ * value:           Any binary value. For values that represent
+ *                  textual data, UTF-8 is STRONGLY recommended.
+ *                  For values that represent textual data, valueSize
+ *                  should NOT include the terminating null (if any)
+ *                  at the end of the string.
+ *                  If NULL, then "key" will be added with no value.
+ *                  If non-NULL but valueSize is zero, then "key=" will be
+ *                  added with empty value.
+ *
+ * return value:    Returns kDNSServiceErr_NoError on success.
+ *                  Returns kDNSServiceErr_Invalid if the "key" string contains
+ *                  illegal characters.
+ *                  Returns kDNSServiceErr_NoMemory if adding this key would
+ *                  exceed the available storage.
+ */
+
+DNSServiceErrorType DNSSD_API TXTRecordSetValue
+    (
+    TXTRecordRef     *txtRecord,
+    const char       *key,
+    uint8_t          valueSize,        /* may be zero */
+    const void       *value            /* may be NULL */
+    );
+
+
+/* TXTRecordRemoveValue()
+ *
+ * Removes a key from a TXTRecordRef. The "key" must be an
+ * ASCII string which exists in the TXTRecordRef.
+ *
+ * txtRecord:       A TXTRecordRef initialized by calling TXTRecordCreate().
+ *
+ * key:             A key name which exists in the TXTRecordRef.
+ *
+ * return value:    Returns kDNSServiceErr_NoError on success.
+ *                  Returns kDNSServiceErr_NoSuchKey if the "key" does not
+ *                  exist in the TXTRecordRef.
+ */
+
+DNSServiceErrorType DNSSD_API TXTRecordRemoveValue
+    (
+    TXTRecordRef     *txtRecord,
+    const char       *key
+    );
+
+
+/* TXTRecordGetLength()
+ *
+ * Allows you to determine the length of the raw bytes within a TXTRecordRef.
+ *
+ * txtRecord:       A TXTRecordRef initialized by calling TXTRecordCreate().
+ *
+ * return value:    Returns the size of the raw bytes inside a TXTRecordRef
+ *                  which you can pass directly to DNSServiceRegister() or
+ *                  to DNSServiceUpdateRecord().
+ *                  Returns 0 if the TXTRecordRef is empty.
+ */
+
+uint16_t DNSSD_API TXTRecordGetLength
+    (
+    const TXTRecordRef *txtRecord
+    );
+
+
+/* TXTRecordGetBytesPtr()
+ *
+ * Allows you to retrieve a pointer to the raw bytes within a TXTRecordRef.
+ *
+ * txtRecord:       A TXTRecordRef initialized by calling TXTRecordCreate().
+ *
+ * return value:    Returns a pointer to the raw bytes inside the TXTRecordRef
+ *                  which you can pass directly to DNSServiceRegister() or
+ *                  to DNSServiceUpdateRecord().
+ */
+
+const void * DNSSD_API TXTRecordGetBytesPtr
+    (
+    const TXTRecordRef *txtRecord
+    );
+
+
+/*********************************************************************************************
+ *
+ *   TXT Record Parsing Functions
+ *
+ *********************************************************************************************/
+
+/*
+ * A typical calling sequence for TXT record parsing is something like:
+ *
+ * Receive TXT record data in DNSServiceResolve() callback
+ * if (TXTRecordContainsKey(txtLen, txtRecord, "key")) then do something
+ * val1ptr = TXTRecordGetValuePtr(txtLen, txtRecord, "key1", &len1);
+ * val2ptr = TXTRecordGetValuePtr(txtLen, txtRecord, "key2", &len2);
+ * ...
+ * memcpy(myval1, val1ptr, len1);
+ * memcpy(myval2, val2ptr, len2);
+ * ...
+ * return;
+ *
+ * If you wish to retain the values after return from the DNSServiceResolve()
+ * callback, then you need to copy the data to your own storage using memcpy()
+ * or similar, as shown in the example above.
+ *
+ * If for some reason you need to parse a TXT record you built yourself
+ * using the TXT record construction functions above, then you can do
+ * that using TXTRecordGetLength and TXTRecordGetBytesPtr calls:
+ * TXTRecordGetValue(TXTRecordGetLength(x), TXTRecordGetBytesPtr(x), key, &len);
+ *
+ * Most applications only fetch keys they know about from a TXT record and
+ * ignore the rest.
+ * However, some debugging tools wish to fetch and display all keys.
+ * To do that, use the TXTRecordGetCount() and TXTRecordGetItemAtIndex() calls.
+ */
+
+/* TXTRecordContainsKey()
+ *
+ * Allows you to determine if a given TXT Record contains a specified key.
+ *
+ * txtLen:          The size of the received TXT Record.
+ *
+ * txtRecord:       Pointer to the received TXT Record bytes.
+ *
+ * key:             A null-terminated ASCII string containing the key name.
+ *
+ * return value:    Returns 1 if the TXT Record contains the specified key.
+ *                  Otherwise, it returns 0.
+ */
+
+int DNSSD_API TXTRecordContainsKey
+    (
+    uint16_t         txtLen,
+    const void       *txtRecord,
+    const char       *key
+    );
+
+
+/* TXTRecordGetValuePtr()
+ *
+ * Allows you to retrieve the value for a given key from a TXT Record.
+ *
+ * txtLen:          The size of the received TXT Record
+ *
+ * txtRecord:       Pointer to the received TXT Record bytes.
+ *
+ * key:             A null-terminated ASCII string containing the key name.
+ *
+ * valueLen:        On output, will be set to the size of the "value" data.
+ *
+ * return value:    Returns NULL if the key does not exist in this TXT record,
+ *                  or exists with no value (to differentiate between
+ *                  these two cases use TXTRecordContainsKey()).
+ *                  Returns pointer to location within TXT Record bytes
+ *                  if the key exists with empty or non-empty value.
+ *                  For empty value, valueLen will be zero.
+ *                  For non-empty value, valueLen will be length of value data.
+ */
+
+const void * DNSSD_API TXTRecordGetValuePtr
+    (
+    uint16_t         txtLen,
+    const void       *txtRecord,
+    const char       *key,
+    uint8_t          *valueLen
+    );
+
+
+/* TXTRecordGetCount()
+ *
+ * Returns the number of keys stored in the TXT Record. The count
+ * can be used with TXTRecordGetItemAtIndex() to iterate through the keys.
+ *
+ * txtLen:          The size of the received TXT Record.
+ *
+ * txtRecord:       Pointer to the received TXT Record bytes.
+ *
+ * return value:    Returns the total number of keys in the TXT Record.
+ *
+ */
+
+uint16_t DNSSD_API TXTRecordGetCount
+    (
+    uint16_t         txtLen,
+    const void       *txtRecord
+    );
+
+
+/* TXTRecordGetItemAtIndex()
+ *
+ * Allows you to retrieve a key name and value pointer, given an index into
+ * a TXT Record. Legal index values range from zero to TXTRecordGetCount()-1.
+ * It's also possible to iterate through keys in a TXT record by simply
+ * calling TXTRecordGetItemAtIndex() repeatedly, beginning with index zero
+ * and increasing until TXTRecordGetItemAtIndex() returns kDNSServiceErr_Invalid.
+ *
+ * On return:
+ * For keys with no value, *value is set to NULL and *valueLen is zero.
+ * For keys with empty value, *value is non-NULL and *valueLen is zero.
+ * For keys with non-empty value, *value is non-NULL and *valueLen is non-zero.
+ *
+ * txtLen:          The size of the received TXT Record.
+ *
+ * txtRecord:       Pointer to the received TXT Record bytes.
+ *
+ * itemIndex:       An index into the TXT Record.
+ *
+ * keyBufLen:       The size of the string buffer being supplied.
+ *
+ * key:             A string buffer used to store the key name.
+ *                  On return, the buffer contains a null-terminated C string
+ *                  giving the key name. DNS-SD TXT keys are usually
+ *                  9 characters or fewer. To hold the maximum possible
+ *                  key name, the buffer should be 256 bytes long.
+ *
+ * valueLen:        On output, will be set to the size of the "value" data.
+ *
+ * value:           On output, *value is set to point to location within TXT
+ *                  Record bytes that holds the value data.
+ *
+ * return value:    Returns kDNSServiceErr_NoError on success.
+ *                  Returns kDNSServiceErr_NoMemory if keyBufLen is too short.
+ *                  Returns kDNSServiceErr_Invalid if index is greater than
+ *                  TXTRecordGetCount()-1.
+ */
+
+DNSServiceErrorType DNSSD_API TXTRecordGetItemAtIndex
+    (
+    uint16_t         txtLen,
+    const void       *txtRecord,
+    uint16_t         itemIndex,
+    uint16_t         keyBufLen,
+    char             *key,
+    uint8_t          *valueLen,
+    const void       **value
+    );
+
+#if _DNS_SD_LIBDISPATCH
+/*
+* DNSServiceSetDispatchQueue
+*
+* Allows you to schedule a DNSServiceRef on a serial dispatch queue for receiving asynchronous
+* callbacks.  It's the clients responsibility to ensure that the provided dispatch queue is running.
+*
+* A typical application that uses CFRunLoopRun or dispatch_main on its main thread will
+* usually schedule DNSServiceRefs on its main queue (which is always a serial queue)
+* using "DNSServiceSetDispatchQueue(sdref, dispatch_get_main_queue());"
+*
+* If there is any error during the processing of events, the application callback will
+* be called with an error code. For shared connections, each subordinate DNSServiceRef
+* will get its own error callback. Currently these error callbacks only happen
+* if the mDNSResponder daemon is manually terminated or crashes, and the error
+* code in this case is kDNSServiceErr_ServiceNotRunning. The application must call
+* DNSServiceRefDeallocate to free the DNSServiceRef when it gets such an error code.
+* These error callbacks are rare and should not normally happen on customer machines,
+* but application code should be written defensively to handle such error callbacks
+* gracefully if they occur.
+*
+* After using DNSServiceSetDispatchQueue on a DNSServiceRef, calling DNSServiceProcessResult
+* on the same DNSServiceRef will result in undefined behavior and should be avoided.
+*
+* Once the application successfully schedules a DNSServiceRef on a serial dispatch queue using
+* DNSServiceSetDispatchQueue, it cannot remove the DNSServiceRef from the dispatch queue, or use
+* DNSServiceSetDispatchQueue a second time to schedule the DNSServiceRef onto a different serial dispatch
+* queue. Once scheduled onto a dispatch queue a DNSServiceRef will deliver events to that queue until
+* the application no longer requires that operation and terminates it using DNSServiceRefDeallocate.
+*
+* service:         DNSServiceRef that was allocated and returned to the application, when the
+*                  application calls one of the DNSService API.
+*
+* queue:           dispatch queue where the application callback will be scheduled
+*
+* return value:    Returns kDNSServiceErr_NoError on success.
+*                  Returns kDNSServiceErr_NoMemory if it cannot create a dispatch source
+*                  Returns kDNSServiceErr_BadParam if the service param is invalid or the
+*                  queue param is invalid
+*/
+
+DNSServiceErrorType DNSSD_API DNSServiceSetDispatchQueue
+  (
+  DNSServiceRef service,
+  dispatch_queue_t queue
+  );
+#endif //_DNS_SD_LIBDISPATCH
+
+#ifdef __APPLE_API_PRIVATE
+
+#define kDNSServiceCompPrivateDNS   "PrivateDNS"
+#define kDNSServiceCompMulticastDNS "MulticastDNS"
+
+#endif //__APPLE_API_PRIVATE
+
+/* Some C compiler cleverness. We can make the compiler check certain things for us,
+ * and report errors at compile-time if anything is wrong. The usual way to do this would
+ * be to use a run-time "if" statement or the conventional run-time "assert" mechanism, but
+ * then you don't find out what's wrong until you run the software. This way, if the assertion
+ * condition is false, the array size is negative, and the complier complains immediately.
+ */
+
+struct CompileTimeAssertionChecks_DNS_SD
+    {
+    char assert0[(sizeof(union _TXTRecordRef_t) == 16) ? 1 : -1];
+    };
+
+#ifdef  __cplusplus
+    }
+#endif
+
+#endif  /* _DNS_SD_H */
diff --git a/mdnsresponder/mDNSShared/dnsextd.8 b/mdnsresponder/mDNSShared/dnsextd.8
new file mode 100644
index 0000000..796caab
--- /dev/null
+++ b/mdnsresponder/mDNSShared/dnsextd.8
@@ -0,0 +1,69 @@
+.\" -*- tab-width: 4 -*-
+.\" 
+.\" Copyright (c) 2004 Apple Computer, Inc. All Rights Reserved.
+.\" 
+.\" Licensed 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.
+.\"
+.Dd August 2004             \" Date
+.Dt dnsextd 8               \" Document Title
+.Os Darwin                  \" Operating System
+.\"
+.Sh NAME
+.Nm dnsextd
+.Nd BIND Extension Daemon   \" Name Description for whatis database
+.\" 
+.Sh SYNOPSIS
+.Nm
+.\"
+.Sh DESCRIPTION
+.Nm
+is a daemon invoked at boot time, running alongside BIND 9,
+to implement two EDNS0 extensions to the standard DNS protocol.
+.Pp
+.Nm
+allows clients to perform DNS Updates with an attached lease lifetime,
+so that if the client crashes or is disconnected from the network, its
+address records will be automatically deleted after the lease expires.
+.Pp
+.Nm
+allows clients to perform long-lived queries. Instead of rapidly polling
+the server to discover when information changes, long-lived queries
+enable a client to indicate its interest in some set of data, and then
+be notified asynchronously by the server whenever any of that data changes.
+.Pp
+.Nm
+has no user-specifiable command-line argument, and users should not run
+.Nm
+manually.
+.\"
+.Sh SEE ALSO
+.Xr mDNS 1
+.Xr mDNSResponder 8
+.Pp
+For information on Dynamic DNS Update, see RFC 2136
+"Dynamic Updates in the Domain Name System (DNS UPDATE)"
+.Pp
+For information on Dynamic DNS Update Leases, see
+.Pa http://files.dns-sd.org/draft-dns-update-leases.txt
+.Pp
+For information on Long-Lived Queries, see
+.Pa http://files.dns-sd.org/draft-dns-llq.txt
+.\"
+.Sh BUGS
+.Nm
+bugs are tracked in Apple Radar component "mDNSResponder".
+.\"
+.Sh HISTORY
+The
+.Nm
+daemon first appeared in Mac OS X 10.4 (Tiger).
diff --git a/mdnsresponder/mDNSShared/dnsextd.c b/mdnsresponder/mDNSShared/dnsextd.c
new file mode 100644
index 0000000..cba52d3
--- /dev/null
+++ b/mdnsresponder/mDNSShared/dnsextd.c
@@ -0,0 +1,3146 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+#if __APPLE__
+// In Mac OS X 10.5 and later trying to use the daemon function gives a “‘daemon’ is deprecated”
+// error, which prevents compilation because we build with "-Werror".
+// Since this is supposed to be portable cross-platform code, we don't care that daemon is
+// deprecated on Mac OS X 10.5, so we use this preprocessor trick to eliminate the error message.
+#define daemon yes_we_know_that_daemon_is_deprecated_in_os_x_10_5_thankyou
+#endif
+
+#include <signal.h>
+#include <pthread.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <stdio.h>
+#include <syslog.h>
+#include <string.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#include <time.h>
+#include <errno.h>
+
+#if __APPLE__
+#undef daemon
+extern int daemon(int, int);
+#endif
+
+// Solaris doesn't have daemon(), so we define it here
+#ifdef NOT_HAVE_DAEMON
+#include "../mDNSPosix/mDNSUNP.h"		// For daemon()
+#endif // NOT_HAVE_DAEMON
+
+#include "dnsextd.h"
+#include "../mDNSShared/uds_daemon.h"
+#include "../mDNSShared/dnssd_ipc.h"
+#include "../mDNSCore/uDNS.h"
+#include "../mDNSShared/DebugServices.h"
+
+// Compatibility workaround
+#ifndef AF_LOCAL
+#define AF_LOCAL AF_UNIX
+#endif
+
+//
+// Constants
+//
+mDNSexport const char ProgramName[] = "dnsextd";
+
+#define LOOPBACK					"127.0.0.1"
+#if !defined(LISTENQ)
+#	define LISTENQ					128					// tcp connection backlog
+#endif
+#define RECV_BUFLEN					9000                
+#define LEASETABLE_INIT_NBUCKETS	256					// initial hashtable size (doubles as table fills)
+#define EXPIRATION_INTERVAL			300					// check for expired records every 5 minutes
+#define SRV_TTL						7200				// TTL For _dns-update SRV records
+#define CONFIG_FILE					"/etc/dnsextd.conf"
+#define TCP_SOCKET_FLAGS   			kTCPSocketFlags_UseTLS
+
+// LLQ Lease bounds (seconds)
+#define LLQ_MIN_LEASE (15 * 60)
+#define LLQ_MAX_LEASE (120 * 60)
+#define LLQ_LEASE_FUDGE 60
+
+// LLQ SOA poll interval (microseconds)
+#define LLQ_MONITOR_ERR_INTERVAL (60 * 1000000)
+#define LLQ_MONITOR_INTERVAL 250000
+#ifdef SIGINFO
+#define INFO_SIGNAL SIGINFO
+#else
+#define INFO_SIGNAL SIGUSR1
+#endif
+
+#define SAME_INADDR(x,y) (*((mDNSu32 *)&x) == *((mDNSu32 *)&y))
+
+//
+// Data Structures
+// Structs/fields that must be locked for thread safety are explicitly commented
+//
+
+// args passed to UDP request handler thread as void*
+
+typedef struct
+	{
+    PktMsg pkt;
+    struct sockaddr_in cliaddr;
+    DaemonInfo *d;
+	int sd;
+	} UDPContext;
+
+// args passed to TCP request handler thread as void*
+typedef struct
+	{
+	PktMsg	pkt;
+    struct sockaddr_in cliaddr;
+    TCPSocket *sock;           // socket connected to client
+    DaemonInfo *d;
+	} TCPContext;
+
+// args passed to UpdateAnswerList thread as void*
+typedef struct
+	{
+    DaemonInfo *d;
+    AnswerListElem *a;
+	} UpdateAnswerListArgs;
+
+//
+// Global Variables
+//
+
+// booleans to determine runtime output
+// read-only after initialization (no mutex protection)
+static mDNSBool foreground = 0;
+static mDNSBool verbose = 0;
+
+// globals set via signal handler (accessed exclusively by main select loop and signal handler)
+static mDNSBool terminate = 0;
+static mDNSBool dumptable = 0;
+static mDNSBool hangup    = 0;
+
+// global for config file location
+static char *   cfgfile   = NULL;
+
+//
+// Logging Routines
+// Log messages are delivered to syslog unless -f option specified
+//
+
+// common message logging subroutine
+mDNSlocal void PrintLog(const char *buffer)
+	{
+	if (foreground)
+		{
+		fprintf(stderr,"%s\n", buffer);
+		fflush(stderr);
+		}
+	else				
+		{
+		openlog("dnsextd", LOG_CONS, LOG_DAEMON);
+		syslog(LOG_ERR, "%s", buffer);
+		closelog();
+		}
+	}
+
+// Verbose Logging (conditional on -v option)
+mDNSlocal void VLog(const char *format, ...)
+	{
+   	char buffer[512];
+	va_list ptr;
+
+	if (!verbose) return;
+	va_start(ptr,format);
+	buffer[mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr)] = 0;
+	va_end(ptr);
+ 	PrintLog(buffer);
+	}
+
+// Unconditional Logging
+mDNSlocal void Log(const char *format, ...)
+	{
+   	char buffer[512];
+	va_list ptr;
+
+	va_start(ptr,format);
+	buffer[mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr)] = 0;
+	va_end(ptr);
+ 	PrintLog(buffer);
+	}
+
+// Error Logging
+// prints message "dnsextd <function>: <operation> - <error message>"
+// must be compiled w/ -D_REENTRANT for thread-safe errno usage
+mDNSlocal void LogErr(const char *fn, const char *operation)
+	{
+	char buf[512], errbuf[256];
+	strerror_r(errno, errbuf, sizeof(errbuf));
+	snprintf(buf, sizeof(buf), "%s: %s - %s", fn, operation, errbuf);
+	PrintLog(buf);
+	}
+
+//
+// Networking Utility Routines
+//
+
+// Convert DNS Message Header from Network to Host byte order
+mDNSlocal void HdrNToH(PktMsg *pkt)
+	{
+	// Read the integer parts which are in IETF byte-order (MSB first, LSB second)
+	mDNSu8 *ptr = (mDNSu8 *)&pkt->msg.h.numQuestions;
+	pkt->msg.h.numQuestions   = (mDNSu16)((mDNSu16)ptr[0] <<  8 | ptr[1]);
+	pkt->msg.h.numAnswers     = (mDNSu16)((mDNSu16)ptr[2] <<  8 | ptr[3]);
+	pkt->msg.h.numAuthorities = (mDNSu16)((mDNSu16)ptr[4] <<  8 | ptr[5]);
+	pkt->msg.h.numAdditionals = (mDNSu16)((mDNSu16)ptr[6] <<  8 | ptr[7]);
+	}
+
+// Convert DNS Message Header from Host to Network byte order
+mDNSlocal void HdrHToN(PktMsg *pkt)
+	{
+	mDNSu16 numQuestions   = pkt->msg.h.numQuestions;
+	mDNSu16 numAnswers     = pkt->msg.h.numAnswers;
+	mDNSu16 numAuthorities = pkt->msg.h.numAuthorities;
+	mDNSu16 numAdditionals = pkt->msg.h.numAdditionals;
+	mDNSu8 *ptr = (mDNSu8 *)&pkt->msg.h.numQuestions;
+
+	// Put all the integer values in IETF byte-order (MSB first, LSB second)
+	*ptr++ = (mDNSu8)(numQuestions   >> 8);
+	*ptr++ = (mDNSu8)(numQuestions   &  0xFF);
+	*ptr++ = (mDNSu8)(numAnswers     >> 8);
+	*ptr++ = (mDNSu8)(numAnswers     &  0xFF);
+	*ptr++ = (mDNSu8)(numAuthorities >> 8);
+	*ptr++ = (mDNSu8)(numAuthorities &  0xFF);
+	*ptr++ = (mDNSu8)(numAdditionals >> 8);
+	*ptr++ = (mDNSu8)(numAdditionals &  0xFF);
+	}
+
+
+// Add socket to event loop
+
+mDNSlocal mStatus AddSourceToEventLoop( DaemonInfo * self, TCPSocket *sock, EventCallback callback, void *context )
+	{
+	EventSource	* newSource;
+	mStatus			err = mStatus_NoError;
+	
+	if ( self->eventSources.LinkOffset == 0 )
+		{
+		InitLinkedList( &self->eventSources, offsetof( EventSource, next));
+		}
+
+	newSource = ( EventSource*) malloc( sizeof *newSource );
+	if ( newSource == NULL )
+		{
+		err = mStatus_NoMemoryErr;
+		goto exit;
+		}
+
+	newSource->callback = callback;
+	newSource->context = context;
+	newSource->sock = sock;
+	newSource->fd = mDNSPlatformTCPGetFD( sock );
+
+	AddToTail( &self->eventSources, newSource );
+
+exit:
+
+	return err;
+	}
+
+
+// Remove socket from event loop
+
+mDNSlocal mStatus RemoveSourceFromEventLoop( DaemonInfo * self, TCPSocket *sock )
+	{
+	EventSource	*	source;
+	mStatus			err;
+	
+	for ( source = ( EventSource* ) self->eventSources.Head; source; source = source->next )
+		{
+		if ( source->sock == sock )
+			{
+			RemoveFromList( &self->eventSources, source );
+
+			free( source );
+			err = mStatus_NoError;
+			goto exit;
+			}
+		}
+
+	err = mStatus_NoSuchNameErr;
+
+exit:
+
+	return err;
+	}
+
+// create a socket connected to nameserver
+// caller terminates connection via close()
+mDNSlocal TCPSocket *ConnectToServer(DaemonInfo *d)
+	{
+	int ntries = 0, retry = 0;
+
+	while (1)
+		{
+		mDNSIPPort port = zeroIPPort;
+		int fd;
+
+		TCPSocket *sock = mDNSPlatformTCPSocket( NULL, 0, &port );
+		if ( !sock ) { LogErr("ConnectToServer", "socket");  return NULL; }
+		fd = mDNSPlatformTCPGetFD( sock );
+		if (!connect( fd, (struct sockaddr *)&d->ns_addr, sizeof(d->ns_addr))) return sock;
+		mDNSPlatformTCPCloseConnection( sock );
+		if (++ntries < 10)
+			{
+			LogErr("ConnectToServer", "connect");
+			Log("ConnectToServer - retrying connection");
+			if (!retry) retry = 500000 + random() % 500000;
+			usleep(retry);
+			retry *= 2;
+			}
+		else { Log("ConnectToServer - %d failed attempts.  Aborting.", ntries); return NULL; }
+		}
+	}
+
+// send an entire block of data over a connected socket
+mDNSlocal int MySend(TCPSocket *sock, const void *msg, int len)
+	{
+	int selectval, n, nsent = 0;
+	fd_set wset;
+	struct timeval timeout = { 3, 0 };  // until we remove all calls from main thread, keep timeout short
+
+	while (nsent < len)
+		{
+		int fd;
+
+		FD_ZERO(&wset);
+
+		fd = mDNSPlatformTCPGetFD( sock );
+
+		FD_SET( fd, &wset );
+		selectval = select( fd+1, NULL, &wset, NULL, &timeout);
+		if (selectval < 0) { LogErr("MySend", "select");  return -1; }
+		if (!selectval || !FD_ISSET(fd, &wset)) { Log("MySend - timeout"); return -1; }
+
+		n = mDNSPlatformWriteTCP( sock, ( char* ) msg + nsent, len - nsent);
+
+		if (n < 0) { LogErr("MySend", "send");  return -1; }
+		nsent += n;
+		}
+	return 0;
+	}
+
+// Transmit a DNS message, prefixed by its length, over TCP, blocking if necessary
+mDNSlocal int SendPacket(TCPSocket *sock, PktMsg *pkt)
+	{
+	// send the lenth, in network byte order
+	mDNSu16 len = htons((mDNSu16)pkt->len);
+	if (MySend(sock, &len, sizeof(len)) < 0) return -1;
+
+	// send the message
+	VLog("SendPacket Q:%d A:%d A:%d A:%d ",
+		ntohs(pkt->msg.h.numQuestions),
+		ntohs(pkt->msg.h.numAnswers),
+		ntohs(pkt->msg.h.numAuthorities),
+		ntohs(pkt->msg.h.numAdditionals));
+	return MySend(sock, &pkt->msg, pkt->len);
+	}
+
+// Receive len bytes, waiting until we have all of them.
+// Returns number of bytes read (which should always be the number asked for).
+static int my_recv(TCPSocket *sock, void *const buf, const int len, mDNSBool * closed)
+    {
+    // Don't use "MSG_WAITALL"; it returns "Invalid argument" on some Linux versions;
+    // use an explicit while() loop instead.
+    // Also, don't try to do '+=' arithmetic on the original "void *" pointer --
+    // arithmetic on "void *" pointers is compiler-dependent.
+
+	fd_set rset;
+	struct timeval timeout = { 3, 0 };  // until we remove all calls from main thread, keep timeout short	
+    int selectval, remaining = len;
+    char *ptr = (char *)buf;
+	ssize_t num_read;
+
+	while (remaining)
+    	{
+		int fd;
+
+		fd = mDNSPlatformTCPGetFD( sock );
+
+		FD_ZERO(&rset);
+		FD_SET(fd, &rset);
+		selectval = select(fd+1, &rset, NULL, NULL, &timeout);
+		if (selectval < 0) { LogErr("my_recv", "select");  return -1; }
+		if (!selectval || !FD_ISSET(fd, &rset))
+			{
+			Log("my_recv - timeout");
+			return -1;
+			}
+
+		num_read = mDNSPlatformReadTCP( sock, ptr, remaining, closed );
+
+    	if (((num_read == 0) && *closed) || (num_read < 0) || (num_read > remaining)) return -1;
+		if (num_read == 0) return 0;
+    	ptr       += num_read;
+    	remaining -= num_read;
+    	}
+    return(len);
+    }
+
+// Return a DNS Message read off of a TCP socket, or NULL on failure
+// If storage is non-null, result is placed in that buffer.  Otherwise,
+// returned value is allocated with Malloc, and contains sufficient extra
+// storage for a Lease OPT RR
+
+mDNSlocal PktMsg*
+RecvPacket
+	(
+	TCPSocket *	sock,
+	PktMsg		*	storage,
+	mDNSBool	*	closed
+	)
+	{
+	int				nread;
+	int 			allocsize;
+	mDNSu16			msglen = 0;
+	PktMsg		*	pkt = NULL;
+	unsigned int	srclen;
+	int				fd;
+	mStatus			err = 0;
+
+	fd = mDNSPlatformTCPGetFD( sock );
+	
+	nread = my_recv( sock, &msglen, sizeof( msglen ), closed );
+	
+	require_action_quiet( nread != -1, exit, err = mStatus_UnknownErr );
+	require_action_quiet( nread > 0, exit, err = mStatus_NoError );
+
+	msglen = ntohs( msglen );
+	require_action_quiet( nread == sizeof( msglen ), exit, err = mStatus_UnknownErr; Log( "Could not read length field of message") );
+
+	if ( storage )
+		{
+		require_action_quiet( msglen <= sizeof( storage->msg ), exit, err = mStatus_UnknownErr; Log( "RecvPacket: provided buffer too small." ) );
+		pkt = storage;
+		}
+	else
+		{
+		// buffer extra space to add an OPT RR
+
+		if ( msglen > sizeof(DNSMessage))
+			{
+			allocsize = sizeof(PktMsg) - sizeof(DNSMessage) + msglen;
+			}
+		else
+			{
+			allocsize = sizeof(PktMsg);
+			}
+
+		pkt = malloc(allocsize);
+		require_action_quiet( pkt, exit, err = mStatus_NoMemoryErr; LogErr( "RecvPacket", "malloc" ) );
+		mDNSPlatformMemZero( pkt, sizeof( *pkt ) );
+		}
+	
+	pkt->len = msglen;
+	srclen = sizeof(pkt->src);
+
+	if ( getpeername( fd, ( struct sockaddr* ) &pkt->src, &srclen ) || ( srclen != sizeof( pkt->src ) ) )
+		{
+		LogErr("RecvPacket", "getpeername");
+		mDNSPlatformMemZero(&pkt->src, sizeof(pkt->src));
+		}
+
+	nread = my_recv(sock, &pkt->msg, msglen, closed );
+	require_action_quiet( nread >= 0, exit, err = mStatus_UnknownErr ; LogErr( "RecvPacket", "recv" ) );
+	require_action_quiet( nread == msglen, exit, err = mStatus_UnknownErr ; Log( "Could not read entire message" ) );
+	require_action_quiet( pkt->len >= sizeof( DNSMessageHeader ), exit, err = mStatus_UnknownErr ; Log( "RecvPacket: Message too short (%d bytes)", pkt->len ) );
+
+exit:
+
+	if ( err && pkt )
+		{
+		if ( pkt != storage )
+			{
+			free(pkt);
+			}
+
+		pkt = NULL;
+		}
+
+	return pkt;
+	}
+
+
+mDNSlocal DNSZone*
+FindZone
+	(
+	DaemonInfo	*	self,
+	domainname	*	name
+	)
+	{
+	DNSZone * zone;
+
+	for ( zone = self->zones; zone; zone = zone->next )
+		{
+		if ( SameDomainName( &zone->name, name ) )
+			{
+				break;
+			}
+		}
+
+		return zone;
+	}
+
+
+mDNSlocal mDNSBool
+ZoneHandlesName
+	(
+	const domainname * zname,
+	const domainname * dname
+	)
+	{
+	mDNSu16	i = DomainNameLength( zname );
+	mDNSu16	j = DomainNameLength( dname );
+
+	if ( ( i == ( MAX_DOMAIN_NAME + 1 ) ) || ( j == ( MAX_DOMAIN_NAME + 1 ) ) || ( i > j )  || ( memcmp( zname->c, dname->c + ( j - i ), i ) != 0 ) )
+		{
+		return mDNSfalse;
+		}
+
+	return mDNStrue;
+	}
+
+
+mDNSlocal mDNSBool IsQuery( PktMsg * pkt )
+	{
+	return ( pkt->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask ) == (mDNSu8) ( kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery );
+	}
+
+
+mDNSlocal mDNSBool IsUpdate( PktMsg * pkt )
+	{
+	return ( pkt->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask ) == (mDNSu8) ( kDNSFlag0_OP_Update );
+	}
+
+
+mDNSlocal mDNSBool IsNotify(PktMsg *pkt)
+	{
+	return ( pkt->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask ) == ( mDNSu8) ( kDNSFlag0_OP_Notify );
+	}
+
+
+mDNSlocal mDNSBool IsLLQRequest(PktMsg *pkt)
+	{
+	const mDNSu8 *ptr = NULL, *end = (mDNSu8 *)&pkt->msg + pkt->len;
+	LargeCacheRecord lcr;
+	int i;
+	mDNSBool result = mDNSfalse;
+	
+	HdrNToH(pkt);
+	if ((mDNSu8)(pkt->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask) != (mDNSu8)(kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery)) goto end;
+
+	if (!pkt->msg.h.numAdditionals) goto end;
+	ptr = LocateAdditionals(&pkt->msg, end);
+	if (!ptr) goto end;
+
+	// find last Additional info.
+	for (i = 0; i < pkt->msg.h.numAdditionals; i++)
+		{
+		ptr = GetLargeResourceRecord(NULL, &pkt->msg, ptr, end, 0, kDNSRecordTypePacketAdd, &lcr);
+		if (!ptr) { Log("Unable to read additional record"); goto end; }
+		}
+
+	if ( lcr.r.resrec.rrtype == kDNSType_OPT && lcr.r.resrec.rdlength >= DNSOpt_LLQData_Space && lcr.r.resrec.rdata->u.opt[0].opt == kDNSOpt_LLQ )
+		{
+		result = mDNStrue;
+		}
+
+	end:
+	HdrHToN(pkt);
+	return result;
+	}
+
+// !!!KRS implement properly
+mDNSlocal mDNSBool IsLLQAck(PktMsg *pkt)
+	{
+	if ((pkt->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask ) == (mDNSu8) ( kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery ) &&
+		pkt->msg.h.numQuestions && !pkt->msg.h.numAnswers && !pkt->msg.h.numAuthorities) return mDNStrue;
+	return mDNSfalse;
+	}
+
+
+mDNSlocal mDNSBool
+IsPublicSRV
+	(
+	DaemonInfo	*	self,
+	DNSQuestion	*	q
+	)
+	{
+	DNameListElem	*	elem;
+	mDNSBool			ret		= mDNSfalse;
+	int					i		= ( int ) DomainNameLength( &q->qname ) - 1;
+
+	for ( elem = self->public_names; elem; elem = elem->next )
+		{
+		int	j = ( int ) DomainNameLength( &elem->name ) - 1;
+
+		if ( i > j )
+			{
+			for ( ; i >= 0; i--, j-- )
+				{
+				if ( q->qname.c[ i ] != elem->name.c[ j ] )
+					{
+					ret = mDNStrue;
+					goto exit;
+					}
+				}
+			}
+		}
+
+exit:
+
+	return ret;
+	}
+
+
+mDNSlocal void
+SetZone
+	(
+	DaemonInfo	* self,
+	PktMsg		* pkt
+	)
+	{
+	domainname			zname;
+	mDNSu8				QR_OP;
+	const mDNSu8	*	ptr = pkt->msg.data;
+	mDNSBool			exception = mDNSfalse;
+
+	// Initialize
+
+	pkt->zone			= NULL;
+	pkt->isZonePublic	= mDNStrue;
+	zname.c[0]			= '\0';
+
+	// Figure out what type of packet this is
+
+	QR_OP = ( mDNSu8 ) ( pkt->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask );
+
+	if ( IsQuery( pkt ) )
+		{
+		DNSQuestion question;
+
+		// It's a query
+
+		ptr = getQuestion( &pkt->msg, ptr, ( ( mDNSu8* ) &pkt->msg ) + pkt->len, NULL, &question );
+
+		AppendDomainName( &zname, &question.qname );
+
+		exception = ( ( question.qtype == kDNSType_SOA ) || ( question.qtype == kDNSType_NS ) || ( ( question.qtype == kDNSType_SRV ) && IsPublicSRV( self, &question ) ) );
+		}
+	else if ( IsUpdate( pkt ) )
+		{
+		DNSQuestion question;
+
+		// It's an update.  The format of the zone section is the same as the format for the question section
+		// according to RFC 2136, so we'll just treat this as a question so we can get at the zone.
+
+		ptr = getQuestion( &pkt->msg, ptr, ( ( mDNSu8* ) &pkt->msg ) + pkt->len, NULL, &question );
+
+		AppendDomainName( &zname, &question.qname );
+
+		exception = mDNSfalse;
+		}
+
+	if ( zname.c[0] != '\0' )
+		{
+		// Find the right zone
+
+		for ( pkt->zone = self->zones; pkt->zone; pkt->zone = pkt->zone->next )
+			{
+			if ( ZoneHandlesName( &pkt->zone->name, &zname ) )
+				{
+				VLog( "found correct zone %##s for query", pkt->zone->name.c );
+
+				pkt->isZonePublic = ( ( pkt->zone->type == kDNSZonePublic ) || exception );
+
+				VLog( "zone %##s is %s", pkt->zone->name.c, ( pkt->isZonePublic ) ? "public" : "private" );
+
+				break;
+				}
+			}
+		}
+	}
+
+
+mDNSlocal int
+UDPServerTransaction(const DaemonInfo *d, const PktMsg *request, PktMsg *reply, mDNSBool *trunc)
+	{
+	fd_set			rset;
+	struct timeval	timeout = { 3, 0 };  // until we remove all calls from main thread, keep timeout short
+	int				sd;
+	int				res;
+	mStatus			err = mStatus_NoError;
+
+	// Initialize
+
+	*trunc = mDNSfalse;
+
+	// Create a socket
+
+ 	sd = socket( AF_INET, SOCK_DGRAM, 0 );
+	require_action( sd >= 0, exit, err = mStatus_UnknownErr; LogErr( "UDPServerTransaction", "socket" ) );
+
+	// Send the packet to the nameserver
+
+	VLog("UDPServerTransaction Q:%d A:%d A:%d A:%d ",
+		ntohs(request->msg.h.numQuestions),
+		ntohs(request->msg.h.numAnswers),
+		ntohs(request->msg.h.numAuthorities),
+		ntohs(request->msg.h.numAdditionals));
+	res = sendto( sd, (char *)&request->msg, request->len, 0, ( struct sockaddr* ) &d->ns_addr, sizeof( d->ns_addr ) );
+	require_action( res == (int) request->len, exit, err = mStatus_UnknownErr; LogErr( "UDPServerTransaction", "sendto" ) );
+
+	// Wait for reply
+
+	FD_ZERO( &rset );
+	FD_SET( sd, &rset );
+	res = select( sd + 1, &rset, NULL, NULL, &timeout );
+	require_action( res >= 0, exit, err = mStatus_UnknownErr; LogErr( "UDPServerTransaction", "select" ) );
+	require_action( ( res > 0 ) && FD_ISSET( sd, &rset ), exit, err = mStatus_UnknownErr; Log( "UDPServerTransaction - timeout" ) );
+
+	// Receive reply
+
+	reply->len = recvfrom( sd, &reply->msg, sizeof(reply->msg), 0, NULL, NULL );
+	require_action( ( ( int ) reply->len ) >= 0, exit, err = mStatus_UnknownErr; LogErr( "UDPServerTransaction", "recvfrom" ) );
+	require_action( reply->len >= sizeof( DNSMessageHeader ), exit, err = mStatus_UnknownErr; Log( "UDPServerTransaction - Message too short (%d bytes)", reply->len ) );
+
+	// Check for truncation bit
+
+	if ( reply->msg.h.flags.b[0] & kDNSFlag0_TC )
+		{
+		*trunc = mDNStrue;
+		}
+
+exit:
+
+	if ( sd >= 0 )
+		{
+		close( sd );
+		}
+
+	return err;
+	}
+
+//
+// Dynamic Update Utility Routines
+//
+
+// check if a request and server response complete a successful dynamic update
+mDNSlocal mDNSBool SuccessfulUpdateTransaction(PktMsg *request, PktMsg *reply)
+	{
+	char buf[32];
+	char *vlogmsg = NULL;
+	
+	// check messages
+	if (!request || !reply) { vlogmsg = "NULL message"; goto failure; }
+	if (request->len < sizeof(DNSMessageHeader) || reply->len < sizeof(DNSMessageHeader)) { vlogmsg = "Malformatted message"; goto failure; }
+
+	// check request operation
+	if ((request->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask) != (request->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask))
+		{ vlogmsg = "Request opcode not an update"; goto failure; }
+
+	// check result
+	if ((reply->msg.h.flags.b[1] & kDNSFlag1_RC_Mask)) { vlogmsg = "Reply contains non-zero rcode";  goto failure; }
+	if ((reply->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask) != (kDNSFlag0_OP_Update | kDNSFlag0_QR_Response))
+		{ vlogmsg = "Reply opcode not an update response"; goto failure; }
+
+	VLog("Successful update from %s", inet_ntop(AF_INET, &request->src.sin_addr, buf, 32));
+	return mDNStrue;
+
+	failure:
+	VLog("Request %s: %s", inet_ntop(AF_INET, &request->src.sin_addr, buf, 32), vlogmsg);
+	return mDNSfalse;
+	}
+
+// Allocate an appropriately sized CacheRecord and copy data from original.
+// Name pointer in CacheRecord object is set to point to the name specified
+//
+mDNSlocal CacheRecord *CopyCacheRecord(const CacheRecord *orig, domainname *name)
+	{
+	CacheRecord *cr;
+	size_t size = sizeof(*cr);
+	if (orig->resrec.rdlength > InlineCacheRDSize) size += orig->resrec.rdlength - InlineCacheRDSize;
+	cr = malloc(size);
+	if (!cr) { LogErr("CopyCacheRecord", "malloc"); return NULL; }
+	memcpy(cr, orig, size);
+	cr->resrec.rdata = (RData*)&cr->smallrdatastorage;
+	cr->resrec.name = name;
+	
+	return cr;
+	}
+
+
+//
+// Lease Hashtable Utility Routines
+//
+
+// double hash table size
+// caller must lock table prior to invocation
+mDNSlocal void RehashTable(DaemonInfo *d)
+	{
+	RRTableElem *ptr, *tmp, **new;
+	int i, bucket, newnbuckets = d->nbuckets * 2;
+
+	VLog("Rehashing lease table (new size %d buckets)", newnbuckets);
+	new = malloc(sizeof(RRTableElem *) * newnbuckets);
+	if (!new) { LogErr("RehashTable", "malloc");  return; }
+	mDNSPlatformMemZero(new, newnbuckets * sizeof(RRTableElem *));
+
+	for (i = 0; i < d->nbuckets; i++)
+		{
+		ptr = d->table[i];
+		while (ptr)
+			{
+			bucket = ptr->rr.resrec.namehash % newnbuckets;
+			tmp = ptr;
+			ptr = ptr->next;
+			tmp->next = new[bucket];
+			new[bucket] = tmp;
+			}
+		}
+	d->nbuckets = newnbuckets;
+	free(d->table);
+	d->table = new;
+	}
+
+// print entire contents of hashtable, invoked via SIGINFO
+mDNSlocal void PrintLeaseTable(DaemonInfo *d)
+	{
+	int i;
+	RRTableElem *ptr;
+	char rrbuf[MaxMsg], addrbuf[16];
+	struct timeval now;
+	int hr, min, sec;
+
+	if (gettimeofday(&now, NULL)) { LogErr("PrintTable", "gettimeofday"); return; }
+	if (pthread_mutex_lock(&d->tablelock)) { LogErr("PrintTable", "pthread_mutex_lock"); return; }
+	
+	Log("Dumping Lease Table Contents (table contains %d resource records)", d->nelems);
+	for (i = 0; i < d->nbuckets; i++)
+		{
+		for (ptr = d->table[i]; ptr; ptr = ptr->next)
+			{
+			hr = ((ptr->expire - now.tv_sec) / 60) / 60;
+			min = ((ptr->expire - now.tv_sec) / 60) % 60;
+			sec = (ptr->expire - now.tv_sec) % 60;
+			Log("Update from %s, Expires in %d:%d:%d\n\t%s", inet_ntop(AF_INET, &ptr->cli.sin_addr, addrbuf, 16), hr, min, sec,
+				GetRRDisplayString_rdb(&ptr->rr.resrec, &ptr->rr.resrec.rdata->u, rrbuf));
+			}
+		}
+	pthread_mutex_unlock(&d->tablelock);
+	}
+
+//
+// Startup SRV Registration Routines 
+// Register _dns-update._udp/_tcp.<zone> SRV records indicating the port on which
+// the daemon accepts requests  
+//
+
+// delete all RRS of a given name/type
+mDNSlocal mDNSu8 *putRRSetDeletion(DNSMessage *msg, mDNSu8 *ptr, mDNSu8 *limit,  ResourceRecord *rr)
+	{
+	ptr = putDomainNameAsLabels(msg, ptr, limit, rr->name);
+	if (!ptr || ptr + 10 >= limit) return NULL;  // out of space
+	ptr[0] = (mDNSu8)(rr->rrtype  >> 8);
+	ptr[1] = (mDNSu8)(rr->rrtype  &  0xFF);
+	ptr[2] = (mDNSu8)((mDNSu16)kDNSQClass_ANY >> 8);
+	ptr[3] = (mDNSu8)((mDNSu16)kDNSQClass_ANY &  0xFF);
+	mDNSPlatformMemZero(ptr+4, sizeof(rr->rroriginalttl) + sizeof(rr->rdlength)); // zero ttl/rdata
+	msg->h.mDNS_numUpdates++;
+	return ptr + 10;
+	}
+
+mDNSlocal mDNSu8 *PutUpdateSRV(DaemonInfo *d, DNSZone * zone, PktMsg *pkt, mDNSu8 *ptr, char *regtype, mDNSIPPort port, mDNSBool registration)
+	{
+	AuthRecord rr;
+	char hostname[1024], buf[MaxMsg];
+	mDNSu8 *end = (mDNSu8 *)&pkt->msg + sizeof(DNSMessage);
+	
+	( void ) d;
+
+	mDNS_SetupResourceRecord(&rr, NULL, 0, kDNSType_SRV, SRV_TTL, kDNSRecordTypeUnique, AuthRecordAny, NULL, NULL);
+	rr.resrec.rrclass = kDNSClass_IN;
+	rr.resrec.rdata->u.srv.priority = 0;
+	rr.resrec.rdata->u.srv.weight   = 0;
+	rr.resrec.rdata->u.srv.port     = port;
+	if (gethostname(hostname, 1024) < 0 || !MakeDomainNameFromDNSNameString(&rr.resrec.rdata->u.srv.target, hostname))
+		rr.resrec.rdata->u.srv.target.c[0] = '\0';
+	
+	MakeDomainNameFromDNSNameString(&rr.namestorage, regtype);
+	AppendDomainName(&rr.namestorage, &zone->name);
+	VLog("%s  %s", registration ? "Registering SRV record" : "Deleting existing RRSet",
+		 GetRRDisplayString_rdb(&rr.resrec, &rr.resrec.rdata->u, buf));
+	if (registration) ptr = PutResourceRecord(&pkt->msg, ptr, &pkt->msg.h.mDNS_numUpdates, &rr.resrec);
+	else              ptr = putRRSetDeletion(&pkt->msg, ptr, end, &rr.resrec);
+	return ptr;
+	}
+
+
+// perform dynamic update.
+// specify deletion by passing false for the register parameter, otherwise register the records.
+mDNSlocal int UpdateSRV(DaemonInfo *d, mDNSBool registration)
+	{
+	TCPSocket *sock = NULL;
+	DNSZone * zone;
+	int err = mStatus_NoError;
+
+	sock = ConnectToServer( d );
+	require_action( sock, exit, err = mStatus_UnknownErr; Log( "UpdateSRV: ConnectToServer failed" ) );
+
+	for ( zone = d->zones; zone; zone = zone->next )
+		{
+		PktMsg pkt;
+		mDNSu8 *ptr = pkt.msg.data;
+		mDNSu8 *end = (mDNSu8 *)&pkt.msg + sizeof(DNSMessage);
+		PktMsg *reply = NULL;
+		mDNSBool closed;
+		mDNSBool ok;
+
+		// Initialize message
+		InitializeDNSMessage(&pkt.msg.h, zeroID, UpdateReqFlags);
+		pkt.src.sin_addr.s_addr = zerov4Addr.NotAnInteger; // address field set solely for verbose logging in subroutines
+		pkt.src.sin_family = AF_INET;
+
+		// format message body
+		ptr = putZone(&pkt.msg, ptr, end, &zone->name, mDNSOpaque16fromIntVal(kDNSClass_IN));
+		require_action( ptr, exit, err = mStatus_UnknownErr; Log("UpdateSRV: Error constructing lease expiration update" ) );
+	
+	   if ( zone->type == kDNSZonePrivate )
+            {
+            ptr = PutUpdateSRV(d, zone, &pkt, ptr, "_dns-update-tls._tcp.", d->private_port, registration);
+            require_action( ptr, exit, err = mStatus_UnknownErr; Log("UpdateSRV: Error constructing lease expiration update" ) );
+            ptr = PutUpdateSRV(d, zone, &pkt, ptr, "_dns-query-tls._tcp.", d->private_port, registration);
+            require_action( ptr, exit, err = mStatus_UnknownErr; Log("UpdateSRV: Error constructing lease expiration update" ) );
+            ptr = PutUpdateSRV(d, zone, &pkt, ptr, "_dns-llq-tls._tcp.", d->private_port, registration);
+            require_action( ptr, exit, err = mStatus_UnknownErr; Log("UpdateSRV: Error constructing lease expiration update" ) );
+            
+			if ( !registration )
+				{
+				ptr = PutUpdateSRV(d, zone, &pkt, ptr, "_dns-update._udp.", d->llq_port, registration);
+				require_action( ptr, exit, err = mStatus_UnknownErr; Log("UpdateSRV: Error constructing lease expiration update" ) );
+				ptr = PutUpdateSRV(d, zone, &pkt, ptr, "_dns-llq._udp.", d->llq_port, registration);
+				require_action( ptr, exit, err = mStatus_UnknownErr; Log("UpdateSRV: Error constructing lease expiration update" ) );
+				}
+			}
+        else
+            {
+			if ( !registration )
+				{
+				ptr = PutUpdateSRV(d, zone, &pkt, ptr, "_dns-update-tls.", d->private_port, registration);
+				require_action( ptr, exit, err = mStatus_UnknownErr; Log("UpdateSRV: Error constructing lease expiration update" ) );
+				ptr = PutUpdateSRV(d, zone, &pkt, ptr, "_dns-query-tls.", d->private_port, registration);
+				require_action( ptr, exit, err = mStatus_UnknownErr; Log("UpdateSRV: Error constructing lease expiration update" ) );
+				ptr = PutUpdateSRV(d, zone, &pkt, ptr, "_dns-llq-tls.", d->private_port, registration);
+				require_action( ptr, exit, err = mStatus_UnknownErr; Log("UpdateSRV: Error constructing lease expiration update" ) );
+				}
+
+			ptr = PutUpdateSRV(d, zone, &pkt, ptr, "_dns-update._udp.", d->llq_port, registration);
+            require_action( ptr, exit, err = mStatus_UnknownErr; Log("UpdateSRV: Error constructing lease expiration update" ) );
+            ptr = PutUpdateSRV(d, zone, &pkt, ptr, "_dns-llq._udp.", d->llq_port, registration);
+            require_action( ptr, exit, err = mStatus_UnknownErr; Log("UpdateSRV: Error constructing lease expiration update" ) );
+			}
+
+		HdrHToN(&pkt);
+
+		if ( zone->updateKeys )
+			{
+			DNSDigest_SignMessage( &pkt.msg, &ptr, zone->updateKeys, 0 );
+			require_action( ptr, exit, Log("UpdateSRV: Error constructing lease expiration update" ) );
+			}
+
+		pkt.len = ptr - (mDNSu8 *)&pkt.msg;
+	
+		// send message, receive reply
+
+		err = SendPacket( sock, &pkt );
+		require_action( !err, exit, Log( "UpdateSRV: SendPacket failed" ) );
+
+		reply = RecvPacket( sock, NULL, &closed );
+		require_action( reply, exit, err = mStatus_UnknownErr; Log( "UpdateSRV: RecvPacket returned NULL" ) );
+
+		ok = SuccessfulUpdateTransaction( &pkt, reply );
+
+		if ( !ok )
+			{
+			Log("SRV record registration failed with rcode %d", reply->msg.h.flags.b[1] & kDNSFlag1_RC_Mask);
+			}
+
+		free( reply );
+		}
+	
+exit:
+
+	if ( sock )
+		{
+		mDNSPlatformTCPCloseConnection( sock );
+		}
+
+	return err;
+	}
+
+// wrapper routines/macros
+#define ClearUpdateSRV(d) UpdateSRV(d, 0)
+
+// clear any existing records prior to registration
+mDNSlocal int SetUpdateSRV(DaemonInfo *d)
+	{
+	int err;
+
+	err = ClearUpdateSRV(d);         // clear any existing record
+	if (!err) err = UpdateSRV(d, 1);
+	return err;
+	}
+
+//
+// Argument Parsing and Configuration
+//
+
+mDNSlocal void PrintUsage(void)
+	{
+	fprintf(stderr, "Usage: dnsextd [-f <config file>] [-vhd] ...\n"
+			"Use \"dnsextd -h\" for help\n");
+	}
+
+mDNSlocal void PrintHelp(void)
+	{
+	fprintf(stderr, "\n\n");
+	PrintUsage();
+
+	fprintf(stderr,
+			"dnsextd is a daemon that implements DNS extensions supporting Dynamic DNS Update Leases\n"
+            "and Long Lived Queries, used in Wide-Area DNS Service Discovery, on behalf of name servers\n"
+			"that do not natively support these extensions.  (See dns-sd.org for more info on DNS Service\n"
+			"Discovery, Update Leases, and Long Lived Queries.)\n\n"
+
+            "dnsextd requires one argument,the zone, which is the domain for which Update Leases\n"
+            "and Long Lived Queries are to be administered.  dnsextd communicates directly with the\n"
+			"primary master server for this zone.\n\n"
+
+			"The options are as follows:\n\n"
+
+			"-f    Specify configuration file. The default is /etc/dnsextd.conf.\n\n"
+
+			"-d    Run daemon in foreground.\n\n"
+
+			"-h    Print help.\n\n"
+
+			"-v    Verbose output.\n\n"
+		);
+	}
+
+
+// Note: ProcessArgs called before process is daemonized, and therefore must open no descriptors
+// returns 0 (success) if program is to continue execution
+// output control arguments (-f, -v) do not affect this routine
+mDNSlocal int ProcessArgs(int argc, char *argv[], DaemonInfo *d)
+	{
+	DNSZone	*	zone;
+	int			opt;
+	int			err = 0;
+	
+	cfgfile = strdup( CONFIG_FILE );
+	require_action( cfgfile, arg_error, err = mStatus_NoMemoryErr );
+
+    // defaults, may be overriden by command option
+
+	// setup our sockaddr
+
+	mDNSPlatformMemZero( &d->addr, sizeof( d->addr ) );
+	d->addr.sin_addr.s_addr	= zerov4Addr.NotAnInteger;
+	d->addr.sin_port		= UnicastDNSPort.NotAnInteger;
+	d->addr.sin_family		= AF_INET;
+#ifndef NOT_HAVE_SA_LEN
+	d->addr.sin_len			= sizeof( d->addr );
+#endif
+
+	// setup nameserver's sockaddr
+
+	mDNSPlatformMemZero(&d->ns_addr, sizeof(d->ns_addr));
+	d->ns_addr.sin_family	= AF_INET;
+	inet_pton( AF_INET, LOOPBACK, &d->ns_addr.sin_addr );
+	d->ns_addr.sin_port		= NSIPCPort.NotAnInteger;
+#ifndef NOT_HAVE_SA_LEN
+	d->ns_addr.sin_len		= sizeof( d->ns_addr );
+#endif
+
+	// setup our ports
+
+	d->private_port = PrivateDNSPort;
+	d->llq_port     = DNSEXTPort;
+
+	while ((opt = getopt(argc, argv, "f:hdv")) != -1)
+		{
+		switch(opt)
+			{
+			case 'f': free( cfgfile ); cfgfile = strdup( optarg ); require_action( cfgfile, arg_error, err = mStatus_NoMemoryErr ); break;
+			case 'h': PrintHelp();    return -1;
+			case 'd': foreground = 1; break;		// Also used when launched via OS X's launchd mechanism
+			case 'v': verbose = 1;    break;
+			default:  goto arg_error;
+			}
+		}
+
+	err = ParseConfig( d, cfgfile );
+	require_noerr( err, arg_error );
+
+	// Make sure we've specified some zones
+
+	require_action( d->zones, arg_error, err = mStatus_UnknownErr );
+
+	// if we have a shared secret, use it for the entire zone
+
+	for ( zone = d->zones; zone; zone = zone->next )
+		{
+		if ( zone->updateKeys )
+			{
+			AssignDomainName( &zone->updateKeys->domain, &zone->name );
+			}
+		}
+
+	return 0;
+	
+arg_error:
+
+	PrintUsage();
+	return -1;
+	}
+
+
+//
+// Initialization Routines
+//
+
+// Allocate memory, initialize locks and bookkeeping variables
+mDNSlocal int InitLeaseTable(DaemonInfo *d)
+	{
+	if (pthread_mutex_init(&d->tablelock, NULL)) { LogErr("InitLeaseTable", "pthread_mutex_init"); return -1; }
+	d->nbuckets = LEASETABLE_INIT_NBUCKETS;
+	d->nelems = 0;
+	d->table = malloc(sizeof(RRTableElem *) * LEASETABLE_INIT_NBUCKETS);
+	if (!d->table) { LogErr("InitLeaseTable", "malloc"); return -1; }
+	mDNSPlatformMemZero(d->table, sizeof(RRTableElem *) * LEASETABLE_INIT_NBUCKETS);
+	return 0;
+	}
+
+
+mDNSlocal int
+SetupSockets
+	(
+	DaemonInfo * self
+	)
+	{
+	static const int kOn = 1;
+	int					sockpair[2];
+	mDNSBool			private = mDNSfalse;
+	struct sockaddr_in	daddr;
+	DNSZone			*	zone;
+	mStatus				err = 0;
+	
+	// set up sockets on which we all ns requests
+
+	self->tcpsd = socket( AF_INET, SOCK_STREAM, 0 );
+	require_action( dnssd_SocketValid(self->tcpsd), exit, err = mStatus_UnknownErr; LogErr( "SetupSockets", "socket" ) );
+
+#if defined(SO_REUSEADDR)
+	err = setsockopt(self->tcpsd, SOL_SOCKET, SO_REUSEADDR, &kOn, sizeof(kOn));
+	require_action( !err, exit, LogErr( "SetupSockets", "SO_REUSEADDR self->tcpsd" ) );
+#endif
+
+	err = bind( self->tcpsd, ( struct sockaddr* ) &self->addr, sizeof( self->addr ) );
+	require_action( !err, exit, LogErr( "SetupSockets", "bind self->tcpsd" ) );
+
+	err = listen( self->tcpsd, LISTENQ );
+	require_action( !err, exit, LogErr( "SetupSockets", "listen" ) );
+
+	self->udpsd = socket( AF_INET, SOCK_DGRAM, 0 );
+	require_action( dnssd_SocketValid(self->udpsd), exit, err = mStatus_UnknownErr; LogErr( "SetupSockets", "socket" ) );
+
+#if defined(SO_REUSEADDR)
+	err = setsockopt(self->udpsd, SOL_SOCKET, SO_REUSEADDR, &kOn, sizeof(kOn));
+	require_action( !err, exit, LogErr( "SetupSockets", "SO_REUSEADDR self->udpsd" ) );
+#endif
+
+	err = bind( self->udpsd, ( struct sockaddr* ) &self->addr, sizeof( self->addr ) );
+	require_action( !err, exit, LogErr( "SetupSockets", "bind self->udpsd" ) );
+
+	// set up sockets on which we receive llq requests
+
+	mDNSPlatformMemZero(&self->llq_addr, sizeof(self->llq_addr));
+	self->llq_addr.sin_family		= AF_INET;
+	self->llq_addr.sin_addr.s_addr	= zerov4Addr.NotAnInteger;
+	self->llq_addr.sin_port			= ( self->llq_port.NotAnInteger ) ? self->llq_port.NotAnInteger : DNSEXTPort.NotAnInteger;
+
+	if (self->llq_addr.sin_port == self->addr.sin_port)
+		{
+		self->llq_tcpsd = self->tcpsd;
+		self->llq_udpsd = self->udpsd;
+		}
+	else
+		{
+		self->llq_tcpsd = socket( AF_INET, SOCK_STREAM, 0 );
+		require_action( dnssd_SocketValid(self->llq_tcpsd), exit, err = mStatus_UnknownErr; LogErr( "SetupSockets", "socket" ) );
+	
+#if defined(SO_REUSEADDR)
+		err = setsockopt(self->llq_tcpsd, SOL_SOCKET, SO_REUSEADDR, &kOn, sizeof(kOn));
+		require_action( !err, exit, LogErr( "SetupSockets", "SO_REUSEADDR self->llq_tcpsd" ) );
+#endif
+	
+		err = bind( self->llq_tcpsd, ( struct sockaddr* ) &self->llq_addr, sizeof( self->llq_addr ) );
+		require_action( !err, exit, LogErr( "SetupSockets", "bind self->llq_tcpsd" ) );
+	
+		err = listen( self->llq_tcpsd, LISTENQ );
+		require_action( !err, exit, LogErr( "SetupSockets", "listen" ) );
+	
+		self->llq_udpsd = socket( AF_INET, SOCK_DGRAM, 0 );
+		require_action( dnssd_SocketValid(self->llq_udpsd), exit, err = mStatus_UnknownErr; LogErr( "SetupSockets", "socket" ) );
+	
+#if defined(SO_REUSEADDR)
+		err = setsockopt(self->llq_udpsd, SOL_SOCKET, SO_REUSEADDR, &kOn, sizeof(kOn));
+		require_action( !err, exit, LogErr( "SetupSockets", "SO_REUSEADDR self->llq_udpsd" ) );
+#endif
+	
+		err = bind(self->llq_udpsd, ( struct sockaddr* ) &self->llq_addr, sizeof( self->llq_addr ) );
+		require_action( !err, exit, LogErr( "SetupSockets", "bind self->llq_udpsd" ) );
+		}
+
+	// set up Unix domain socket pair for LLQ polling thread to signal main thread that a change to the zone occurred
+
+	err = socketpair( AF_LOCAL, SOCK_STREAM, 0, sockpair );
+	require_action( !err, exit, LogErr( "SetupSockets", "socketpair" ) );
+
+	self->LLQEventListenSock = sockpair[0];
+	self->LLQEventNotifySock = sockpair[1];
+
+	// set up socket on which we receive private requests
+
+	self->llq_tcpsd = socket( AF_INET, SOCK_STREAM, 0 );
+	require_action( dnssd_SocketValid(self->tlssd), exit, err = mStatus_UnknownErr; LogErr( "SetupSockets", "socket" ) );
+	mDNSPlatformMemZero(&daddr, sizeof(daddr));
+	daddr.sin_family		= AF_INET;
+	daddr.sin_addr.s_addr	= zerov4Addr.NotAnInteger;
+	daddr.sin_port			= ( self->private_port.NotAnInteger ) ? self->private_port.NotAnInteger : PrivateDNSPort.NotAnInteger;
+
+	self->tlssd = socket( AF_INET, SOCK_STREAM, 0 );
+	require_action( dnssd_SocketValid(self->tlssd), exit, err = mStatus_UnknownErr; LogErr( "SetupSockets", "socket" ) );
+
+#if defined(SO_REUSEADDR)
+	err = setsockopt(self->tlssd, SOL_SOCKET, SO_REUSEADDR, &kOn, sizeof(kOn));
+	require_action( !err, exit, LogErr( "SetupSockets", "SO_REUSEADDR self->tlssd" ) );
+#endif
+
+	err = bind( self->tlssd, ( struct sockaddr* ) &daddr, sizeof( daddr ) );
+	require_action( !err, exit, LogErr( "SetupSockets", "bind self->tlssd" ) );
+
+	err = listen( self->tlssd, LISTENQ );
+	require_action( !err, exit, LogErr( "SetupSockets", "listen" ) );
+
+	// Do we have any private zones?
+
+	for ( zone = self->zones; zone; zone = zone->next )
+		{
+		if ( zone->type == kDNSZonePrivate )
+			{
+			private = mDNStrue;
+			break;
+			}
+		}
+
+	if ( private )
+		{
+		err = mDNSPlatformTLSSetupCerts();
+		require_action( !err, exit, LogErr( "SetupSockets", "mDNSPlatformTLSSetupCerts" ) );
+		}
+
+exit:
+
+	return err;
+	}
+
+//
+// periodic table updates
+//
+
+// Delete a resource record from the nameserver via a dynamic update
+// sd is a socket already connected to the server
+mDNSlocal void DeleteOneRecord(DaemonInfo *d, CacheRecord *rr, domainname *zname, TCPSocket *sock)
+	{
+	DNSZone	*	zone;
+	PktMsg pkt;
+	mDNSu8 *ptr = pkt.msg.data;
+	mDNSu8 *end = (mDNSu8 *)&pkt.msg + sizeof(DNSMessage);
+	char buf[MaxMsg];
+	mDNSBool closed;
+	PktMsg *reply = NULL;
+
+	VLog("Expiring record %s", GetRRDisplayString_rdb(&rr->resrec, &rr->resrec.rdata->u, buf));
+	
+	InitializeDNSMessage(&pkt.msg.h, zeroID, UpdateReqFlags);
+	
+	ptr = putZone(&pkt.msg, ptr, end, zname, mDNSOpaque16fromIntVal(rr->resrec.rrclass));
+	if (!ptr) goto end;
+	ptr = putDeletionRecord(&pkt.msg, ptr, &rr->resrec);
+	if (!ptr) goto end;
+
+	HdrHToN(&pkt);
+
+	zone = FindZone( d, zname );
+
+	if ( zone && zone->updateKeys)
+		{
+		DNSDigest_SignMessage(&pkt.msg, &ptr, zone->updateKeys, 0 );
+		if (!ptr) goto end;
+		}
+
+	pkt.len = ptr - (mDNSu8 *)&pkt.msg;
+	pkt.src.sin_addr.s_addr = zerov4Addr.NotAnInteger; // address field set solely for verbose logging in subroutines
+	pkt.src.sin_family = AF_INET;
+	if (SendPacket( sock, &pkt)) { Log("DeleteOneRecord: SendPacket failed"); }
+	reply = RecvPacket( sock, NULL, &closed );
+	if (reply) HdrNToH(reply);
+	require_action( reply, end, Log( "DeleteOneRecord: RecvPacket returned NULL" ) );
+
+	if (!SuccessfulUpdateTransaction(&pkt, reply))
+		Log("Expiration update failed with rcode %d", reply ? reply->msg.h.flags.b[1] & kDNSFlag1_RC_Mask : -1);
+					  
+	end:
+	if (!ptr) { Log("DeleteOneRecord: Error constructing lease expiration update"); }
+	if (reply) free(reply);
+	}
+
+// iterate over table, deleting expired records (or all records if DeleteAll is true)
+mDNSlocal void DeleteRecords(DaemonInfo *d, mDNSBool DeleteAll)
+	{
+	struct timeval now;
+	int i;
+	TCPSocket *sock = ConnectToServer(d);
+	if (!sock) { Log("DeleteRecords: ConnectToServer failed"); return; }
+	if (gettimeofday(&now, NULL)) { LogErr("DeleteRecords ", "gettimeofday"); return; }
+	if (pthread_mutex_lock(&d->tablelock)) { LogErr("DeleteRecords", "pthread_mutex_lock"); return; }
+
+	for (i = 0; i < d->nbuckets; i++)
+		{
+		RRTableElem **ptr = &d->table[i];
+		while (*ptr)
+			{
+			if (DeleteAll || (*ptr)->expire - now.tv_sec < 0)
+				{
+				RRTableElem *fptr;
+				// delete record from server
+				DeleteOneRecord(d, &(*ptr)->rr, &(*ptr)->zone, sock);
+				fptr = *ptr;
+				*ptr = (*ptr)->next;
+				free(fptr);
+				d->nelems--;
+				}
+			else ptr = &(*ptr)->next;
+			}
+		}
+	pthread_mutex_unlock(&d->tablelock);
+	mDNSPlatformTCPCloseConnection( sock );
+	}
+
+//
+// main update request handling
+//
+
+// Add, delete, or refresh records in table based on contents of a successfully completed dynamic update
+mDNSlocal void UpdateLeaseTable(PktMsg *pkt, DaemonInfo *d, mDNSs32 lease)
+	{
+	RRTableElem **rptr, *tmp;
+	int i, allocsize, bucket;
+	LargeCacheRecord lcr;
+	ResourceRecord *rr = &lcr.r.resrec;
+	const mDNSu8 *ptr, *end;
+	struct timeval tv;
+	DNSQuestion zone;
+	char buf[MaxMsg];
+	
+	if (pthread_mutex_lock(&d->tablelock)) { LogErr("UpdateLeaseTable", "pthread_mutex_lock"); return; }
+	HdrNToH(pkt);
+	ptr = pkt->msg.data;
+	end = (mDNSu8 *)&pkt->msg + pkt->len;
+	ptr = getQuestion(&pkt->msg, ptr, end, 0, &zone);
+	if (!ptr) { Log("UpdateLeaseTable: cannot read zone");  goto cleanup; }
+	ptr = LocateAuthorities(&pkt->msg, end);
+	if (!ptr) { Log("UpdateLeaseTable: Format error");  goto cleanup; }
+	
+	for (i = 0; i < pkt->msg.h.mDNS_numUpdates; i++)
+		{
+		mDNSBool DeleteAllRRSets = mDNSfalse, DeleteOneRRSet = mDNSfalse, DeleteOneRR = mDNSfalse;
+		
+		ptr = GetLargeResourceRecord(NULL, &pkt->msg, ptr, end, 0, kDNSRecordTypePacketAns, &lcr);
+		if (!ptr || lcr.r.resrec.RecordType == kDNSRecordTypePacketNegative) { Log("UpdateLeaseTable: GetLargeResourceRecord failed"); goto cleanup; }
+		bucket = rr->namehash % d->nbuckets;
+		rptr = &d->table[bucket];
+
+		// handle deletions		
+		if (rr->rrtype == kDNSQType_ANY && !rr->rroriginalttl && rr->rrclass == kDNSQClass_ANY && !rr->rdlength)
+			DeleteAllRRSets = mDNStrue; // delete all rrsets for a name
+		else if (!rr->rroriginalttl && rr->rrclass == kDNSQClass_ANY && !rr->rdlength)
+			DeleteOneRRSet = mDNStrue;
+		else if (!rr->rroriginalttl && rr->rrclass == kDNSClass_NONE)
+			DeleteOneRR = mDNStrue;
+
+		if (DeleteAllRRSets || DeleteOneRRSet || DeleteOneRR)
+			{
+			while (*rptr)
+			  {
+			  if (SameDomainName((*rptr)->rr.resrec.name, rr->name) &&
+				 (DeleteAllRRSets ||
+				 (DeleteOneRRSet && (*rptr)->rr.resrec.rrtype == rr->rrtype) ||
+				  (DeleteOneRR && IdenticalResourceRecord(&(*rptr)->rr.resrec, rr))))
+				  {
+				  tmp = *rptr;
+				  VLog("Received deletion update for %s", GetRRDisplayString_rdb(&tmp->rr.resrec, &tmp->rr.resrec.rdata->u, buf));
+				  *rptr = (*rptr)->next;
+				  free(tmp);
+				  d->nelems--;
+				  }
+			  else rptr = &(*rptr)->next;
+			  }
+			}
+		else if (lease > 0)
+			{
+			// see if add or refresh
+			while (*rptr && !IdenticalResourceRecord(&(*rptr)->rr.resrec, rr)) rptr = &(*rptr)->next;
+			if (*rptr)
+				{
+				// refresh
+				if (gettimeofday(&tv, NULL)) { LogErr("UpdateLeaseTable", "gettimeofday"); goto cleanup; }
+				(*rptr)->expire = tv.tv_sec + (unsigned)lease;
+				VLog("Refreshing lease for %s", GetRRDisplayString_rdb(&lcr.r.resrec, &lcr.r.resrec.rdata->u, buf));
+				}
+			else
+				{
+				// New record - add to table
+				if (d->nelems > d->nbuckets)
+					{
+					RehashTable(d);
+					bucket = rr->namehash % d->nbuckets;
+					rptr = &d->table[bucket];
+					}
+				if (gettimeofday(&tv, NULL)) { LogErr("UpdateLeaseTable", "gettimeofday"); goto cleanup; }
+				allocsize = sizeof(RRTableElem);
+				if (rr->rdlength > InlineCacheRDSize) allocsize += (rr->rdlength - InlineCacheRDSize);
+				tmp = malloc(allocsize);
+				if (!tmp) { LogErr("UpdateLeaseTable", "malloc"); goto cleanup; }
+				memcpy(&tmp->rr, &lcr.r, sizeof(CacheRecord) + rr->rdlength - InlineCacheRDSize);
+				tmp->rr.resrec.rdata = (RData *)&tmp->rr.smallrdatastorage;
+				AssignDomainName(&tmp->name, rr->name);
+				tmp->rr.resrec.name = &tmp->name;
+				tmp->expire = tv.tv_sec + (unsigned)lease;
+				tmp->cli.sin_addr = pkt->src.sin_addr;
+				AssignDomainName(&tmp->zone, &zone.qname);
+				tmp->next = d->table[bucket];
+				d->table[bucket] = tmp;
+				d->nelems++;
+				VLog("Adding update for %s to lease table", GetRRDisplayString_rdb(&lcr.r.resrec, &lcr.r.resrec.rdata->u, buf));
+				}
+			}
+		}
+					
+	cleanup:
+	pthread_mutex_unlock(&d->tablelock);
+	HdrHToN(pkt);
+	}
+
+// Given a successful reply from a server, create a new reply that contains lease information
+// Replies are currently not signed !!!KRS change this
+mDNSlocal PktMsg *FormatLeaseReply(DaemonInfo *d, PktMsg *orig, mDNSu32 lease)
+	{
+	PktMsg *reply;
+	mDNSu8 *ptr, *end;
+	mDNSOpaque16 flags;
+
+	(void)d;  //unused
+	reply = malloc(sizeof(*reply));
+	if (!reply) { LogErr("FormatLeaseReply", "malloc"); return NULL; }
+	flags.b[0] = kDNSFlag0_QR_Response | kDNSFlag0_OP_Update;
+	flags.b[1] = 0;
+ 
+	InitializeDNSMessage(&reply->msg.h, orig->msg.h.id, flags);
+	reply->src.sin_addr.s_addr = zerov4Addr.NotAnInteger;            // unused except for log messages
+	reply->src.sin_family = AF_INET;
+	ptr = reply->msg.data;
+	end = (mDNSu8 *)&reply->msg + sizeof(DNSMessage);
+	ptr = putUpdateLease(&reply->msg, ptr, lease);
+	if (!ptr) { Log("FormatLeaseReply: putUpdateLease failed"); free(reply); return NULL; }
+	reply->len = ptr - (mDNSu8 *)&reply->msg;
+	HdrHToN(reply);
+	return reply;
+	}
+
+
+// pkt is thread-local, not requiring locking
+
+mDNSlocal PktMsg*
+HandleRequest
+	(
+	DaemonInfo	*	self,
+	PktMsg		*	request
+	)
+	{
+	PktMsg		*	reply = NULL;
+	PktMsg		*	leaseReply;
+	PktMsg	 		buf;
+	char			addrbuf[32];
+	TCPSocket *	sock = NULL;
+	mStatus			err;
+	mDNSs32		lease = 0;
+	if ((request->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask) == kDNSFlag0_OP_Update)
+		{
+		int i, adds = 0, dels = 0;
+		const mDNSu8 *ptr, *end = (mDNSu8 *)&request->msg + request->len;
+		HdrNToH(request);
+		lease = GetPktLease(&mDNSStorage, &request->msg, end);
+		ptr = LocateAuthorities(&request->msg, end);
+		for (i = 0; i < request->msg.h.mDNS_numUpdates; i++)
+			{
+			LargeCacheRecord lcr;
+			ptr = GetLargeResourceRecord(NULL, &request->msg, ptr, end, 0, kDNSRecordTypePacketAns, &lcr);
+			if (lcr.r.resrec.RecordType != kDNSRecordTypePacketNegative && lcr.r.resrec.rroriginalttl) adds++; else dels++;
+			}
+		HdrHToN(request);
+		if (adds && !lease)
+			{
+			static const mDNSOpaque16 UpdateRefused = { { kDNSFlag0_QR_Response | kDNSFlag0_OP_Update, kDNSFlag1_RC_Refused } };
+			Log("Rejecting Update Request with %d additions but no lease", adds);
+			reply = malloc(sizeof(*reply));
+			mDNSPlatformMemZero(&reply->src, sizeof(reply->src));
+			reply->len = sizeof(DNSMessageHeader);
+			reply->zone = NULL;
+			reply->isZonePublic = 0;
+			InitializeDNSMessage(&reply->msg.h, request->msg.h.id, UpdateRefused);
+			return(reply);
+			}
+		if (lease > 7200)	// Don't allow lease greater than two hours; typically 90-minute renewal period
+			lease = 7200;
+		}
+	// Send msg to server, read reply
+
+	if ( request->len <= 512 )
+		{
+		mDNSBool trunc;
+
+		if ( UDPServerTransaction( self, request, &buf, &trunc) < 0 )
+			{
+			Log("HandleRequest - UDPServerTransaction failed.  Trying TCP");
+			}
+		else if ( trunc )
+			{
+			VLog("HandleRequest - answer truncated.  Using TCP");
+			}
+		else
+			{
+			reply = &buf; // success
+			}
+		}
+	
+	if ( !reply )
+		{
+		mDNSBool closed;
+		int res;
+
+		sock = ConnectToServer( self );
+		require_action_quiet( sock, exit, err = mStatus_UnknownErr ; Log( "Discarding request from %s due to connection errors", inet_ntop( AF_INET, &request->src.sin_addr, addrbuf, 32 ) ) );
+
+		res = SendPacket( sock, request );
+		require_action_quiet( res >= 0, exit, err = mStatus_UnknownErr ; Log( "Couldn't relay message from %s to server.  Discarding.", inet_ntop(AF_INET, &request->src.sin_addr, addrbuf, 32 ) ) );
+
+		reply = RecvPacket( sock, &buf, &closed );
+		}
+	
+	// IMPORTANT: reply is in network byte order at this point in the code
+	// We keep it this way because we send it back to the client in the same form
+	
+	// Is it an update?
+
+	if ( reply && ( ( reply->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask ) == ( kDNSFlag0_OP_Update | kDNSFlag0_QR_Response ) ) )
+		{
+		char 		pingmsg[4];
+		mDNSBool	ok = SuccessfulUpdateTransaction( request, reply );
+		require_action( ok, exit, err = mStatus_UnknownErr; VLog( "Message from %s not a successful update.", inet_ntop(AF_INET, &request->src.sin_addr, addrbuf, 32 ) ) );
+
+		UpdateLeaseTable( request, self, lease );
+
+		if ( lease > 0 )
+			{
+			leaseReply = FormatLeaseReply( self, reply, lease );
+
+			if ( !leaseReply )
+				{
+				Log("HandleRequest - unable to format lease reply");
+				}
+
+			// %%% Looks like a potential memory leak -- who frees the original reply?
+			reply = leaseReply;
+			}
+
+		// tell the main thread there was an update so it can send LLQs
+
+		if ( send( self->LLQEventNotifySock, pingmsg, sizeof( pingmsg ), 0 ) != sizeof( pingmsg ) )
+			{
+			LogErr("HandleRequest", "send");
+			}
+		}
+
+exit:
+
+	if ( sock )
+		{
+		mDNSPlatformTCPCloseConnection( sock );
+		}
+
+	if ( reply == &buf )
+		{
+		reply = malloc( sizeof( *reply ) );
+
+		if ( reply )
+			{
+			reply->len = buf.len;
+			memcpy(&reply->msg, &buf.msg, buf.len);
+			}
+		else
+			{
+			LogErr("HandleRequest", "malloc");
+			}
+		}
+
+	return reply;
+	}
+
+
+//
+// LLQ Support Routines
+//
+
+// Set fields of an LLQ OPT Resource Record
+mDNSlocal void FormatLLQOpt(AuthRecord *opt, int opcode, const mDNSOpaque64 *const id, mDNSs32 lease)
+	{
+	mDNSPlatformMemZero(opt, sizeof(*opt));
+	mDNS_SetupResourceRecord(opt, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, AuthRecordAny, mDNSNULL, mDNSNULL);
+	opt->resrec.rrclass = NormalMaxDNSMessageData;
+	opt->resrec.rdlength   = sizeof(rdataOPT);	// One option in this OPT record
+	opt->resrec.rdestimate = sizeof(rdataOPT);
+	opt->resrec.rdata->u.opt[0].opt = kDNSOpt_LLQ;
+	opt->resrec.rdata->u.opt[0].u.llq.vers  = kLLQ_Vers;
+	opt->resrec.rdata->u.opt[0].u.llq.llqOp = opcode;
+	opt->resrec.rdata->u.opt[0].u.llq.err   = LLQErr_NoError;
+	opt->resrec.rdata->u.opt[0].u.llq.id    = *id;
+	opt->resrec.rdata->u.opt[0].u.llq.llqlease = lease;
+	}
+
+// Calculate effective remaining lease of an LLQ
+mDNSlocal mDNSu32 LLQLease(LLQEntry *e)
+	{
+	struct timeval t;
+	
+	gettimeofday(&t, NULL);
+	if (e->expire < t.tv_sec) return 0;
+	else return e->expire - t.tv_sec;
+	}
+
+mDNSlocal void DeleteLLQ(DaemonInfo *d, LLQEntry *e)
+	{
+	int  bucket = DomainNameHashValue(&e->qname) % LLQ_TABLESIZE;
+	LLQEntry **ptr = &d->LLQTable[bucket];
+	AnswerListElem *a = e->AnswerList;
+	char addr[32];
+	
+	inet_ntop(AF_INET, &e->cli.sin_addr, addr, 32);
+	VLog("Deleting LLQ table entry for %##s client %s", e->qname.c, addr);
+
+	if (a && !(--a->refcount) && d->AnswerTableCount >= LLQ_TABLESIZE)
+		{
+		// currently, generating initial answers blocks the main thread, so we keep the answer list
+		// even if the ref count drops to zero.  To prevent unbounded table growth, we free shared answers
+		// if the ref count drops to zero AND there are more table elements than buckets
+		// !!!KRS update this when we make the table dynamically growable
+
+		CacheRecord *cr = a->KnownAnswers, *tmp;
+		AnswerListElem **tbl = &d->AnswerTable[bucket];
+
+		while (cr)
+			{
+			tmp = cr;
+			cr = cr->next;
+			free(tmp);
+			}
+
+		while (*tbl && *tbl != a) tbl = &(*tbl)->next;
+		if (*tbl) { *tbl = (*tbl)->next; free(a); d->AnswerTableCount--; }
+		else Log("Error: DeleteLLQ - AnswerList not found in table");
+		}
+
+	// remove LLQ from table, free memory
+	while(*ptr && *ptr != e) ptr = &(*ptr)->next;
+	if (!*ptr) { Log("Error: DeleteLLQ - LLQ not in table"); return; }
+	*ptr = (*ptr)->next;
+	free(e);
+	}
+
+mDNSlocal int SendLLQ(DaemonInfo *d, PktMsg *pkt, struct sockaddr_in dst, TCPSocket *sock)
+	{
+	char addr[32];
+	int err = -1;
+
+	HdrHToN(pkt);
+
+	if ( sock )
+		{
+		if ( SendPacket( sock, pkt ) != 0 )
+			{
+			LogErr("DaemonInfo", "MySend");
+			Log("Could not send response to client %s", inet_ntop(AF_INET, &dst.sin_addr, addr, 32));
+			}
+		}
+	else
+		{
+		if (sendto(d->llq_udpsd, &pkt->msg, pkt->len, 0, (struct sockaddr *)&dst, sizeof(dst)) != (int)pkt->len)
+			{
+			LogErr("DaemonInfo", "sendto");
+			Log("Could not send response to client %s", inet_ntop(AF_INET, &dst.sin_addr, addr, 32));
+			}
+		}
+
+	err = 0;
+	HdrNToH(pkt);
+	return err;
+	}
+
+mDNSlocal CacheRecord *AnswerQuestion(DaemonInfo *d, AnswerListElem *e)
+	{
+	PktMsg q;
+	int i;
+	TCPSocket *sock = NULL;
+	const mDNSu8 *ansptr;
+	mDNSu8 *end = q.msg.data;
+	PktMsg buf, *reply = NULL;
+	LargeCacheRecord lcr;
+	CacheRecord *AnswerList = NULL;
+	mDNSu8 rcode;
+	
+	VLog("Querying server for %##s type %d", e->name.c, e->type);
+	
+	InitializeDNSMessage(&q.msg.h, zeroID, uQueryFlags);
+	
+	end = putQuestion(&q.msg, end, end + AbsoluteMaxDNSMessageData, &e->name, e->type, kDNSClass_IN);
+	if (!end) { Log("Error: AnswerQuestion - putQuestion returned NULL"); goto end; }
+	q.len = (int)(end - (mDNSu8 *)&q.msg);
+
+	HdrHToN(&q);
+
+	if (!e->UseTCP)
+		{
+		mDNSBool trunc;
+
+		if (UDPServerTransaction(d, &q, &buf, &trunc) < 0)
+			Log("AnswerQuestion %##s - UDPServerTransaction failed.  Trying TCP", e->name.c);
+		else if (trunc)
+			{ VLog("AnswerQuestion %##s - answer truncated.  Using TCP", e->name.c); e->UseTCP = mDNStrue; }
+		else reply = &buf;  // success
+		}
+	
+	if (!reply)
+		{
+		mDNSBool closed;
+
+		sock = ConnectToServer(d);
+		if (!sock) { Log("AnswerQuestion: ConnectToServer failed"); goto end; }
+		if (SendPacket( sock, &q)) { Log("AnswerQuestion: SendPacket failed"); mDNSPlatformTCPCloseConnection( sock ); goto end; }
+		reply = RecvPacket( sock, NULL, &closed );
+		mDNSPlatformTCPCloseConnection( sock );
+		require_action( reply, end, Log( "AnswerQuestion: RecvPacket returned NULL" ) );
+		}
+
+	HdrNToH(&q);
+	if (reply) HdrNToH(reply);
+
+	if ((reply->msg.h.flags.b[0] & kDNSFlag0_QROP_Mask) != (kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery))
+		{ Log("AnswerQuestion: %##s type %d - Invalid response flags from server"); goto end; }
+	rcode = (mDNSu8)(reply->msg.h.flags.b[1] & kDNSFlag1_RC_Mask);
+	if (rcode && rcode != kDNSFlag1_RC_NXDomain) { Log("AnswerQuestion: %##s type %d - non-zero rcode %d from server", e->name.c, e->type, rcode); goto end; }
+
+	end = (mDNSu8 *)&reply->msg + reply->len;
+	ansptr = LocateAnswers(&reply->msg, end);
+	if (!ansptr) { Log("Error: AnswerQuestion - LocateAnswers returned NULL"); goto end; }
+
+	for (i = 0; i < reply->msg.h.numAnswers; i++)
+		{
+		ansptr = GetLargeResourceRecord(NULL, &reply->msg, ansptr, end, 0, kDNSRecordTypePacketAns, &lcr);
+		if (!ansptr) { Log("AnswerQuestions: GetLargeResourceRecord returned NULL"); goto end; }
+		if (lcr.r.resrec.RecordType != kDNSRecordTypePacketNegative)
+			{
+			if (lcr.r.resrec.rrtype != e->type || lcr.r.resrec.rrclass != kDNSClass_IN || !SameDomainName(lcr.r.resrec.name, &e->name))
+				{
+				Log("AnswerQuestion: response %##s type #d does not answer question %##s type #d.  Discarding",
+					  lcr.r.resrec.name->c, lcr.r.resrec.rrtype, e->name.c, e->type);
+				}
+			else
+				{
+				CacheRecord *cr = CopyCacheRecord(&lcr.r, &e->name);
+				if (!cr) { Log("Error: AnswerQuestion - CopyCacheRecord returned NULL"); goto end; }
+				cr->next = AnswerList;
+				AnswerList = cr;
+				}
+			}
+		}
+	
+	end:
+	if (reply && reply != &buf) free(reply);
+	return AnswerList;
+	}
+
+// Routine forks a thread to set EventList to contain Add/Remove events, and deletes any removes from the KnownAnswer list
+mDNSlocal void *UpdateAnswerList(void *args)
+	{
+	CacheRecord *cr, *NewAnswers, **na, **ka; // "new answer", "known answer"
+	DaemonInfo *d = ((UpdateAnswerListArgs *)args)->d;
+	AnswerListElem *a = ((UpdateAnswerListArgs *)args)->a;
+
+	free(args);
+	args = NULL;
+	
+	// get up to date answers
+	NewAnswers = AnswerQuestion(d, a);
+	
+	// first pass - mark all answers for deletion
+	for (ka = &a->KnownAnswers; *ka; ka = &(*ka)->next)
+		(*ka)->resrec.rroriginalttl = (unsigned)-1; // -1 means delete
+
+	// second pass - mark answers pre-existent
+	for (ka = &a->KnownAnswers; *ka; ka = &(*ka)->next)
+		{
+		for (na = &NewAnswers; *na; na = &(*na)->next)
+			{
+			if (IdenticalResourceRecord(&(*ka)->resrec, &(*na)->resrec))
+				{ (*ka)->resrec.rroriginalttl = 0; break; } // 0 means no change
+			}
+		}
+
+	// third pass - add new records to Event list
+	na = &NewAnswers;
+	while (*na)
+		{
+		for (ka = &a->KnownAnswers; *ka; ka = &(*ka)->next)
+			if (IdenticalResourceRecord(&(*ka)->resrec, &(*na)->resrec)) break;
+		if (!*ka)
+			{
+			// answer is not in list - splice from NewAnswers list, add to Event list
+			cr = *na;
+			*na = (*na)->next;        // splice from list
+			cr->next = a->EventList;  // add spliced record to event list
+			a->EventList = cr;
+			cr->resrec.rroriginalttl = 1; // 1 means add
+			}
+		else na = &(*na)->next;
+		}
+	
+	// move all the removes from the answer list to the event list	
+	ka = &a->KnownAnswers;
+	while (*ka)
+		{
+		if ((*ka)->resrec.rroriginalttl == (unsigned)-1)
+			{
+			cr = *ka;
+			*ka = (*ka)->next;
+			cr->next = a->EventList;
+			a->EventList = cr;
+			}
+		else ka = &(*ka)->next;
+		}
+	
+	// lastly, free the remaining records (known answers) in NewAnswers list
+	while (NewAnswers)
+		{
+		cr = NewAnswers;
+		NewAnswers = NewAnswers->next;
+		free(cr);
+		}
+	
+	return NULL;
+	}
+
+mDNSlocal void SendEvents(DaemonInfo *d, LLQEntry *e)
+	{
+	PktMsg  response;
+	CacheRecord *cr;
+	mDNSu8 *end = (mDNSu8 *)&response.msg.data;
+	mDNSOpaque16 msgID;
+	char rrbuf[MaxMsg], addrbuf[32];
+	AuthRecord opt;
+	
+	// Should this really be random?  Do we use the msgID on the receiving end?
+	msgID.NotAnInteger = random();
+	if (verbose) inet_ntop(AF_INET, &e->cli.sin_addr, addrbuf, 32);
+	InitializeDNSMessage(&response.msg.h, msgID, ResponseFlags);
+	end = putQuestion(&response.msg, end, end + AbsoluteMaxDNSMessageData, &e->qname, e->qtype, kDNSClass_IN);
+	if (!end) { Log("Error: SendEvents - putQuestion returned NULL"); return; }
+	
+	// put adds/removes in packet
+	for (cr = e->AnswerList->EventList; cr; cr = cr->next)
+		{
+		if (verbose) GetRRDisplayString_rdb(&cr->resrec, &cr->resrec.rdata->u, rrbuf);
+		VLog("%s (%s): %s", addrbuf, (mDNSs32)cr->resrec.rroriginalttl < 0 ? "Remove": "Add", rrbuf);
+		end = PutResourceRecordTTLJumbo(&response.msg, end, &response.msg.h.numAnswers, &cr->resrec, cr->resrec.rroriginalttl);
+		if (!end) { Log("Error: SendEvents - PutResourceRecordTTLJumbo returned NULL"); return; }
+		}
+			   
+	FormatLLQOpt(&opt, kLLQOp_Event, &e->id, LLQLease(e));
+	end = PutResourceRecordTTLJumbo(&response.msg, end, &response.msg.h.numAdditionals, &opt.resrec, 0);
+	if (!end) { Log("Error: SendEvents - PutResourceRecordTTLJumbo"); return; }
+
+	response.len = (int)(end - (mDNSu8 *)&response.msg);
+	if (SendLLQ(d, &response, e->cli, NULL ) < 0) LogMsg("Error: SendEvents - SendLLQ");
+	}
+
+mDNSlocal void PrintLLQAnswers(DaemonInfo *d)
+	{
+	int i;
+	char rrbuf[MaxMsg];
+	
+	Log("Printing LLQ Answer Table contents");
+
+	for (i = 0; i < LLQ_TABLESIZE; i++)
+		{
+		AnswerListElem *a = d->AnswerTable[i];
+		while(a)
+			{
+			int ancount = 0;
+			const CacheRecord *rr = a->KnownAnswers;
+			while (rr) { ancount++; rr = rr->next; }
+			Log("%p : Question %##s;  type %d;  referenced by %d LLQs; %d answers:", a, a->name.c, a->type, a->refcount, ancount);
+			for (rr = a->KnownAnswers; rr; rr = rr->next) Log("\t%s", GetRRDisplayString_rdb(&rr->resrec, &rr->resrec.rdata->u, rrbuf));
+			a = a->next;
+			}
+		}
+	}
+
+mDNSlocal void PrintLLQTable(DaemonInfo *d)
+	{
+	LLQEntry *e;
+	char addr[32];
+	int i;
+	
+	Log("Printing LLQ table contents");
+
+	for (i = 0; i < LLQ_TABLESIZE; i++)
+		{
+		e = d->LLQTable[i];
+		while(e)
+			{
+			char *state;
+			
+			switch (e->state)
+				{
+				case RequestReceived: state = "RequestReceived"; break;
+				case ChallengeSent:   state = "ChallengeSent";   break;
+				case Established:     state = "Established";     break;
+				default:              state = "unknown";
+				}
+			inet_ntop(AF_INET, &e->cli.sin_addr, addr, 32);
+			
+			Log("LLQ from %s in state %s; %##s; type %d; orig lease %d; remaining lease %d; AnswerList %p)",
+				addr, state, e->qname.c, e->qtype, e->lease, LLQLease(e), e->AnswerList);
+			e = e->next;
+			}
+		}
+	}
+
+// Send events to clients as a result of a change in the zone
+mDNSlocal void GenLLQEvents(DaemonInfo *d)
+	{
+	LLQEntry **e;
+	int i;
+	struct timeval t;
+	UpdateAnswerListArgs *args;
+	
+	VLog("Generating LLQ Events");
+
+	gettimeofday(&t, NULL);
+
+	// get all answers up to date
+	for (i = 0; i < LLQ_TABLESIZE; i++)
+		{
+		AnswerListElem *a = d->AnswerTable[i];
+		while(a)
+			{
+			args = malloc(sizeof(*args));
+			if (!args) { LogErr("GenLLQEvents", "malloc"); return; }
+			args->d = d;
+			args->a = a;
+			if (pthread_create(&a->tid, NULL, UpdateAnswerList, args) < 0) { LogErr("GenLLQEvents", "pthread_create"); return; }
+			usleep(1);
+			a = a->next;
+			}
+		}
+
+	for (i = 0; i < LLQ_TABLESIZE; i++)
+		{
+		AnswerListElem *a = d->AnswerTable[i];
+		while(a)
+			{
+			if (pthread_join(a->tid, NULL)) LogErr("GenLLQEvents", "pthread_join");
+			a = a->next;
+			}
+		}
+	
+    // for each established LLQ, send events
+	for (i = 0; i < LLQ_TABLESIZE; i++)
+		{
+		e = &d->LLQTable[i];
+		while(*e)
+			{
+			if ((*e)->expire < t.tv_sec) DeleteLLQ(d, *e);
+			else
+				{
+				if ((*e)->state == Established && (*e)->AnswerList->EventList) SendEvents(d, *e);
+				e = &(*e)->next;
+				}
+			}
+		}
+	
+	// now that all LLQs are updated, we move Add events from the Event list to the Known Answer list, and free Removes
+	for (i = 0; i < LLQ_TABLESIZE; i++)
+		{
+		AnswerListElem *a = d->AnswerTable[i];
+		while(a)
+			{
+			if (a->EventList)
+				{
+				CacheRecord *cr = a->EventList, *tmp;
+				while (cr)
+					{
+					tmp = cr;
+					cr = cr->next;
+					if ((signed)tmp->resrec.rroriginalttl < 0) free(tmp);
+					else
+						{
+						tmp->next = a->KnownAnswers;
+						a->KnownAnswers = tmp;
+						tmp->resrec.rroriginalttl = 0;
+						}
+					}
+				a->EventList = NULL;
+				}
+			a = a->next;
+			}
+		}
+	}
+
+mDNSlocal void SetAnswerList(DaemonInfo *d, LLQEntry *e)
+	{
+	int bucket = DomainNameHashValue(&e->qname) % LLQ_TABLESIZE;
+	AnswerListElem *a = d->AnswerTable[bucket];
+	while (a && (a->type != e->qtype ||!SameDomainName(&a->name, &e->qname))) a = a->next;
+	if (!a)
+		{
+		a = malloc(sizeof(*a));
+		if (!a) { LogErr("SetAnswerList", "malloc"); return; }
+		AssignDomainName(&a->name, &e->qname);
+		a->type = e->qtype;
+		a->refcount = 0;
+		a->EventList = NULL;
+		a->UseTCP = mDNSfalse;
+		a->next = d->AnswerTable[bucket];
+		d->AnswerTable[bucket] = a;
+		d->AnswerTableCount++;
+		a->KnownAnswers = AnswerQuestion(d, a);
+		}
+	
+	e->AnswerList = a;
+	a->refcount ++;
+	}
+	
+ // Allocate LLQ entry, insert into table
+mDNSlocal LLQEntry *NewLLQ(DaemonInfo *d, struct sockaddr_in cli, domainname *qname, mDNSu16 qtype, mDNSu32 lease )
+	{
+	char addr[32];
+	struct timeval t;
+	int bucket = DomainNameHashValue(qname) % LLQ_TABLESIZE;
+   	LLQEntry *e;
+
+	e = malloc(sizeof(*e));
+	if (!e) { LogErr("NewLLQ", "malloc"); return NULL; }
+
+	inet_ntop(AF_INET, &cli.sin_addr, addr, 32);
+	VLog("Allocating LLQ entry for client %s question %##s type %d", addr, qname->c, qtype);
+	
+	// initialize structure
+	e->cli = cli;
+	AssignDomainName(&e->qname, qname);
+	e->qtype = qtype;
+	e->id    = zeroOpaque64;
+	e->state = RequestReceived;
+	e->AnswerList = NULL;
+	
+	if (lease < LLQ_MIN_LEASE) lease = LLQ_MIN_LEASE;
+	else if (lease > LLQ_MAX_LEASE) lease = LLQ_MAX_LEASE;
+
+	gettimeofday(&t, NULL);
+	e->expire = t.tv_sec + (int)lease;
+	e->lease = lease;
+	
+	// add to table
+	e->next = d->LLQTable[bucket];
+	d->LLQTable[bucket] = e;
+	
+	return e;
+	}
+
+// Handle a refresh request from client
+mDNSlocal void LLQRefresh(DaemonInfo *d, LLQEntry *e, LLQOptData *llq, mDNSOpaque16 msgID, TCPSocket *sock )
+	{
+	AuthRecord opt;
+	PktMsg ack;
+	mDNSu8 *end = (mDNSu8 *)&ack.msg.data;
+	char addr[32];
+
+	inet_ntop(AF_INET, &e->cli.sin_addr, addr, 32);
+	VLog("%s LLQ for %##s from %s", llq->llqlease ? "Refreshing" : "Deleting", e->qname.c, addr);
+	
+	if (llq->llqlease)
+		{
+		struct timeval t;
+		if (llq->llqlease < LLQ_MIN_LEASE) llq->llqlease = LLQ_MIN_LEASE;
+		else if (llq->llqlease > LLQ_MAX_LEASE) llq->llqlease = LLQ_MIN_LEASE;
+		gettimeofday(&t, NULL);
+		e->expire = t.tv_sec + llq->llqlease;
+		}
+	
+	ack.src.sin_addr.s_addr = 0; // unused 
+	InitializeDNSMessage(&ack.msg.h, msgID, ResponseFlags);
+	end = putQuestion(&ack.msg, end, end + AbsoluteMaxDNSMessageData, &e->qname, e->qtype, kDNSClass_IN);
+	if (!end) { Log("Error: putQuestion"); return; }
+
+	FormatLLQOpt(&opt, kLLQOp_Refresh, &e->id, llq->llqlease ? LLQLease(e) : 0);
+	end = PutResourceRecordTTLJumbo(&ack.msg, end, &ack.msg.h.numAdditionals, &opt.resrec, 0);
+	if (!end) { Log("Error: PutResourceRecordTTLJumbo"); return; }
+
+	ack.len = (int)(end - (mDNSu8 *)&ack.msg);
+	if (SendLLQ(d, &ack, e->cli, sock)) Log("Error: LLQRefresh");
+
+	if (llq->llqlease) e->state = Established;
+	else DeleteLLQ(d, e);
+	}
+
+// Complete handshake with Ack an initial answers
+mDNSlocal void LLQCompleteHandshake(DaemonInfo *d, LLQEntry *e, LLQOptData *llq, mDNSOpaque16 msgID, TCPSocket *sock)
+	{
+	char addr[32];
+	CacheRecord *ptr;
+	AuthRecord opt;
+	PktMsg ack;
+	mDNSu8 *end = (mDNSu8 *)&ack.msg.data;
+	char rrbuf[MaxMsg], addrbuf[32];
+	
+	inet_ntop(AF_INET, &e->cli.sin_addr, addr, 32);
+
+	if (!mDNSSameOpaque64(&llq->id, &e->id) ||
+		llq->vers  != kLLQ_Vers             ||
+		llq->llqOp != kLLQOp_Setup          ||
+		llq->err   != LLQErr_NoError        ||
+		llq->llqlease > e->lease + LLQ_LEASE_FUDGE ||
+		llq->llqlease < e->lease - LLQ_LEASE_FUDGE)
+		{
+			Log("Incorrect challenge response from %s", addr);
+			return;
+		}
+
+	if (e->state == Established) VLog("Retransmitting LLQ ack + answers for %##s", e->qname.c);
+	else                         VLog("Delivering LLQ ack + answers for %##s", e->qname.c);
+	
+	// format ack + answers
+	ack.src.sin_addr.s_addr = 0; // unused 
+	InitializeDNSMessage(&ack.msg.h, msgID, ResponseFlags);
+	end = putQuestion(&ack.msg, end, end + AbsoluteMaxDNSMessageData, &e->qname, e->qtype, kDNSClass_IN);
+	if (!end) { Log("Error: putQuestion"); return; }
+	
+	if (e->state != Established) { SetAnswerList(d, e); e->state = Established; }
+	
+	if (verbose) inet_ntop(AF_INET, &e->cli.sin_addr, addrbuf, 32);
+	for (ptr = e->AnswerList->KnownAnswers; ptr; ptr = ptr->next)
+		{
+		if (verbose) GetRRDisplayString_rdb(&ptr->resrec, &ptr->resrec.rdata->u, rrbuf);
+		VLog("%s Intitial Answer - %s", addr, rrbuf);
+		end = PutResourceRecordTTLJumbo(&ack.msg, end, &ack.msg.h.numAnswers, &ptr->resrec, 1);
+		if (!end) { Log("Error: PutResourceRecordTTLJumbo"); return; }
+		}
+
+	FormatLLQOpt(&opt, kLLQOp_Setup, &e->id, LLQLease(e));
+	end = PutResourceRecordTTLJumbo(&ack.msg, end, &ack.msg.h.numAdditionals, &opt.resrec, 0);
+	if (!end) { Log("Error: PutResourceRecordTTLJumbo"); return; }
+
+	ack.len = (int)(end - (mDNSu8 *)&ack.msg);
+	if (SendLLQ(d, &ack, e->cli, sock)) Log("Error: LLQCompleteHandshake");
+	}
+
+mDNSlocal void LLQSetupChallenge(DaemonInfo *d, LLQEntry *e, LLQOptData *llq, mDNSOpaque16 msgID)
+	{
+	struct timeval t;
+	PktMsg challenge;
+	mDNSu8 *end = challenge.msg.data;
+	AuthRecord opt;
+
+	if (e->state == ChallengeSent) VLog("Retransmitting LLQ setup challenge for %##s", e->qname.c);
+	else                           VLog("Sending LLQ setup challenge for %##s", e->qname.c);
+	
+	if (!mDNSOpaque64IsZero(&llq->id)) { Log("Error: LLQSetupChallenge - nonzero ID"); return; } // server bug
+	if (llq->llqOp != kLLQOp_Setup) { Log("LLQSetupChallenge - incorrrect operation from client"); return; } // client error
+	
+	if (mDNSOpaque64IsZero(&e->id)) // don't regenerate random ID for retransmissions
+		{
+		// construct ID <time><random>
+		gettimeofday(&t, NULL);
+		e->id.l[0] = t.tv_sec;
+		e->id.l[1] = random();
+		}
+
+	// format response (query + LLQ opt rr)
+	challenge.src.sin_addr.s_addr = 0; // unused 
+	InitializeDNSMessage(&challenge.msg.h, msgID, ResponseFlags);
+	end = putQuestion(&challenge.msg, end, end + AbsoluteMaxDNSMessageData, &e->qname, e->qtype, kDNSClass_IN);
+	if (!end) { Log("Error: putQuestion"); return; }
+	FormatLLQOpt(&opt, kLLQOp_Setup, &e->id, LLQLease(e));
+	end = PutResourceRecordTTLJumbo(&challenge.msg, end, &challenge.msg.h.numAdditionals, &opt.resrec, 0);
+	if (!end) { Log("Error: PutResourceRecordTTLJumbo"); return; }
+	challenge.len = (int)(end - (mDNSu8 *)&challenge.msg);
+	if (SendLLQ(d, &challenge, e->cli, NULL)) { Log("Error: LLQSetupChallenge"); return; }
+	e->state = ChallengeSent;
+	}
+
+// Take action on an LLQ message from client.  Entry must be initialized and in table
+mDNSlocal void UpdateLLQ(DaemonInfo *d, LLQEntry *e, LLQOptData *llq, mDNSOpaque16 msgID, TCPSocket *sock )
+	{
+	switch(e->state)
+		{
+		case RequestReceived:
+			if ( sock )
+				{
+				struct timeval t;
+				gettimeofday(&t, NULL);
+				e->id.l[0] = t.tv_sec;	// construct ID <time><random>
+				e->id.l[1] = random();
+				llq->id = e->id;
+				LLQCompleteHandshake( d, e, llq, msgID, sock );
+
+				// Set the state to established because we've just set the LLQ up using TCP
+				e->state = Established;
+				}
+			else
+				{
+				LLQSetupChallenge(d, e, llq, msgID);
+				}
+			return;
+		case ChallengeSent:
+			if (mDNSOpaque64IsZero(&llq->id)) LLQSetupChallenge(d, e, llq, msgID); // challenge sent and lost
+			else LLQCompleteHandshake(d, e, llq, msgID, sock );
+			return;
+		case Established:
+			if (mDNSOpaque64IsZero(&llq->id))
+				{
+				// client started over.  reset state.
+				LLQEntry *newe = NewLLQ(d, e->cli, &e->qname, e->qtype, llq->llqlease );
+				if (!newe) return;
+				DeleteLLQ(d, e);
+				LLQSetupChallenge(d, newe, llq, msgID);
+				return;
+				}
+			else if (llq->llqOp == kLLQOp_Setup)
+				{ LLQCompleteHandshake(d, e, llq, msgID, sock); return; } // Ack lost				
+			else if (llq->llqOp == kLLQOp_Refresh)
+				{ LLQRefresh(d, e, llq, msgID, sock); return; }
+			else { Log("Unhandled message for established LLQ"); return; }
+		}
+	}
+
+mDNSlocal LLQEntry *LookupLLQ(DaemonInfo *d, struct sockaddr_in cli, domainname *qname, mDNSu16 qtype, const mDNSOpaque64 *const id)
+	{
+	int bucket = bucket = DomainNameHashValue(qname) % LLQ_TABLESIZE;
+	LLQEntry *ptr = d->LLQTable[bucket];
+
+	while(ptr)
+		{
+		if (((ptr->state == ChallengeSent && mDNSOpaque64IsZero(id) && (cli.sin_port == ptr->cli.sin_port)) || // zero-id due to packet loss OK in state ChallengeSent
+			 mDNSSameOpaque64(id, &ptr->id)) &&                        // id match
+			(cli.sin_addr.s_addr == ptr->cli.sin_addr.s_addr) && (qtype == ptr->qtype) && SameDomainName(&ptr->qname, qname)) // same source, type, qname
+			return ptr;
+		ptr = ptr->next;
+		}
+	return NULL;
+	}
+
+mDNSlocal int
+RecvNotify
+	(
+	DaemonInfo	*	d,
+	PktMsg		*	pkt
+	)
+	{
+	int	res;
+	int	err = 0;
+
+	pkt->msg.h.flags.b[0] |= kDNSFlag0_QR_Response;
+
+	res = sendto( d->udpsd, &pkt->msg, pkt->len, 0, ( struct sockaddr* ) &pkt->src, sizeof( pkt->src ) );
+	require_action( res == ( int ) pkt->len, exit, err = mStatus_UnknownErr; LogErr( "RecvNotify", "sendto" ) );
+
+exit:
+
+	return err;
+	}
+
+
+mDNSlocal int RecvLLQ( DaemonInfo *d, PktMsg *pkt, TCPSocket *sock )
+	{
+	DNSQuestion q;
+	LargeCacheRecord opt;
+	int i, err = -1;
+	char addr[32];
+	const mDNSu8 *qptr = pkt->msg.data;
+    const mDNSu8 *end = (mDNSu8 *)&pkt->msg + pkt->len;
+	const mDNSu8 *aptr;
+	LLQOptData *llq = NULL;
+	LLQEntry *e = NULL;
+	
+	HdrNToH(pkt);
+	aptr = LocateAdditionals(&pkt->msg, end);	// Can't do this until after HdrNToH(pkt);
+	inet_ntop(AF_INET, &pkt->src.sin_addr, addr, 32);
+
+	VLog("Received LLQ msg from %s", addr);
+	// sanity-check packet
+	if (!pkt->msg.h.numQuestions || !pkt->msg.h.numAdditionals)
+		{
+		Log("Malformatted LLQ from %s with %d questions, %d additionals", addr, pkt->msg.h.numQuestions, pkt->msg.h.numAdditionals);
+		goto end;
+		}
+
+	// Locate the OPT record.
+	// According to RFC 2671, "One OPT pseudo-RR can be added to the additional data section of either a request or a response."
+	// This implies that there may be *at most* one OPT record per DNS message, in the Additional Section,
+	// but not necessarily the *last* entry in the Additional Section.
+	for (i = 0; i < pkt->msg.h.numAdditionals; i++)
+		{
+		aptr = GetLargeResourceRecord(NULL, &pkt->msg, aptr, end, 0, kDNSRecordTypePacketAdd, &opt);
+		if (!aptr) { Log("Malformatted LLQ from %s: could not get Additional record %d", addr, i); goto end; }
+		if (opt.r.resrec.RecordType != kDNSRecordTypePacketNegative && opt.r.resrec.rrtype == kDNSType_OPT) break;
+		}
+
+	// validate OPT
+	if (opt.r.resrec.rrtype != kDNSType_OPT) { Log("Malformatted LLQ from %s: last Additional not an OPT RR", addr); goto end; }
+	if (opt.r.resrec.rdlength < pkt->msg.h.numQuestions * DNSOpt_LLQData_Space) { Log("Malformatted LLQ from %s: OPT RR to small (%d bytes for %d questions)", addr, opt.r.resrec.rdlength, pkt->msg.h.numQuestions); }
+	
+	// dispatch each question
+	for (i = 0; i < pkt->msg.h.numQuestions; i++)
+		{
+		qptr = getQuestion(&pkt->msg, qptr, end, 0, &q);
+		if (!qptr) { Log("Malformatted LLQ from %s: cannot read question %d", addr, i); goto end; }
+		llq = (LLQOptData *)&opt.r.resrec.rdata->u.opt[0].u.llq + i; // point into OptData at index i
+		if (llq->vers != kLLQ_Vers) { Log("LLQ from %s contains bad version %d (expected %d)", addr, llq->vers, kLLQ_Vers); goto end; }
+		
+		e = LookupLLQ(d, pkt->src, &q.qname, q.qtype, &llq->id);
+		if (!e)
+			{
+			// no entry - if zero ID, create new
+			e = NewLLQ(d, pkt->src, &q.qname, q.qtype, llq->llqlease );
+			if (!e) goto end;
+			}
+		UpdateLLQ(d, e, llq, pkt->msg.h.id, sock);
+		}
+	err = 0;
+	
+	end:
+	HdrHToN(pkt);
+	return err;
+	}
+
+
+mDNSlocal mDNSBool IsAuthorized( DaemonInfo * d, PktMsg * pkt, DomainAuthInfo ** key, mDNSu16 * rcode, mDNSu16 * tcode )
+	{
+	const mDNSu8 	*	lastPtr = NULL;
+	const mDNSu8 	*	ptr = NULL;
+	DomainAuthInfo	*	keys;
+	mDNSu8 			*	end	= ( mDNSu8* ) &pkt->msg + pkt->len;
+	LargeCacheRecord	lcr;
+	mDNSBool			hasTSIG = mDNSfalse;
+	mDNSBool			strip = mDNSfalse;
+	mDNSBool			ok = mDNSfalse;
+	int					i;
+
+	// Unused parameters
+
+	( void ) d;
+
+	HdrNToH(pkt);
+
+	*key = NULL;
+
+	if ( pkt->msg.h.numAdditionals )
+		{
+		ptr = LocateAdditionals(&pkt->msg, end);
+		if (ptr)
+			{
+			for (i = 0; i < pkt->msg.h.numAdditionals; i++)
+				{
+				lastPtr = ptr;
+				ptr = GetLargeResourceRecord(NULL, &pkt->msg, ptr, end, 0, kDNSRecordTypePacketAdd, &lcr);
+				if (!ptr)
+					{
+					Log("Unable to read additional record");
+					lastPtr = NULL;
+					break;
+					}
+				}
+
+				hasTSIG = ( ptr && lcr.r.resrec.RecordType != kDNSRecordTypePacketNegative && lcr.r.resrec.rrtype == kDNSType_TSIG );
+			}
+		else
+			{
+			LogMsg( "IsAuthorized: unable to find Additional section" );
+			}
+		}
+
+	// If we don't know what zone this is, then it's authorized.
+
+	if ( !pkt->zone )
+		{
+		ok = mDNStrue;
+		strip = mDNSfalse;
+		goto exit;
+		}
+
+	if ( IsQuery( pkt ) )
+		{
+		keys = pkt->zone->queryKeys;
+		strip = mDNStrue;
+		}
+	else if ( IsUpdate( pkt ) )
+		{
+		keys = pkt->zone->updateKeys;
+		strip = mDNSfalse;
+		}
+	else
+		{
+		ok = mDNStrue;
+		strip = mDNSfalse;
+		goto exit;
+		}
+		
+	if ( pkt->isZonePublic )
+		{
+		ok = mDNStrue;
+		goto exit;
+		}
+
+	// If there are no keys, then we're authorized
+
+	if ( ( hasTSIG && !keys ) || ( !hasTSIG && keys ) )
+		{
+		Log( "Invalid TSIG spec %##s for zone %##s", lcr.r.resrec.name->c, pkt->zone->name.c );
+		*rcode = kDNSFlag1_RC_NotAuth;
+		*tcode = TSIG_ErrBadKey;
+		strip = mDNStrue;
+		ok = mDNSfalse;
+		goto exit;
+		}
+
+	// Find the right key
+
+	for ( *key = keys; *key; *key = (*key)->next )
+		{
+		if ( SameDomainName( lcr.r.resrec.name, &(*key)->keyname ) )
+			{
+			break;
+			}
+		}
+
+	if ( !(*key) )
+		{
+		Log( "Invalid TSIG name %##s for zone %##s", lcr.r.resrec.name->c, pkt->zone->name.c );
+		*rcode = kDNSFlag1_RC_NotAuth;
+		*tcode = TSIG_ErrBadKey;
+		strip = mDNStrue;
+		ok = mDNSfalse;
+		goto exit;
+		}
+
+	// Okay, we have the correct key and a TSIG record.  DNSDigest_VerifyMessage does the heavy
+	// lifting of message verification
+
+	pkt->msg.h.numAdditionals--;
+
+	HdrHToN( pkt );
+
+	ok = DNSDigest_VerifyMessage( &pkt->msg, ( mDNSu8* ) lastPtr, &lcr, (*key), rcode, tcode );
+
+	HdrNToH( pkt );
+
+	pkt->msg.h.numAdditionals++;
+
+exit:
+
+	if ( hasTSIG && strip )
+		{
+		// Strip the TSIG from the message
+
+		pkt->msg.h.numAdditionals--;
+		pkt->len = lastPtr - ( mDNSu8* ) ( &pkt->msg );
+		}
+
+	HdrHToN(pkt);
+
+	return ok;
+	}
+
+// request handler wrappers for TCP and UDP requests
+// (read message off socket, fork thread that invokes main processing routine and handles cleanup)
+
+mDNSlocal void*
+UDPMessageHandler
+	(
+	void * vptr
+	)
+	{
+	UDPContext	*	context	= ( UDPContext* ) vptr;
+	PktMsg		*	reply	= NULL;
+	int				res;
+	mStatus			err;
+
+	// !!!KRS strictly speaking, we shouldn't use TCP for a UDP request because the server
+	// may give us a long answer that would require truncation for UDP delivery to client
+
+	reply = HandleRequest( context->d, &context->pkt );
+	require_action( reply, exit, err = mStatus_UnknownErr );
+
+	res = sendto( context->sd, &reply->msg, reply->len, 0, ( struct sockaddr* ) &context->pkt.src, sizeof( context->pkt.src ) );
+	require_action_quiet( res == ( int ) reply->len, exit, LogErr( "UDPMessageHandler", "sendto" ) );
+
+exit:
+
+	if ( reply )
+		{
+		free( reply );
+		}
+
+	free( context );
+
+	pthread_exit( NULL );
+
+	return NULL;
+	}
+
+
+mDNSlocal int
+RecvUDPMessage
+	(
+	DaemonInfo	*	self,
+	int				sd
+	)
+	{
+	UDPContext		*	context = NULL;
+	pthread_t			tid;
+	mDNSu16				rcode;
+	mDNSu16				tcode;
+	DomainAuthInfo	*	key;
+	unsigned int		clisize = sizeof( context->cliaddr );
+	int					res;
+	mStatus				err = mStatus_NoError;
+	
+	context = malloc( sizeof( UDPContext ) );
+	require_action( context, exit, err = mStatus_NoMemoryErr ; LogErr( "RecvUDPMessage", "malloc" ) );
+
+	mDNSPlatformMemZero( context, sizeof( *context ) );
+	context->d = self;
+	context->sd = sd;
+
+	res = recvfrom(sd, &context->pkt.msg, sizeof(context->pkt.msg), 0, (struct sockaddr *)&context->cliaddr, &clisize);
+
+	require_action( res >= 0, exit, err = mStatus_UnknownErr ; LogErr( "RecvUDPMessage", "recvfrom" ) );
+	context->pkt.len = res;
+	require_action( clisize == sizeof( context->cliaddr ), exit, err = mStatus_UnknownErr ; Log( "Client address of unknown size %d", clisize ) );
+	context->pkt.src = context->cliaddr;
+
+	// Set the zone in the packet
+
+	SetZone( context->d, &context->pkt );
+
+	// Notify messages handled by main thread
+
+	if ( IsNotify( &context->pkt ) )
+		{
+		int e = RecvNotify( self, &context->pkt );
+		free(context);
+		return e;
+		}
+	else if ( IsAuthorized( context->d, &context->pkt, &key, &rcode, &tcode ) )
+		{
+		if ( IsLLQRequest( &context->pkt ) )
+			{
+			// LLQ messages handled by main thread
+			int e = RecvLLQ( self, &context->pkt, NULL );
+			free(context);
+			return e;
+			}
+
+		if ( IsLLQAck(&context->pkt ) )
+			{
+			// !!!KRS need to do acks + retrans
+	
+			free(context);
+			return 0;
+			}
+	
+		err = pthread_create( &tid, NULL, UDPMessageHandler, context );
+		require_action( !err, exit, LogErr( "RecvUDPMessage", "pthread_create" ) );
+
+		pthread_detach(tid);
+		}
+	else
+		{
+		PktMsg reply;
+		int    e;
+
+		memcpy( &reply, &context->pkt, sizeof( PktMsg ) );
+
+		reply.msg.h.flags.b[0]  =  kDNSFlag0_QR_Response | kDNSFlag0_AA | kDNSFlag0_RD;
+		reply.msg.h.flags.b[1]  =  kDNSFlag1_RA | kDNSFlag1_RC_NXDomain;
+
+		e = sendto( sd, &reply.msg, reply.len, 0, ( struct sockaddr* ) &context->pkt.src, sizeof( context->pkt.src ) );
+		require_action_quiet( e == ( int ) reply.len, exit, LogErr( "RecvUDPMessage", "sendto" ) );
+
+		err = mStatus_NoAuth;
+		}
+
+exit:
+
+	if ( err && context )
+		{
+		free( context );
+		}
+
+	return err;
+	}
+
+
+mDNSlocal void
+FreeTCPContext
+	(
+	TCPContext * context
+	)
+	{
+	if ( context )
+		{
+		if ( context->sock )
+			{
+			mDNSPlatformTCPCloseConnection( context->sock );
+			}
+
+		free( context );
+		}
+	}
+
+
+mDNSlocal void*
+TCPMessageHandler
+	(
+	void * vptr
+	)
+	{
+	TCPContext	*	context	= ( TCPContext* ) vptr;
+	PktMsg		*	reply = NULL;
+	int				res;
+	char 			buf[32];
+
+    //!!!KRS if this read blocks indefinitely, we can run out of threads
+	// read the request
+
+	reply = HandleRequest( context->d, &context->pkt );
+	require_action_quiet( reply, exit, LogMsg( "TCPMessageHandler: No reply for client %s", inet_ntop( AF_INET, &context->cliaddr.sin_addr, buf, 32 ) ) );
+
+	// deliver reply to client
+
+	res = SendPacket( context->sock, reply );
+	require_action( res >= 0, exit, LogMsg("TCPMessageHandler: Unable to send reply to client %s", inet_ntop(AF_INET, &context->cliaddr.sin_addr, buf, 32 ) ) );
+
+exit:
+
+	FreeTCPContext( context );
+
+	if ( reply )
+		{
+		free( reply );
+		}
+
+	pthread_exit(NULL);
+	}
+
+
+mDNSlocal void
+RecvTCPMessage
+	(
+	void * param
+	)
+	{
+	TCPContext		*	context = ( TCPContext* ) param;
+	mDNSu16				rcode;
+	mDNSu16				tcode;
+	pthread_t			tid;
+	DomainAuthInfo	*	key;
+	PktMsg			*	pkt;
+	mDNSBool			closed;
+	mDNSBool			freeContext = mDNStrue;
+	mStatus				err = mStatus_NoError;
+
+	// Receive a packet.  It's okay if we don't actually read a packet, as long as the closed flag is
+	// set to false.  This is because SSL/TLS layer might gobble up the first packet that we read off the
+	// wire.  We'll let it do that, and wait for the next packet which will be ours.
+
+	pkt = RecvPacket( context->sock, &context->pkt, &closed );
+	if (pkt) HdrNToH(pkt);
+	require_action( pkt || !closed, exit, err = mStatus_UnknownErr; LogMsg( "client disconnected" ) );
+
+	if ( pkt )
+		{
+		// Always do this, regardless of what kind of packet it is.  If we wanted LLQ events to be sent over TCP,
+		// we would change this line of code.  As it is now, we will reply to an LLQ via TCP, but then events
+		// are sent over UDP
+
+		RemoveSourceFromEventLoop( context->d, context->sock );
+
+		// Set's the DNS Zone that is associated with this message
+
+		SetZone( context->d, &context->pkt );
+
+		// IsAuthorized will make sure the message is authorized for the designated zone.
+		// After verifying the signature, it will strip the TSIG from the message
+
+		if ( IsAuthorized( context->d, &context->pkt, &key, &rcode, &tcode ) )
+			{
+			if ( IsLLQRequest( &context->pkt ) )
+				{
+				// LLQ messages handled by main thread
+				RecvLLQ( context->d, &context->pkt, context->sock);
+				}
+			else
+				{
+				err = pthread_create( &tid, NULL, TCPMessageHandler, context );
+
+				if ( err )
+					{
+					LogErr( "RecvTCPMessage", "pthread_create" );
+					err = mStatus_NoError;
+					goto exit;
+					}
+
+				// Let the thread free the context
+
+				freeContext = mDNSfalse;
+					
+				pthread_detach(tid);
+				}
+			}
+		else
+			{
+			PktMsg reply;
+
+			LogMsg( "Client %s Not authorized for zone %##s", inet_ntoa( context->pkt.src.sin_addr ), pkt->zone->name.c );
+
+			memcpy( &reply, &context->pkt, sizeof( PktMsg ) );
+
+			reply.msg.h.flags.b[0]  =  kDNSFlag0_QR_Response | kDNSFlag0_AA | kDNSFlag0_RD;
+			reply.msg.h.flags.b[1]  =  kDNSFlag1_RA | kDNSFlag1_RC_Refused;
+
+			SendPacket( context->sock, &reply );
+			}
+		}
+	else
+		{
+		freeContext = mDNSfalse;
+		}
+
+exit:
+
+	if ( err )
+		{
+		RemoveSourceFromEventLoop( context->d, context->sock );
+		}
+
+	if ( freeContext )
+		{
+		FreeTCPContext( context );
+		}
+	}
+
+
+mDNSlocal int
+AcceptTCPConnection
+	(
+	DaemonInfo		*	self,
+	int					sd,
+	TCPSocketFlags	flags
+	)
+	{
+	TCPContext *	context = NULL;
+	unsigned int	clilen = sizeof( context->cliaddr);
+	int				newSock;
+	mStatus			err = mStatus_NoError;
+	
+	context = ( TCPContext* ) malloc( sizeof( TCPContext ) );
+	require_action( context, exit, err = mStatus_NoMemoryErr; LogErr( "AcceptTCPConnection", "malloc" ) );
+	mDNSPlatformMemZero( context, sizeof( sizeof( TCPContext ) ) );
+	context->d		 = self;
+	newSock = accept( sd, ( struct sockaddr* ) &context->cliaddr, &clilen );
+	require_action( newSock != -1, exit, err = mStatus_UnknownErr; LogErr( "AcceptTCPConnection", "accept" ) );
+
+	context->sock = mDNSPlatformTCPAccept( flags, newSock );
+	require_action( context->sock, exit, err = mStatus_UnknownErr; LogErr( "AcceptTCPConnection", "mDNSPlatformTCPAccept" ) );
+
+	err = AddSourceToEventLoop( self, context->sock, RecvTCPMessage, context );
+	require_action( !err, exit, LogErr( "AcceptTCPConnection", "AddSourceToEventLoop" ) );
+
+exit:
+
+	if ( err && context )
+		{
+		free( context );
+		context = NULL;
+		}
+
+	return err;
+	}
+
+
+// main event loop
+// listen for incoming requests, periodically check table for expired records, respond to signals
+mDNSlocal int Run(DaemonInfo *d)
+	{
+	int staticMaxFD, nfds;
+	fd_set rset;
+	struct timeval timenow, timeout, EventTS, tablecheck = { 0, 0 };
+	mDNSBool EventsPending = mDNSfalse;
+	
+   	VLog("Listening for requests...");
+
+	staticMaxFD = 0;
+
+	if ( d->tcpsd + 1  > staticMaxFD )				staticMaxFD = d->tcpsd + 1;
+	if ( d->udpsd + 1  > staticMaxFD )				staticMaxFD = d->udpsd + 1;
+	if ( d->tlssd + 1  > staticMaxFD )				staticMaxFD = d->tlssd + 1;
+	if ( d->llq_tcpsd + 1 > staticMaxFD )			staticMaxFD = d->llq_tcpsd + 1;
+	if ( d->llq_udpsd + 1 > staticMaxFD )			staticMaxFD = d->llq_udpsd + 1;
+	if ( d->LLQEventListenSock + 1 > staticMaxFD )	staticMaxFD = d->LLQEventListenSock + 1;
+	
+	while(1)
+		{
+		EventSource	* source;
+		int           maxFD;
+
+		// set timeout
+		timeout.tv_sec = timeout.tv_usec = 0;
+		if (gettimeofday(&timenow, NULL)) { LogErr("Run", "gettimeofday"); return -1; }
+
+		if (EventsPending)
+			{
+			if (timenow.tv_sec - EventTS.tv_sec >= 5)           // if we've been waiting 5 seconds for a "quiet" period to send
+				{ GenLLQEvents(d); EventsPending = mDNSfalse; } // events, we go ahead and do it now
+			else timeout.tv_usec = 500000;                      // else do events after 1/2 second with no new events or LLQs
+			}
+		if (!EventsPending)
+			{
+			// if no pending events, timeout when we need to check for expired records
+			if (tablecheck.tv_sec && timenow.tv_sec - tablecheck.tv_sec >= 0)
+				{ DeleteRecords(d, mDNSfalse); tablecheck.tv_sec = 0; } // table check overdue				
+			if (!tablecheck.tv_sec) tablecheck.tv_sec = timenow.tv_sec + EXPIRATION_INTERVAL;
+			timeout.tv_sec = tablecheck.tv_sec - timenow.tv_sec;
+			}
+
+		FD_ZERO(&rset);
+		FD_SET( d->tcpsd, &rset );
+		FD_SET( d->udpsd, &rset );
+		FD_SET( d->tlssd, &rset );
+		FD_SET( d->llq_tcpsd, &rset );
+		FD_SET( d->llq_udpsd, &rset );
+		FD_SET( d->LLQEventListenSock, &rset );
+
+		maxFD = staticMaxFD;
+
+		for ( source = ( EventSource* ) d->eventSources.Head; source; source = source->next )
+			{
+			FD_SET( source->fd, &rset );
+
+			if ( source->fd > maxFD )
+				{
+				maxFD = source->fd;
+				}
+			}
+
+		nfds = select( maxFD + 1, &rset, NULL, NULL, &timeout);
+		if (nfds < 0)
+			{
+			if (errno == EINTR)
+				{
+				if (terminate)
+					{
+					// close sockets to prevent clients from making new requests during shutdown
+					close( d->tcpsd );
+					close( d->udpsd );
+					close( d->tlssd );
+					close( d->llq_tcpsd );
+					close( d->llq_udpsd );
+					d->tcpsd = d->udpsd = d->tlssd = d->llq_tcpsd = d->llq_udpsd = -1;
+					DeleteRecords(d, mDNStrue);
+					return 0;
+					}
+				else if (dumptable)
+					{
+					Log( "Received SIGINFO" );
+
+					PrintLeaseTable(d);
+					PrintLLQTable(d);
+					PrintLLQAnswers(d);
+					dumptable = 0;
+					}
+				else if (hangup)
+					{
+					int err;
+
+					Log( "Received SIGHUP" );
+
+					err = ParseConfig( d, cfgfile );
+
+					if ( err )
+						{
+						LogErr( "Run", "ParseConfig" );
+						return -1;
+						}
+
+					hangup = 0;
+					}
+				else
+					{
+					Log("Received unhandled signal - continuing");
+					}
+				}
+			else
+				{
+				LogErr("Run", "select"); return -1;
+				}
+			}
+		else if (nfds)
+			{
+			if (FD_ISSET(d->udpsd, &rset))		RecvUDPMessage( d, d->udpsd );
+			if (FD_ISSET(d->llq_udpsd, &rset))	RecvUDPMessage( d, d->llq_udpsd );
+			if (FD_ISSET(d->tcpsd, &rset))		AcceptTCPConnection( d, d->tcpsd, 0 );
+			if (FD_ISSET(d->llq_tcpsd, &rset))	AcceptTCPConnection( d, d->llq_tcpsd, 0 );
+			if (FD_ISSET(d->tlssd, &rset))  	AcceptTCPConnection( d, d->tlssd, TCP_SOCKET_FLAGS );
+			if (FD_ISSET(d->LLQEventListenSock, &rset))
+				{
+				// clear signalling data off socket
+				char buf[256];
+				recv(d->LLQEventListenSock, buf, 256, 0);
+				if (!EventsPending)
+					{
+					EventsPending = mDNStrue;
+					if (gettimeofday(&EventTS, NULL)) { LogErr("Run", "gettimeofday"); return -1; }
+					}
+				}
+
+			for ( source = ( EventSource* ) d->eventSources.Head; source; source = source->next )
+				{
+				if ( FD_ISSET( source->fd, &rset ) )
+					{
+					source->callback( source->context );
+					break;  // in case we removed this guy from the event loop
+					}
+				}
+			}
+		else
+			{
+			// timeout
+			if (EventsPending) { GenLLQEvents(d); EventsPending = mDNSfalse; }
+			else { DeleteRecords(d, mDNSfalse); tablecheck.tv_sec = 0; }
+			}
+		}
+	return 0;
+	}
+
+// signal handler sets global variables, which are inspected by main event loop
+// (select automatically returns due to the handled signal)
+mDNSlocal void HndlSignal(int sig)
+	{
+	if (sig == SIGTERM || sig == SIGINT ) { terminate = 1; return; }
+	if (sig == INFO_SIGNAL)               { dumptable = 1; return; }
+	if (sig == SIGHUP)                    { hangup    = 1; return; }
+	}
+
+mDNSlocal mStatus
+SetPublicSRV
+	(
+	DaemonInfo	*	d,
+	const char	*	name
+	)
+	{
+	DNameListElem * elem;
+	mStatus			err = mStatus_NoError;
+
+	elem = ( DNameListElem* ) malloc( sizeof( DNameListElem ) );
+	require_action( elem, exit, err = mStatus_NoMemoryErr );
+	MakeDomainNameFromDNSNameString( &elem->name, name );
+	elem->next = d->public_names;
+	d->public_names = elem;
+
+exit:
+
+	return err;
+	}
+
+
+int main(int argc, char *argv[])
+	{
+	int started_via_launchd = 0;
+	DaemonInfo *d;
+	struct rlimit rlim;
+
+	Log("dnsextd starting");
+
+	d = malloc(sizeof(*d));
+	if (!d) { LogErr("main", "malloc"); exit(1); }
+	mDNSPlatformMemZero(d, sizeof(DaemonInfo));
+
+	// Setup the public SRV record names
+
+	SetPublicSRV(d, "_dns-update._udp.");
+	SetPublicSRV(d, "_dns-llq._udp.");
+	SetPublicSRV(d, "_dns-update-tls._tcp.");
+	SetPublicSRV(d, "_dns-query-tls._tcp.");
+	SetPublicSRV(d, "_dns-llq-tls._tcp.");
+
+	// Setup signal handling
+	
+	if (signal(SIGHUP,      HndlSignal) == SIG_ERR) perror("Can't catch SIGHUP");
+	if (signal(SIGTERM,     HndlSignal) == SIG_ERR) perror("Can't catch SIGTERM");
+	if (signal(INFO_SIGNAL, HndlSignal) == SIG_ERR) perror("Can't catch SIGINFO");
+	if (signal(SIGINT,      HndlSignal) == SIG_ERR) perror("Can't catch SIGINT");
+	if (signal(SIGPIPE,     SIG_IGN  )  == SIG_ERR) perror("Can't ignore SIGPIPE");
+
+	// remove open file limit
+	rlim.rlim_max = RLIM_INFINITY;
+	rlim.rlim_cur = RLIM_INFINITY;
+	if (setrlimit(RLIMIT_NOFILE, &rlim) < 0)
+		{
+		LogErr("main", "setrlimit");
+		Log("Using default file descriptor resource limit");
+		}
+	
+	if (argc > 1 && !strcasecmp(argv[1], "-launchd"))
+		{
+		Log("started_via_launchd");
+		started_via_launchd = 1;
+		argv++;
+		argc--;
+		}
+	if (ProcessArgs(argc, argv, d) < 0) { LogErr("main", "ProcessArgs"); exit(1); }
+
+	if (!foreground && !started_via_launchd)
+		{
+		if (daemon(0,0))
+			{
+			LogErr("main", "daemon");
+			foreground = 1;
+			}
+		}
+
+	if (InitLeaseTable(d) < 0) { LogErr("main", "InitLeaseTable"); exit(1); }
+	if (SetupSockets(d) < 0) { LogErr("main", "SetupSockets"); exit(1); }
+	if (SetUpdateSRV(d) < 0) { LogErr("main", "SetUpdateSRV"); exit(1); }
+
+	Run(d);
+
+	Log("dnsextd stopping");
+
+	if (ClearUpdateSRV(d) < 0) { LogErr("main", "ClearUpdateSRV"); exit(1); }  // clear update srv's even if Run or pthread_create returns an error
+	free(d);
+	exit(0);
+	}
+
+
+// These are stubbed out implementations of up-call routines that the various platform support layers
+// call.  These routines are fully implemented in both mDNS.c and uDNS.c, but dnsextd doesn't
+// link this code in.
+//
+// It's an error for these routines to actually be called, so perhaps we should log any call
+// to them.
+void mDNSCoreInitComplete( mDNS * const m, mStatus result) { ( void ) m; ( void ) result; }
+void mDNS_ConfigChanged(mDNS *const m)  { ( void ) m; }
+void mDNSCoreMachineSleep(mDNS * const m, mDNSBool wake) { ( void ) m; ( void ) wake; }
+void mDNSCoreReceive(mDNS *const m, void *const msg, const mDNSu8 *const end,
+                                const mDNSAddr *const srcaddr, const mDNSIPPort srcport,
+                                const mDNSAddr *const dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID iid)
+	{ ( void ) m; ( void ) msg; ( void ) end; ( void ) srcaddr; ( void ) srcport; ( void ) dstaddr; ( void ) dstport; ( void ) iid; }
+DNSServer *mDNS_AddDNSServer(mDNS *const m, const domainname *d, const mDNSInterfaceID interface, const mDNSAddr *addr, const mDNSIPPort port, mDNSBool scoped, mDNSu32 timeout)
+	{ ( void ) m; ( void ) d; ( void ) interface; ( void ) addr; ( void ) port; ( void ) scoped; ( void ) timeout; return(NULL); }
+void mDNS_AddSearchDomain(const domainname *const domain, mDNSInterfaceID InterfaceID) { (void)domain; (void) InterfaceID;}
+void mDNS_AddDynDNSHostName(mDNS *m, const domainname *fqdn, mDNSRecordCallback *StatusCallback, const void *StatusContext)
+	{ ( void ) m; ( void ) fqdn; ( void ) StatusCallback; ( void ) StatusContext; }
+mDNSs32 mDNS_Execute   (mDNS *const m) { ( void ) m; return 0; }
+mDNSs32 mDNS_TimeNow(const mDNS *const m) { ( void ) m; return 0; }
+mStatus mDNS_Deregister(mDNS *const m, AuthRecord *const rr) { ( void ) m; ( void ) rr; return 0; }
+void mDNS_DeregisterInterface(mDNS *const m, NetworkInterfaceInfo *set, mDNSBool flapping)
+	{ ( void ) m; ( void ) set; ( void ) flapping; }
+const char * const  mDNS_DomainTypeNames[1] = {};
+mStatus mDNS_GetDomains(mDNS *const m, DNSQuestion *const question, mDNS_DomainType DomainType, const domainname *dom,
+                                const mDNSInterfaceID InterfaceID, mDNSQuestionCallback *Callback, void *Context)
+	{ ( void ) m; ( void ) question; ( void ) DomainType; ( void ) dom; ( void ) InterfaceID; ( void ) Callback; ( void ) Context; return 0; }
+mStatus mDNS_Register(mDNS *const m, AuthRecord *const rr) { ( void ) m; ( void ) rr; return 0; }
+mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *set, mDNSBool flapping)
+	{ ( void ) m; ( void ) set; ( void ) flapping; return 0; }
+void mDNS_RemoveDynDNSHostName(mDNS *m, const domainname *fqdn) { ( void ) m; ( void ) fqdn; }
+void mDNS_SetFQDN(mDNS * const m) { ( void ) m; }
+void mDNS_SetPrimaryInterfaceInfo(mDNS *m, const mDNSAddr *v4addr,  const mDNSAddr *v6addr, const mDNSAddr *router)
+	{ ( void ) m; ( void ) v4addr; ( void ) v6addr; ( void ) router; }
+mStatus uDNS_SetupDNSConfig( mDNS *const m ) { ( void ) m; return 0; }
+mStatus mDNS_SetSecretForDomain(mDNS *m, DomainAuthInfo *info,
+	const domainname *domain, const domainname *keyname, const char *b64keydata, const domainname *hostname, mDNSIPPort *port, const char *autoTunnelPrefix)
+	{ ( void ) m; ( void ) info; ( void ) domain; ( void ) keyname; ( void ) b64keydata; ( void ) hostname; (void) port; ( void ) autoTunnelPrefix; return 0; }
+mStatus mDNS_StopQuery(mDNS *const m, DNSQuestion *const question) { ( void ) m; ( void ) question; return 0; }
+void TriggerEventCompletion(void);
+void TriggerEventCompletion() {}
+mDNS mDNSStorage;
+
+
+// For convenience when using the "strings" command, this is the last thing in the file
+// The "@(#) " pattern is a special prefix the "what" command looks for
+const char mDNSResponderVersionString_SCCS[] = "@(#) dnsextd " STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")";
+
+#if _BUILDING_XCODE_PROJECT_
+// If the process crashes, then this string will be magically included in the automatically-generated crash log
+const char *__crashreporter_info__ = mDNSResponderVersionString_SCCS + 5;
+asm(".desc ___crashreporter_info__, 0x10");
+#endif
diff --git a/mdnsresponder/mDNSShared/dnsextd.conf b/mdnsresponder/mDNSShared/dnsextd.conf
new file mode 100644
index 0000000..0379580
--- /dev/null
+++ b/mdnsresponder/mDNSShared/dnsextd.conf
@@ -0,0 +1,60 @@
+// ----------------------------------------------------------------------------
+//
+// Instructions for /etc/dnsextd.conf (this file)
+//
+// In most cases, you should not need to change these default options in
+// the "options" section below. The dnsextd daemon will receive DNS packets
+// on port 53, and forward them on as appropriate to BIND on localhost:5030.
+//
+// You need to edit the "zone" statement below to give the name of your
+// dynamic zone that will be accepting Wide-Area Bonjour DNS updates.
+//
+// ----------------------------------------------------------------------------
+//
+// Instructions for /etc/named.conf
+//
+// In /etc/named.conf you will need to modify the "options" section to
+// tell BIND to accept packets from localhost:5030, like this:
+//
+//   listen-on port 5030 { 127.0.0.1; };
+//
+// You also need a "zone" statement in /etc/named.conf to tell BIND the update
+// policy for your dynamic zone. For example, within a small closed private
+// network, you might allow anyone to perform updates. To do that, you just
+// permit any and all updates coming from dnsextd on the same machine:
+//
+//   zone "my-dynamic-subdomain.company.com."
+//     { type master; file "db.xxx"; allow-update { 127.0.0.1; }; };
+//
+// On a machine connected to the Internet or other large open network,
+// you'll want to limit updates to only users with keys. For example,
+// you could choose to allow anyone with a DNS key on your server to
+// perform updates in your dynamic zone, like this:
+//
+//   key keyname. { algorithm hmac-md5; secret "abcdefghijklmnopqrstuv=="; };
+//   zone "my-dynamic-subdomain.company.com." in
+//     {
+//     type master;
+//     file "db.my-dynamic-subdomain.company.com";
+//     update-policy { grant * wildcard *.my-dynamic-subdomain.company.com.; };
+//     };
+//
+// You could use a single key which you give to all authorized users, but
+// it is better (though more work) to create a unique key for each user.
+//
+// ----------------------------------------------------------------------------
+
+options {
+//  This defaults to: * port 53
+//	listen-on 			port 53 { 192.168.2.10; 127.0.0.1; };
+//  This defaults to: 127.0.0.1:5030
+//	nameserver			address 127.0.0.1 port 5030;
+//  This defaults to: 5533
+//	private 			port 5533;
+//  This defaults to: 5352
+//	llq	   				port 5352;
+};
+
+zone "my-dynamic-subdomain.company.com." {
+	type public;
+};
diff --git a/mdnsresponder/mDNSShared/dnsextd.h b/mdnsresponder/mDNSShared/dnsextd.h
new file mode 100644
index 0000000..d75d695
--- /dev/null
+++ b/mdnsresponder/mDNSShared/dnsextd.h
@@ -0,0 +1,163 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2006 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+
+#ifndef _dnsextd_h
+#define _dnsextd_h
+
+
+#include <mDNSEmbeddedAPI.h>
+#include <DNSCommon.h>
+#include <GenLinkedList.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+
+#define LLQ_TABLESIZE	1024	// !!!KRS make this dynamically growable
+
+
+typedef enum DNSZoneSpecType
+{
+	kDNSZonePublic,
+	kDNSZonePrivate
+} DNSZoneSpecType;
+
+
+typedef struct DNSZone
+{
+	domainname				name;
+	DNSZoneSpecType			type;
+	DomainAuthInfo		*	updateKeys;	// linked list of keys for signing deletion updates
+	DomainAuthInfo		*	queryKeys;	// linked list of keys for queries
+	struct DNSZone		*	next;
+} DNSZone;
+	
+	
+typedef struct
+	{
+    struct sockaddr_in src;
+    size_t len;
+	DNSZone * zone;
+	mDNSBool   isZonePublic;
+    DNSMessage msg;
+    // Note: extra storage for oversized (TCP) messages goes here
+	} PktMsg;
+
+// lease table entry
+typedef struct RRTableElem
+	{
+    struct RRTableElem *next;
+    struct sockaddr_in cli;   // client's source address
+    long expire;              // expiration time, in seconds since epoch
+    domainname zone;          // from zone field of update message
+    domainname name;          // name of the record
+    CacheRecord rr;           // last field in struct allows for allocation of oversized RRs
+	} RRTableElem;
+
+typedef enum
+	{
+	RequestReceived = 0,
+	ChallengeSent   = 1,
+	Established     = 2
+	} LLQState;
+
+typedef struct AnswerListElem
+	{
+    struct AnswerListElem *next;
+    domainname name;
+    mDNSu16 type;
+    CacheRecord *KnownAnswers;  // All valid answers delivered to client
+    CacheRecord *EventList;     // New answers (adds/removes) to be sent to client
+    int refcount;
+    mDNSBool UseTCP;            // Use TCP if UDP would cause truncation
+    pthread_t tid;              // Allow parallel list updates
+	} AnswerListElem;
+
+// llq table entry
+typedef struct LLQEntry
+	{
+    struct LLQEntry *next;     
+    struct sockaddr_in cli;   // clien'ts source address 
+    domainname qname;
+    mDNSu16 qtype;
+    mDNSOpaque64 id;
+    LLQState state;
+    mDNSu32 lease;            // original lease, in seconds
+    mDNSs32 expire;           // expiration, absolute, in seconds since epoch
+    AnswerListElem *AnswerList;
+	} LLQEntry;
+
+
+typedef	void (*EventCallback)( void * context );
+
+typedef struct EventSource
+	{
+	EventCallback			callback;
+	void				*	context;
+	TCPSocket *			sock;
+	int						fd;
+	mDNSBool				markedForDeletion;
+	struct  EventSource	*	next;
+	} EventSource;
+
+
+// daemon-wide information
+typedef struct 
+	{
+    // server variables - read only after initialization (no locking)
+	struct sockaddr_in	addr;			// the address we will bind to
+	struct sockaddr_in	llq_addr;		// the address we will receive llq requests on.
+    struct sockaddr_in	ns_addr;		// the real ns server address
+	int					tcpsd;			// listening TCP socket for dns requests
+	int					udpsd;			// listening UDP socket for dns requests
+	int					tlssd;			// listening TCP socket for private browsing
+    int					llq_tcpsd;		// listening TCP socket for llq service
+    int					llq_udpsd;		// listening UDP socket for llq service
+	DNameListElem	*	public_names;	// list of public SRV names
+	DNSZone			*	zones;
+
+    // daemon variables - read only after initialization (no locking)
+    mDNSIPPort private_port;           // listening port for private messages
+    mDNSIPPort llq_port;           // listening port for llq
+
+    // lease table variables (locked via mutex after initialization)
+    RRTableElem **table;       // hashtable for records with leases
+    pthread_mutex_t tablelock; // mutex for lease table
+    mDNSs32 nbuckets;          // buckets allocated
+    mDNSs32 nelems;            // elements in table
+
+    // LLQ table variables
+    LLQEntry *LLQTable[LLQ_TABLESIZE];  // !!!KRS change this and RRTable to use a common data structure
+    AnswerListElem *AnswerTable[LLQ_TABLESIZE];
+    int AnswerTableCount;
+    int LLQEventNotifySock;          // Unix domain socket pair - update handling thread writes to EventNotifySock, which wakes
+    int LLQEventListenSock;          // the main thread listening on EventListenSock, indicating that the zone has changed
+
+	GenLinkedList	eventSources;	// linked list of EventSource's
+	} DaemonInfo;
+
+
+int
+ParseConfig
+	(
+	DaemonInfo	*	d,
+	const char	*	file
+	);
+
+
+#endif
diff --git a/mdnsresponder/mDNSShared/dnsextd_lexer.l b/mdnsresponder/mDNSShared/dnsextd_lexer.l
new file mode 100644
index 0000000..2f614e5
--- /dev/null
+++ b/mdnsresponder/mDNSShared/dnsextd_lexer.l
@@ -0,0 +1,84 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2006 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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 <string.h>
+#include <stdio.h>
+#include "dnsextd_parser.h"
+
+
+extern YYSTYPE yylval;
+
+/* Mac OS X 10.4 has flex version 2.5.4, which doesn't define yylineno for us */
+/* Mac OS X 10.5 has flex version 2.5.33, which does define yylineno          */
+#if YY_FLEX_MAJOR_VERSION <= 2 && YY_FLEX_MINOR_VERSION <= 5 && YY_FLEX_SUBMINOR_VERSION <= 4
+int yylineno = 1;
+#endif
+
+int  yylex(void);
+
+static char*
+StripQuotes
+	(
+	const char * string
+	)
+{
+	char * dup;
+
+	dup = strdup( string + 1);
+
+	dup[ strlen( dup ) - 1 ] = '\0';
+
+	return dup;
+}
+
+
+%}
+
+%option nounput
+%%
+
+options								return OPTIONS;
+listen-on							return LISTEN_ON;
+nameserver							return NAMESERVER;
+port								return PORT;
+address								return ADDRESS;
+llq									return LLQ;
+public								return PUBLIC;
+private								return PRIVATE;
+key									return KEY;
+allow-update						return ALLOWUPDATE;
+allow-query							return ALLOWQUERY;
+algorithm							return ALGORITHM;
+secret								return SECRET;
+zone                    			return ZONE;
+type                    			return TYPE;
+allow								return ALLOW;
+\{                      			return OBRACE;
+\}                      			return EBRACE;
+;                       			return SEMICOLON;
+IN									return IN;
+\*									yylval.string = strdup(yytext);	return WILDCARD;
+[0-9]+\.[0-9]+\.[0-9]+\.[0-9]+		yylval.string = strdup(yytext);	return DOTTED_DECIMAL_ADDRESS;
+[0123456789]+						yylval.number = atoi(yytext);	return NUMBER;
+[a-zA-Z0-9]+(\.[a-zA-Z0-9]+)*		yylval.string = strdup(yytext);	return HOSTNAME;
+[a-zA-Z0-9\.]+([a-zA-Z0-9\.]+)*		yylval.string = strdup(yytext);	return DOMAINNAME;
+\"([^"\\\r\n]*(\\.[^"\\\r\n]*)*)\"	yylval.string = StripQuotes(yytext);	return QUOTEDSTRING;
+[\/][\/].*							/* ignore C++ style comments */;
+\n                      			yylineno++; /* ignore EOL */;
+[ \t]+                  			/* ignore whitespace */;
+%%
diff --git a/mdnsresponder/mDNSShared/dnsextd_parser.y b/mdnsresponder/mDNSShared/dnsextd_parser.y
new file mode 100644
index 0000000..18c5990
--- /dev/null
+++ b/mdnsresponder/mDNSShared/dnsextd_parser.y
@@ -0,0 +1,585 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2006 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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 <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include "mDNSEmbeddedAPI.h"
+#include "DebugServices.h"
+#include "dnsextd.h"
+
+void yyerror( const char* error );
+int  yylex(void);
+
+
+typedef struct StringListElem
+{
+	char					*	string;
+	struct StringListElem	*	next;
+} StringListElem;
+
+
+typedef struct OptionsInfo
+{
+	char	server_address[ 256 ];
+	int		server_port;
+	char	source_address[ 256 ];
+	int		source_port;
+	int		private_port;
+	int		llq_port;
+} OptionsInfo;
+
+
+typedef struct ZoneInfo
+{
+	char	name[ 256 ];
+	char	certificate_name[ 256 ];
+	char	allow_clients_file[ 256 ];
+	char	allow_clients[ 256 ];
+	char	key[ 256 ];
+} ZoneInfo;
+
+
+typedef struct KeySpec
+{
+	char 				name[ 256 ];
+	char				algorithm[ 256 ];
+	char				secret[ 256 ];
+	struct KeySpec	*	next;
+} KeySpec;
+
+
+typedef struct ZoneSpec
+{
+	char				name[ 256 ];
+	DNSZoneSpecType		type;
+	StringListElem	*	allowUpdate;
+	StringListElem	*	allowQuery;
+	char				key[ 256 ];
+	struct ZoneSpec	*	next;
+} ZoneSpec;
+
+
+static StringListElem	*	g_stringList = NULL;
+static KeySpec			*	g_keys;
+static ZoneSpec			*	g_zones;
+static ZoneSpec				g_zoneSpec;
+static const char		*	g_filename;
+
+#define YYPARSE_PARAM  context
+
+void
+SetupOptions
+	(
+	OptionsInfo	*	info,
+	void		*	context
+	);
+
+%}
+
+%union
+{
+	int			number;
+	char	*	string;
+}
+
+%token	OPTIONS 
+%token	LISTEN_ON 
+%token	NAMESERVER
+%token	PORT 
+%token	ADDRESS 
+%token	LLQ 
+%token	PUBLIC
+%token  PRIVATE
+%token  ALLOWUPDATE
+%token  ALLOWQUERY
+%token	KEY 
+%token  ALGORITHM
+%token  SECRET
+%token  ISSUER
+%token  SERIAL
+%token	ZONE
+%token  TYPE
+%token	ALLOW
+%token	OBRACE 
+%token	EBRACE 
+%token	SEMICOLON
+%token 	IN
+%token	<string>	DOTTED_DECIMAL_ADDRESS 
+%token	<string>	WILDCARD 
+%token	<string>	DOMAINNAME 
+%token	<string>	HOSTNAME 
+%token	<string>	QUOTEDSTRING
+%token	<number> 	NUMBER 
+
+%type	<string>	addressstatement
+%type	<string>	networkaddress
+
+%%
+
+commands:
+        |        
+        commands command SEMICOLON
+        ;
+
+
+command:
+		options_set
+		|
+        zone_set 
+		|
+		key_set
+        ;
+
+
+options_set:
+		OPTIONS optionscontent
+		{
+			// SetupOptions( &g_optionsInfo, context );
+		}
+		;
+
+optionscontent:
+		OBRACE optionsstatements EBRACE
+		;
+
+optionsstatements:
+		|
+		optionsstatements optionsstatement SEMICOLON
+		;
+
+
+optionsstatement:
+		statements
+		|
+		LISTEN_ON addresscontent
+		{
+		}
+		|
+		LISTEN_ON PORT NUMBER addresscontent
+		{
+		}
+		|
+		NAMESERVER ADDRESS networkaddress
+		{
+		}
+		|
+		NAMESERVER ADDRESS networkaddress PORT NUMBER
+		{
+		}
+		|
+		PRIVATE PORT NUMBER
+		{
+			( ( DaemonInfo* ) context )->private_port = mDNSOpaque16fromIntVal( $3 );
+		}
+		|
+		LLQ PORT NUMBER
+		{
+			( ( DaemonInfo* ) context )->llq_port = mDNSOpaque16fromIntVal( $3 );
+		}
+		;
+
+key_set:
+        KEY QUOTEDSTRING OBRACE SECRET QUOTEDSTRING SEMICOLON EBRACE
+        {
+			KeySpec	* keySpec;
+
+			keySpec = ( KeySpec* ) malloc( sizeof( KeySpec ) );
+
+			if ( !keySpec )
+				{
+				LogMsg("ERROR: memory allocation failure");
+				YYABORT;
+				}
+
+			strncpy( keySpec->name, $2, sizeof( keySpec->name ) );
+			strncpy( keySpec->secret, $5, sizeof( keySpec->secret ) );
+
+			keySpec->next	= g_keys;
+			g_keys			= keySpec;
+        }
+        ;
+
+zone_set:
+		ZONE QUOTEDSTRING zonecontent
+		{
+			ZoneSpec * zoneSpec;
+
+			zoneSpec = ( ZoneSpec* ) malloc( sizeof( ZoneSpec ) );
+
+			if ( !zoneSpec )
+				{
+				LogMsg("ERROR: memory allocation failure");
+				YYABORT;
+				}
+
+			strncpy( zoneSpec->name, $2, sizeof( zoneSpec->name ) );
+			zoneSpec->type = g_zoneSpec.type;
+			strcpy( zoneSpec->key, g_zoneSpec.key );
+			zoneSpec->allowUpdate = g_zoneSpec.allowUpdate;
+			zoneSpec->allowQuery = g_zoneSpec.allowQuery;
+
+			zoneSpec->next = g_zones;
+			g_zones = zoneSpec;
+		}
+		|
+		ZONE QUOTEDSTRING IN zonecontent
+        {
+			ZoneSpec * zoneSpec;
+
+			zoneSpec = ( ZoneSpec* ) malloc( sizeof( ZoneSpec ) );
+
+			if ( !zoneSpec )
+				{
+				LogMsg("ERROR: memory allocation failure");
+				YYABORT;
+				}
+
+			strncpy( zoneSpec->name, $2, sizeof( zoneSpec->name ) );
+			zoneSpec->type = g_zoneSpec.type;
+			strcpy( zoneSpec->key, g_zoneSpec.key );
+			zoneSpec->allowUpdate = g_zoneSpec.allowUpdate;
+			zoneSpec->allowQuery = g_zoneSpec.allowQuery;
+
+			zoneSpec->next = g_zones;
+			g_zones = zoneSpec;
+		}
+        ;
+
+zonecontent:
+		OBRACE zonestatements EBRACE 
+
+zonestatements:
+        |
+        zonestatements zonestatement SEMICOLON
+        ;
+
+zonestatement:
+		TYPE PUBLIC
+		{
+			g_zoneSpec.type = kDNSZonePublic;
+		}
+		|
+		TYPE PRIVATE
+		{
+			g_zoneSpec.type = kDNSZonePrivate;
+		}
+		|
+		ALLOWUPDATE keycontent
+		{
+			g_zoneSpec.allowUpdate = g_stringList;
+			g_stringList = NULL;
+		}
+		|
+		ALLOWQUERY keycontent
+		{
+			g_zoneSpec.allowQuery = g_stringList;
+			g_stringList = NULL;
+		}
+        ;
+
+addresscontent:
+		OBRACE addressstatements EBRACE
+		{
+		}
+
+addressstatements:
+		|
+		addressstatements addressstatement SEMICOLON
+		{
+		}
+		;
+
+addressstatement:
+		DOTTED_DECIMAL_ADDRESS
+		{
+		}
+		;
+
+
+keycontent:
+		OBRACE keystatements EBRACE
+		{
+		}
+
+keystatements:
+		|
+		keystatements keystatement SEMICOLON
+		{
+		}
+		;
+
+keystatement:
+		KEY DOMAINNAME
+		{
+			StringListElem * elem;
+
+			elem = ( StringListElem* ) malloc( sizeof( StringListElem ) );
+
+			if ( !elem )
+				{
+				LogMsg("ERROR: memory allocation failure");
+				YYABORT;
+				}
+
+			elem->string = $2;
+
+			elem->next		= g_stringList;
+			g_stringList	= elem;
+		}
+		;
+
+
+networkaddress:
+		DOTTED_DECIMAL_ADDRESS
+		|
+		HOSTNAME
+		|
+		WILDCARD
+		;
+
+block: 
+		OBRACE zonestatements EBRACE SEMICOLON
+        ;
+
+statements:
+        |
+		statements statement
+        ;
+
+statement:
+		block
+		{
+			$<string>$ = NULL;
+		}
+		|
+		QUOTEDSTRING
+		{
+			$<string>$ = $1;
+		}
+%%
+
+int yywrap(void);
+
+extern int yylineno;
+
+void yyerror( const char *str )
+{
+        fprintf( stderr,"%s:%d: error: %s\n", g_filename, yylineno, str );
+}
+ 
+int yywrap()
+{
+        return 1;
+} 
+
+
+int
+ParseConfig
+	(
+	DaemonInfo	*	d,
+	const char	*	file
+	)
+	{
+	extern FILE		*	yyin;
+	DNSZone			*	zone;
+	DomainAuthInfo	*	key;
+	KeySpec			*	keySpec;
+	ZoneSpec		*	zoneSpec;
+	int					err = 0;
+
+	g_filename = file;
+
+	// Tear down the current zone specifiers
+
+	zone = d->zones;
+
+	while ( zone )
+		{
+		DNSZone * next = zone->next;
+
+		key = zone->updateKeys;
+
+		while ( key )
+			{
+			DomainAuthInfo * nextKey = key->next;
+
+			free( key );
+
+			key = nextKey;
+			}
+
+		key = zone->queryKeys;
+
+		while ( key )
+			{
+			DomainAuthInfo * nextKey = key->next;
+
+			free( key );
+
+			key = nextKey;
+			}
+
+		free( zone );
+
+		zone = next;
+		}
+
+	d->zones = NULL;
+	
+	yyin = fopen( file, "r" );
+	require_action( yyin, exit, err = 0 );
+
+	err = yyparse( ( void* ) d );
+	require_action( !err, exit, err = 1 );
+
+	for ( zoneSpec = g_zones; zoneSpec; zoneSpec = zoneSpec->next )
+		{
+		StringListElem  *   elem;
+		mDNSu8			*	ok;
+
+		zone = ( DNSZone* ) malloc( sizeof( DNSZone ) );
+		require_action( zone, exit, err = 1 );
+		memset( zone, 0, sizeof( DNSZone ) );
+
+		zone->next	= d->zones;
+		d->zones	= zone;
+
+		// Fill in the domainname
+
+		ok = MakeDomainNameFromDNSNameString( &zone->name, zoneSpec->name );
+		require_action( ok, exit, err = 1 );
+
+		// Fill in the type
+
+		zone->type = zoneSpec->type;
+
+		// Fill in the allow-update keys
+
+		for ( elem = zoneSpec->allowUpdate; elem; elem = elem->next )
+			{
+			mDNSBool found = mDNSfalse;
+
+			for ( keySpec = g_keys; keySpec; keySpec = keySpec->next )
+				{
+				if ( strcmp( elem->string, keySpec->name ) == 0 )
+					{
+					DomainAuthInfo	*	authInfo = malloc( sizeof( DomainAuthInfo ) );
+					mDNSs32				keylen;
+					require_action( authInfo, exit, err = 1 );
+					memset( authInfo, 0, sizeof( DomainAuthInfo ) );
+
+					ok = MakeDomainNameFromDNSNameString( &authInfo->keyname, keySpec->name );
+					if (!ok) { free(authInfo); err = 1; goto exit; }
+
+					keylen = DNSDigest_ConstructHMACKeyfromBase64( authInfo, keySpec->secret );
+					if (keylen < 0) { free(authInfo); err = 1; goto exit; }
+
+					authInfo->next = zone->updateKeys;
+					zone->updateKeys = authInfo;
+
+					found = mDNStrue;
+
+					break;
+					}
+				}
+
+			// Log this
+			require_action( found, exit, err = 1 );
+			}
+
+		// Fill in the allow-query keys
+
+		for ( elem = zoneSpec->allowQuery; elem; elem = elem->next )
+			{
+			mDNSBool found = mDNSfalse;
+
+			for ( keySpec = g_keys; keySpec; keySpec = keySpec->next )
+				{
+				if ( strcmp( elem->string, keySpec->name ) == 0 )
+					{
+					DomainAuthInfo	*	authInfo = malloc( sizeof( DomainAuthInfo ) );
+					mDNSs32				keylen;
+					require_action( authInfo, exit, err = 1 );
+					memset( authInfo, 0, sizeof( DomainAuthInfo ) );
+
+					ok = MakeDomainNameFromDNSNameString( &authInfo->keyname, keySpec->name );
+					if (!ok) { free(authInfo); err = 1; goto exit; }
+
+					keylen = DNSDigest_ConstructHMACKeyfromBase64( authInfo, keySpec->secret );
+					if (keylen < 0) { free(authInfo); err = 1; goto exit; }
+
+					authInfo->next = zone->queryKeys;
+					zone->queryKeys = authInfo;
+
+					found = mDNStrue;
+
+					break;
+					}
+				}
+
+			// Log this
+			require_action( found, exit, err = 1 );
+			}
+		}
+
+exit:
+
+	return err;
+	}
+
+
+void
+SetupOptions
+	(
+	OptionsInfo	*	info,
+	void		*	context
+	)
+	{
+	DaemonInfo * d = ( DaemonInfo* ) context;
+
+	if ( strlen( info->source_address ) )
+		{
+		inet_pton( AF_INET, info->source_address, &d->addr.sin_addr );
+		}
+
+	if ( info->source_port )
+		{
+		d->addr.sin_port = htons( ( mDNSu16 ) info->source_port );
+		}
+				
+	if ( strlen( info->server_address ) )
+		{
+		inet_pton( AF_INET, info->server_address, &d->ns_addr.sin_addr );
+		}
+
+	if ( info->server_port )
+		{
+		d->ns_addr.sin_port = htons( ( mDNSu16 ) info->server_port );
+		}
+
+	if ( info->private_port )
+		{
+		d->private_port = mDNSOpaque16fromIntVal( info->private_port );
+		}
+
+	if ( info->llq_port )
+		{
+		d->llq_port = mDNSOpaque16fromIntVal( info->llq_port );
+		}
+	}
diff --git a/mdnsresponder/mDNSShared/dnssd_clientlib.c b/mdnsresponder/mDNSShared/dnssd_clientlib.c
new file mode 100644
index 0000000..a92c0c8
--- /dev/null
+++ b/mdnsresponder/mDNSShared/dnssd_clientlib.c
@@ -0,0 +1,368 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2004, Apple Computer, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without 
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright notice, 
+ *     this list of conditions and the following disclaimer. 
+ * 2.  Redistributions in binary form must reproduce the above copyright notice, 
+ *     this list of conditions and the following disclaimer in the documentation 
+ *     and/or other materials provided with the distribution. 
+ * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of its 
+ *     contributors may be used to endorse or promote products derived from this 
+ *     software without specific prior written permission. 
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY 
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY 
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include <string.h>
+
+#include "dns_sd.h"
+
+#if MDNS_BUILDINGSHAREDLIBRARY || MDNS_BUILDINGSTUBLIBRARY
+#pragma export on
+#endif
+
+#if defined(_WIN32)
+// disable warning "conversion from <data> to uint16_t"
+#pragma warning(disable:4244)
+#define strncasecmp _strnicmp
+#define strcasecmp _stricmp 
+#endif
+
+/*********************************************************************************************
+ *
+ *  Supporting Functions
+ *
+ *********************************************************************************************/
+
+#define mDNSIsDigit(X)     ((X) >= '0' && (X) <= '9')
+
+// DomainEndsInDot returns 1 if name ends with a dot, 0 otherwise
+// (DNSServiceConstructFullName depends this returning 1 for true, rather than any non-zero value meaning true)
+
+static int DomainEndsInDot(const char *dom)
+	{
+	while (dom[0] && dom[1])
+		{
+		if (dom[0] == '\\') // advance past escaped byte sequence
+			{
+			if (mDNSIsDigit(dom[1]) && mDNSIsDigit(dom[2]) && mDNSIsDigit(dom[3]))
+				dom += 4;			// If "\ddd"    then skip four
+			else dom += 2;			// else if "\x" then skip two
+			}
+		else dom++;					// else goto next character
+		}
+	return (dom[0] == '.');
+	}
+
+static uint8_t *InternalTXTRecordSearch
+	(
+	uint16_t         txtLen,
+	const void       *txtRecord,
+	const char       *key,
+	unsigned long    *keylen
+	)
+	{
+	uint8_t *p = (uint8_t*)txtRecord;
+	uint8_t *e = p + txtLen;
+	*keylen = (unsigned long) strlen(key);
+	while (p<e)
+		{
+		uint8_t *x = p;
+		p += 1 + p[0];
+		if (p <= e && *keylen <= x[0] && !strncasecmp(key, (char*)x+1, *keylen))
+			if (*keylen == x[0] || x[1+*keylen] == '=') return(x);
+		}
+	return(NULL);
+	}
+
+/*********************************************************************************************
+ *
+ *  General Utility Functions
+ *
+ *********************************************************************************************/
+
+// Note: Need to make sure we don't write more than kDNSServiceMaxDomainName (1009) bytes to fullName
+// In earlier builds this constant was defined to be 1005, so to avoid buffer overruns on clients
+// compiled with that constant we'll actually limit the output to 1005 bytes.
+
+DNSServiceErrorType DNSSD_API DNSServiceConstructFullName
+	(
+	char       *const fullName,
+	const char *const service,      // May be NULL
+	const char *const regtype,
+	const char *const domain
+	)
+	{
+	const size_t len = !regtype ? 0 : strlen(regtype) - DomainEndsInDot(regtype);
+	char       *fn   = fullName;
+	char *const lim  = fullName + 1005;
+	const char *s    = service;
+	const char *r    = regtype;
+	const char *d    = domain;
+
+	// regtype must be at least "x._udp" or "x._tcp"
+	if (len < 6 || !domain || !domain[0]) return kDNSServiceErr_BadParam;
+	if (strncasecmp((regtype + len - 4), "_tcp", 4) && strncasecmp((regtype + len - 4), "_udp", 4)) return kDNSServiceErr_BadParam;
+
+	if (service && *service)
+		{
+		while (*s)
+			{
+			unsigned char c = *s++;				// Needs to be unsigned, or values like 0xFF will be interpreted as < 32
+			if (c <= ' ')						// Escape non-printable characters
+				{
+				if (fn+4 >= lim) goto fail;
+				*fn++ = '\\';
+				*fn++ = '0' + (c / 100);
+				*fn++ = '0' + (c /  10) % 10;
+				c     = '0' + (c      ) % 10;
+				}
+			else if (c == '.' || (c == '\\'))	// Escape dot and backslash literals
+				{
+				if (fn+2 >= lim) goto fail;
+				*fn++ = '\\';
+				}
+			else
+				if (fn+1 >= lim) goto fail;
+			*fn++ = (char)c;
+			}
+		*fn++ = '.';
+		}
+
+	while (*r) if (fn+1 >= lim) goto fail; else *fn++ = *r++;
+	if (!DomainEndsInDot(regtype)) { if (fn+1 >= lim) goto fail; else *fn++ = '.'; }
+
+	while (*d) if (fn+1 >= lim) goto fail; else *fn++ = *d++;
+	if (!DomainEndsInDot(domain)) { if (fn+1 >= lim) goto fail; else *fn++ = '.'; }
+
+	*fn = '\0';
+	return kDNSServiceErr_NoError;
+
+fail:
+	*fn = '\0';
+	return kDNSServiceErr_BadParam;
+	}
+
+/*********************************************************************************************
+ *
+ *   TXT Record Construction Functions
+ *
+ *********************************************************************************************/
+
+typedef struct _TXTRecordRefRealType
+	{
+	uint8_t  *buffer;		// Pointer to data
+	uint16_t buflen;		// Length of buffer
+	uint16_t datalen;		// Length currently in use
+	uint16_t malloced;	// Non-zero if buffer was allocated via malloc()
+	} TXTRecordRefRealType;
+
+#define txtRec ((TXTRecordRefRealType*)txtRecord)
+
+// The opaque storage defined in the public dns_sd.h header is 16 bytes;
+// make sure we don't exceed that.
+struct CompileTimeAssertionCheck_dnssd_clientlib
+	{
+	char assert0[(sizeof(TXTRecordRefRealType) <= 16) ? 1 : -1];
+	};
+
+void DNSSD_API TXTRecordCreate
+	(
+	TXTRecordRef     *txtRecord,
+	uint16_t         bufferLen,
+	void             *buffer
+	)
+	{
+	txtRec->buffer   = buffer;
+	txtRec->buflen   = buffer ? bufferLen : (uint16_t)0;
+	txtRec->datalen  = 0;
+	txtRec->malloced = 0;
+	}
+
+void DNSSD_API TXTRecordDeallocate(TXTRecordRef *txtRecord)
+	{
+	if (txtRec->malloced) free(txtRec->buffer);
+	}
+
+DNSServiceErrorType DNSSD_API TXTRecordSetValue
+	(
+	TXTRecordRef     *txtRecord,
+	const char       *key,
+	uint8_t          valueSize,
+	const void       *value
+	)
+	{
+	uint8_t *start, *p;
+	const char *k;
+	unsigned long keysize, keyvalsize;
+
+	for (k = key; *k; k++) if (*k < 0x20 || *k > 0x7E || *k == '=') return(kDNSServiceErr_Invalid);
+	keysize = (unsigned long)(k - key);
+	keyvalsize = 1 + keysize + (value ? (1 + valueSize) : 0);
+	if (keysize < 1 || keyvalsize > 255) return(kDNSServiceErr_Invalid);
+	(void)TXTRecordRemoveValue(txtRecord, key);
+	if (txtRec->datalen + keyvalsize > txtRec->buflen)
+		{
+		unsigned char *newbuf;
+		unsigned long newlen = txtRec->datalen + keyvalsize;
+		if (newlen > 0xFFFF) return(kDNSServiceErr_Invalid);
+		newbuf = malloc((size_t)newlen);
+		if (!newbuf) return(kDNSServiceErr_NoMemory);
+		memcpy(newbuf, txtRec->buffer, txtRec->datalen);
+		if (txtRec->malloced) free(txtRec->buffer);
+		txtRec->buffer = newbuf;
+		txtRec->buflen = (uint16_t)(newlen);
+		txtRec->malloced = 1;
+		}
+	start = txtRec->buffer + txtRec->datalen;
+	p = start + 1;
+	memcpy(p, key, keysize);
+	p += keysize;
+	if (value)
+		{
+		*p++ = '=';
+		memcpy(p, value, valueSize);
+		p += valueSize;
+		}
+	*start = (uint8_t)(p - start - 1);
+	txtRec->datalen += p - start;
+	return(kDNSServiceErr_NoError);
+	}
+
+DNSServiceErrorType DNSSD_API TXTRecordRemoveValue
+	(
+	TXTRecordRef     *txtRecord,
+	const char       *key
+	)
+	{
+	unsigned long keylen, itemlen, remainder;
+	uint8_t *item = InternalTXTRecordSearch(txtRec->datalen, txtRec->buffer, key, &keylen);
+	if (!item) return(kDNSServiceErr_NoSuchKey);
+	itemlen   = (unsigned long)(1 + item[0]);
+	remainder = (unsigned long)((txtRec->buffer + txtRec->datalen) - (item + itemlen));
+	// Use memmove because memcpy behaviour is undefined for overlapping regions
+	memmove(item, item + itemlen, remainder);
+	txtRec->datalen -= itemlen;
+	return(kDNSServiceErr_NoError);
+	}
+
+uint16_t DNSSD_API TXTRecordGetLength  (const TXTRecordRef *txtRecord) { return(txtRec->datalen); }
+const void * DNSSD_API TXTRecordGetBytesPtr(const TXTRecordRef *txtRecord) { return(txtRec->buffer); }
+
+/*********************************************************************************************
+ *
+ *   TXT Record Parsing Functions
+ *
+ *********************************************************************************************/
+
+int DNSSD_API TXTRecordContainsKey
+	(
+	uint16_t         txtLen,
+	const void       *txtRecord,
+	const char       *key
+	)
+	{
+	unsigned long keylen;
+	return (InternalTXTRecordSearch(txtLen, txtRecord, key, &keylen) ? 1 : 0);
+	}
+
+const void * DNSSD_API TXTRecordGetValuePtr
+	(
+	uint16_t         txtLen,
+	const void       *txtRecord,
+	const char       *key,
+	uint8_t          *valueLen
+	)
+	{
+	unsigned long keylen;
+	uint8_t *item = InternalTXTRecordSearch(txtLen, txtRecord, key, &keylen);
+	if (!item || item[0] <= keylen) return(NULL);	// If key not found, or found with no value, return NULL
+	*valueLen = (uint8_t)(item[0] - (keylen + 1));
+	return (item + 1 + keylen + 1);
+	}
+
+uint16_t DNSSD_API TXTRecordGetCount
+	(
+	uint16_t         txtLen,
+	const void       *txtRecord
+	)
+	{
+	uint16_t count = 0;
+	uint8_t *p = (uint8_t*)txtRecord;
+	uint8_t *e = p + txtLen;
+	while (p<e) { p += 1 + p[0]; count++; }
+	return((p>e) ? (uint16_t)0 : count);
+	}
+
+DNSServiceErrorType DNSSD_API TXTRecordGetItemAtIndex
+	(
+	uint16_t         txtLen,
+	const void       *txtRecord,
+	uint16_t         itemIndex,
+	uint16_t         keyBufLen,
+	char             *key,
+	uint8_t          *valueLen,
+	const void       **value
+	)
+	{
+	uint16_t count = 0;
+	uint8_t *p = (uint8_t*)txtRecord;
+	uint8_t *e = p + txtLen;
+	while (p<e && count<itemIndex) { p += 1 + p[0]; count++; }	// Find requested item
+	if (p<e && p + 1 + p[0] <= e)	// If valid
+		{
+		uint8_t *x = p+1;
+		unsigned long len = 0;
+		e = p + 1 + p[0];
+		while (x+len<e && x[len] != '=') len++;
+		if (len >= keyBufLen) return(kDNSServiceErr_NoMemory);
+		memcpy(key, x, len);
+		key[len] = 0;
+		if (x+len<e)		// If we found '='
+			{
+			*value = x + len + 1;
+			*valueLen = (uint8_t)(p[0] - (len + 1));
+			}
+		else
+			{
+			*value = NULL;
+			*valueLen = 0;
+			}
+		return(kDNSServiceErr_NoError);
+		}
+	return(kDNSServiceErr_Invalid);
+	}
+
+/*********************************************************************************************
+ *
+ *   SCCS-compatible version string
+ *
+ *********************************************************************************************/
+
+// For convenience when using the "strings" command, this is the last thing in the file
+
+// Note: The C preprocessor stringify operator ('#') makes a string from its argument, without macro expansion
+// e.g. If "version" is #define'd to be "4", then STRINGIFY_AWE(version) will return the string "version", not "4"
+// To expand "version" to its value before making the string, use STRINGIFY(version) instead
+#define STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s) #s
+#define STRINGIFY(s) STRINGIFY_ARGUMENT_WITHOUT_EXPANSION(s)
+
+// NOT static -- otherwise the compiler may optimize it out
+// The "@(#) " pattern is a special prefix the "what" command looks for
+#ifndef __ANDROID__
+const char VersionString_SCCS_libdnssd[] = "@(#) libdns_sd " STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ")";
+#endif
diff --git a/mdnsresponder/mDNSShared/dnssd_clientshim.c b/mdnsresponder/mDNSShared/dnssd_clientshim.c
new file mode 100644
index 0000000..0690800
--- /dev/null
+++ b/mdnsresponder/mDNSShared/dnssd_clientshim.c
@@ -0,0 +1,802 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
+ * 
+ * Licensed 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.
+
+ * This file defines a simple shim layer between a client calling the "/usr/include/dns_sd.h" APIs
+ * and an implementation of mDNSCore ("mDNSEmbeddedAPI.h" APIs) in the same address space.
+ * When the client calls a dns_sd.h function, the shim calls the corresponding mDNSEmbeddedAPI.h
+ * function, and when mDNSCore calls the shim's callback, we call through to the client's callback.
+ * The shim is responsible for two main things:
+ * - converting string parameters between C string format and native DNS format,
+ * - and for allocating and freeing memory.
+ */
+
+#include "dns_sd.h"				// Defines the interface to the client layer above
+#include "mDNSEmbeddedAPI.h"		// The interface we're building on top of
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+extern mDNS mDNSStorage;		// We need to pass the address of this storage to the lower-layer functions
+
+#if MDNS_BUILDINGSHAREDLIBRARY || MDNS_BUILDINGSTUBLIBRARY
+#pragma export on
+#endif
+
+//*************************************************************************************************************
+// General Utility Functions
+
+// All mDNS_DirectOP structures start with the pointer to the type-specific disposal function.
+// Optional type-specific data follows these three fields
+// When the client starts an operation, we return the address of the corresponding mDNS_DirectOP
+// as the DNSServiceRef for the operation
+// We stash the value in core context fields so we can get it back to recover our state in our callbacks,
+// and pass it though to the client for it to recover its state
+
+typedef struct mDNS_DirectOP_struct mDNS_DirectOP;
+typedef void mDNS_DirectOP_Dispose(mDNS_DirectOP *op);
+struct mDNS_DirectOP_struct
+	{
+	mDNS_DirectOP_Dispose  *disposefn;
+	};
+
+typedef struct
+	{
+	mDNS_DirectOP_Dispose  *disposefn;
+	DNSServiceRegisterReply callback;
+	void                   *context;
+	mDNSBool                autoname;		// Set if this name is tied to the Computer Name
+	mDNSBool                autorename;		// Set if we just got a name conflict and now need to automatically pick a new name
+	domainlabel             name;
+	domainname              host;
+	ServiceRecordSet        s;
+	} mDNS_DirectOP_Register;
+
+typedef struct
+	{
+	mDNS_DirectOP_Dispose  *disposefn;
+	DNSServiceBrowseReply   callback;
+	void                   *context;
+	DNSQuestion             q;
+	} mDNS_DirectOP_Browse;
+
+typedef struct
+	{
+	mDNS_DirectOP_Dispose        *disposefn;
+	DNSServiceRef                aQuery;
+	DNSServiceGetAddrInfoReply   callback;
+  	void                         *context;
+	} mDNS_DirectOP_GetAddrInfo;
+
+typedef struct
+	{
+	mDNS_DirectOP_Dispose  *disposefn;
+	DNSServiceResolveReply  callback;
+	void                   *context;
+	const ResourceRecord   *SRV;
+	const ResourceRecord   *TXT;
+	DNSQuestion             qSRV;
+	DNSQuestion             qTXT;
+	} mDNS_DirectOP_Resolve;
+
+typedef struct
+	{
+	mDNS_DirectOP_Dispose      *disposefn;
+	DNSServiceQueryRecordReply  callback;
+	void                       *context;
+	DNSQuestion                 q;
+	} mDNS_DirectOP_QueryRecord;
+
+int DNSServiceRefSockFD(DNSServiceRef sdRef)
+	{
+	(void)sdRef;	// Unused
+	return(0);
+	}
+
+DNSServiceErrorType DNSServiceProcessResult(DNSServiceRef sdRef)
+	{
+	(void)sdRef;	// Unused
+	return(kDNSServiceErr_NoError);
+	}
+
+void DNSServiceRefDeallocate(DNSServiceRef sdRef)
+	{
+	mDNS_DirectOP *op = (mDNS_DirectOP *)sdRef;
+	//LogMsg("DNSServiceRefDeallocate");
+	op->disposefn(op);
+	}
+
+//*************************************************************************************************************
+// Domain Enumeration
+
+// Not yet implemented, so don't include in stub library
+// We DO include it in the actual Extension, so that if a later client compiled to use this
+// is run against this Extension, it will get a reasonable error code instead of just
+// failing to launch (Strong Link) or calling an unresolved symbol and crashing (Weak Link)
+#if !MDNS_BUILDINGSTUBLIBRARY
+DNSServiceErrorType DNSServiceEnumerateDomains
+	(
+	DNSServiceRef                       *sdRef,
+	DNSServiceFlags                     flags,
+	uint32_t                            interfaceIndex,
+	DNSServiceDomainEnumReply           callback,
+	void                                *context  /* may be NULL */
+	)
+	{
+	(void)sdRef;			// Unused
+	(void)flags;			// Unused
+	(void)interfaceIndex;	// Unused
+	(void)callback;			// Unused
+	(void)context;			// Unused
+	return(kDNSServiceErr_Unsupported);
+	}
+#endif
+
+//*************************************************************************************************************
+// Register Service
+
+mDNSlocal void FreeDNSServiceRegistration(mDNS_DirectOP_Register *x)
+	{
+	while (x->s.Extras)
+		{
+		ExtraResourceRecord *extras = x->s.Extras;
+		x->s.Extras = x->s.Extras->next;
+		if (extras->r.resrec.rdata != &extras->r.rdatastorage)
+			mDNSPlatformMemFree(extras->r.resrec.rdata);
+		mDNSPlatformMemFree(extras);
+		}
+
+	if (x->s.RR_TXT.resrec.rdata != &x->s.RR_TXT.rdatastorage)
+			mDNSPlatformMemFree(x->s.RR_TXT.resrec.rdata);
+
+	if (x->s.SubTypes) mDNSPlatformMemFree(x->s.SubTypes);
+	
+	mDNSPlatformMemFree(x);
+	}
+
+static void DNSServiceRegisterDispose(mDNS_DirectOP *op)
+	{
+	mDNS_DirectOP_Register *x = (mDNS_DirectOP_Register*)op;
+	x->autorename = mDNSfalse;
+	// If mDNS_DeregisterService() returns mStatus_NoError, that means that the service was found in the list,
+	// is sending its goodbye packet, and we'll get an mStatus_MemFree message when we can free the memory.
+	// If mDNS_DeregisterService() returns an error, it means that the service had already been removed from
+	// the list, so we should go ahead and free the memory right now
+	if (mDNS_DeregisterService(&mDNSStorage, &x->s) != mStatus_NoError)
+		FreeDNSServiceRegistration(x);
+	}
+
+mDNSlocal void RegCallback(mDNS *const m, ServiceRecordSet *const sr, mStatus result)
+	{
+	mDNS_DirectOP_Register *x = (mDNS_DirectOP_Register*)sr->ServiceContext;
+
+    domainlabel name;
+    domainname type, dom;
+	char namestr[MAX_DOMAIN_LABEL+1];		// Unescaped name: up to 63 bytes plus C-string terminating NULL.
+	char typestr[MAX_ESCAPED_DOMAIN_NAME];
+	char domstr [MAX_ESCAPED_DOMAIN_NAME];
+    if (!DeconstructServiceName(sr->RR_SRV.resrec.name, &name, &type, &dom)) return;
+    if (!ConvertDomainLabelToCString_unescaped(&name, namestr)) return;
+    if (!ConvertDomainNameToCString(&type, typestr)) return;
+    if (!ConvertDomainNameToCString(&dom, domstr)) return;
+
+	if (result == mStatus_NoError)
+		{
+		if (x->callback)
+			x->callback((DNSServiceRef)x, 0, result, namestr, typestr, domstr, x->context);
+		}
+	else if (result == mStatus_NameConflict)
+		{
+			if (x->autoname) mDNS_RenameAndReregisterService(m, sr, mDNSNULL);
+			else if (x->autorename) {
+				IncrementLabelSuffix(&x->name, mDNStrue);
+				mDNS_RenameAndReregisterService(m, &x->s, &x->name);
+			}
+			else if (x->callback)
+				x->callback((DNSServiceRef)x, 0, result, namestr, typestr, domstr, x->context);
+		}
+	else if (result == mStatus_MemFree)
+		{
+		if (x->autorename)
+			{
+			x->autorename = mDNSfalse;
+			x->name = mDNSStorage.nicelabel;
+			mDNS_RenameAndReregisterService(m, &x->s, &x->name);
+			}
+		else
+			FreeDNSServiceRegistration(x);
+		}
+	}
+
+DNSServiceErrorType DNSServiceRegister
+	(
+	DNSServiceRef                       *sdRef,
+	DNSServiceFlags                     flags,
+	uint32_t                            interfaceIndex,
+	const char                          *name,         /* may be NULL */
+	const char                          *regtype,
+	const char                          *domain,       /* may be NULL */
+	const char                          *host,         /* may be NULL */
+	uint16_t                            notAnIntPort,
+	uint16_t                            txtLen,
+	const void                          *txtRecord,    /* may be NULL */
+	DNSServiceRegisterReply             callback,      /* may be NULL */
+	void                                *context       /* may be NULL */
+	)
+	{
+	mStatus err = mStatus_NoError;
+	const char *errormsg = "Unknown";
+	domainlabel n;
+	domainname t, d, h, srv;
+	mDNSIPPort port;
+	unsigned int size = sizeof(RDataBody);
+	AuthRecord *SubTypes = mDNSNULL;
+	mDNSu32 NumSubTypes = 0;
+	mDNS_DirectOP_Register *x;
+	(void)flags;			// Unused
+	(void)interfaceIndex;	// Unused
+
+	// Check parameters
+	if (!name) name = "";
+	if (!name[0]) n = mDNSStorage.nicelabel;
+	else if (!MakeDomainLabelFromLiteralString(&n, name))                              { errormsg = "Bad Instance Name"; goto badparam; }
+	if (!regtype || !*regtype || !MakeDomainNameFromDNSNameString(&t, regtype))        { errormsg = "Bad Service Type";  goto badparam; }
+	if (!MakeDomainNameFromDNSNameString(&d, (domain && *domain) ? domain : "local.")) { errormsg = "Bad Domain";        goto badparam; }
+	if (!MakeDomainNameFromDNSNameString(&h, (host   && *host  ) ? host   : ""))       { errormsg = "Bad Target Host";   goto badparam; }
+	if (!ConstructServiceName(&srv, &n, &t, &d))                                       { errormsg = "Bad Name";          goto badparam; }
+	port.NotAnInteger = notAnIntPort;
+
+	// Allocate memory, and handle failure
+	if (size < txtLen)
+		size = txtLen;
+	x = (mDNS_DirectOP_Register *)mDNSPlatformMemAllocate(sizeof(*x) - sizeof(RDataBody) + size);
+	if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
+
+	// Set up object
+	x->disposefn = DNSServiceRegisterDispose;
+	x->callback  = callback;
+	x->context   = context;
+	x->autoname = (!name[0]);
+	x->autorename = !(flags & kDNSServiceFlagsNoAutoRename);
+	x->name = n;
+	x->host = h;
+
+	// Do the operation
+	err = mDNS_RegisterService(&mDNSStorage, &x->s,
+		&x->name, &t, &d,		// Name, type, domain
+		&x->host, port,			// Host and port
+		txtRecord, txtLen,		// TXT data, length
+		SubTypes, NumSubTypes,	// Subtypes
+		mDNSInterface_Any,		// Interface ID
+		RegCallback, x, 0);		// Callback, context, flags
+	if (err) { mDNSPlatformMemFree(x); errormsg = "mDNS_RegisterService"; goto fail; }
+
+	// Succeeded: Wrap up and return
+	*sdRef = (DNSServiceRef)x;
+	return(mStatus_NoError);
+
+badparam:
+	err = mStatus_BadParamErr;
+fail:
+	LogMsg("DNSServiceBrowse(\"%s\", \"%s\") failed: %s (%ld)", regtype, domain, errormsg, err);
+	return(err);
+	}
+
+//*************************************************************************************************************
+// Add / Update / Remove records from existing Registration
+
+// Not yet implemented, so don't include in stub library
+// We DO include it in the actual Extension, so that if a later client compiled to use this
+// is run against this Extension, it will get a reasonable error code instead of just
+// failing to launch (Strong Link) or calling an unresolved symbol and crashing (Weak Link)
+#if !MDNS_BUILDINGSTUBLIBRARY
+DNSServiceErrorType DNSServiceAddRecord
+	(
+	DNSServiceRef                       sdRef,
+	DNSRecordRef                        *RecordRef,
+	DNSServiceFlags                     flags,
+	uint16_t                            rrtype,
+	uint16_t                            rdlen,
+	const void                          *rdata,
+	uint32_t                            ttl
+	)
+	{
+	(void)sdRef;		// Unused
+	(void)RecordRef;	// Unused
+	(void)flags;		// Unused
+	(void)rrtype;		// Unused
+	(void)rdlen;		// Unused
+	(void)rdata;		// Unused
+	(void)ttl;			// Unused
+	return(kDNSServiceErr_Unsupported);
+	}
+
+DNSServiceErrorType DNSServiceUpdateRecord
+	(
+	DNSServiceRef                       sdRef,
+	DNSRecordRef                        RecordRef,     /* may be NULL */
+	DNSServiceFlags                     flags,
+	uint16_t                            rdlen,
+	const void                          *rdata,
+	uint32_t                            ttl
+	)
+	{
+	(void)sdRef;		// Unused
+	(void)RecordRef;	// Unused
+	(void)flags;		// Unused
+	(void)rdlen;		// Unused
+	(void)rdata;		// Unused
+	(void)ttl;			// Unused
+	return(kDNSServiceErr_Unsupported);
+	}
+
+DNSServiceErrorType DNSServiceRemoveRecord
+	(
+	DNSServiceRef                 sdRef,
+	DNSRecordRef                  RecordRef,
+	DNSServiceFlags               flags
+	)
+	{
+	(void)sdRef;		// Unused
+	(void)RecordRef;	// Unused
+	(void)flags;		// Unused
+	return(kDNSServiceErr_Unsupported);
+	}
+#endif
+
+//*************************************************************************************************************
+// Browse for services
+
+static void DNSServiceBrowseDispose(mDNS_DirectOP *op)
+	{
+	mDNS_DirectOP_Browse *x = (mDNS_DirectOP_Browse*)op;
+	//LogMsg("DNSServiceBrowseDispose");
+	mDNS_StopBrowse(&mDNSStorage, &x->q);
+	mDNSPlatformMemFree(x);
+	}
+
+mDNSlocal void FoundInstance(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
+	{
+	DNSServiceFlags flags = AddRecord ? kDNSServiceFlagsAdd : (DNSServiceFlags)0;
+	domainlabel name;
+	domainname type, domain;
+	char cname[MAX_DOMAIN_LABEL+1];			// Unescaped name: up to 63 bytes plus C-string terminating NULL.
+	char ctype[MAX_ESCAPED_DOMAIN_NAME];
+	char cdom [MAX_ESCAPED_DOMAIN_NAME];
+	mDNS_DirectOP_Browse *x = (mDNS_DirectOP_Browse*)question->QuestionContext;
+	(void)m;		// Unused
+	
+	if (answer->rrtype != kDNSType_PTR)
+		{ LogMsg("FoundInstance: Should not be called with rrtype %d (not a PTR record)", answer->rrtype); return; }
+	
+	if (!DeconstructServiceName(&answer->rdata->u.name, &name, &type, &domain))
+		{
+		LogMsg("FoundInstance: %##s PTR %##s received from network is not valid DNS-SD service pointer",
+			answer->name->c, answer->rdata->u.name.c);
+		return;
+		}
+
+	ConvertDomainLabelToCString_unescaped(&name, cname);
+	ConvertDomainNameToCString(&type, ctype);
+	ConvertDomainNameToCString(&domain, cdom);
+	if (x->callback)
+		x->callback((DNSServiceRef)x, flags, 0, 0, cname, ctype, cdom, x->context);
+	}
+
+DNSServiceErrorType DNSServiceBrowse
+	(
+	DNSServiceRef                       *sdRef,
+	DNSServiceFlags                     flags,
+	uint32_t                            interfaceIndex,
+	const char                          *regtype,
+	const char                          *domain,    /* may be NULL */
+	DNSServiceBrowseReply               callback,
+	void                                *context    /* may be NULL */
+	)
+	{
+	mStatus err = mStatus_NoError;
+	const char *errormsg = "Unknown";
+	domainname t, d;
+	mDNS_DirectOP_Browse *x;
+	(void)flags;			// Unused
+	(void)interfaceIndex;	// Unused
+
+	// Check parameters
+	if (!regtype[0] || !MakeDomainNameFromDNSNameString(&t, regtype))      { errormsg = "Illegal regtype"; goto badparam; }
+	if (!MakeDomainNameFromDNSNameString(&d, *domain ? domain : "local.")) { errormsg = "Illegal domain";  goto badparam; }
+
+	// Allocate memory, and handle failure
+	x = (mDNS_DirectOP_Browse *)mDNSPlatformMemAllocate(sizeof(*x));
+	if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
+
+	// Set up object
+	x->disposefn = DNSServiceBrowseDispose;
+	x->callback  = callback;
+	x->context   = context;
+	x->q.QuestionContext = x;
+
+	// Do the operation
+	err = mDNS_StartBrowse(&mDNSStorage, &x->q, &t, &d, mDNSInterface_Any, (flags & kDNSServiceFlagsForceMulticast) != 0, FoundInstance, x);
+	if (err) { mDNSPlatformMemFree(x); errormsg = "mDNS_StartBrowse"; goto fail; }
+
+	// Succeeded: Wrap up and return
+	*sdRef = (DNSServiceRef)x;
+	return(mStatus_NoError);
+
+badparam:
+	err = mStatus_BadParamErr;
+fail:
+	LogMsg("DNSServiceBrowse(\"%s\", \"%s\") failed: %s (%ld)", regtype, domain, errormsg, err);
+	return(err);
+	}
+
+//*************************************************************************************************************
+// Resolve Service Info
+
+static void DNSServiceResolveDispose(mDNS_DirectOP *op)
+	{
+	mDNS_DirectOP_Resolve *x = (mDNS_DirectOP_Resolve*)op;
+	if (x->qSRV.ThisQInterval >= 0) mDNS_StopQuery(&mDNSStorage, &x->qSRV);
+	if (x->qTXT.ThisQInterval >= 0) mDNS_StopQuery(&mDNSStorage, &x->qTXT);
+	mDNSPlatformMemFree(x);
+	}
+
+mDNSlocal void FoundServiceInfo(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
+	{
+	mDNS_DirectOP_Resolve *x = (mDNS_DirectOP_Resolve*)question->QuestionContext;
+	(void)m;	// Unused
+	if (!AddRecord)
+		{
+		if (answer->rrtype == kDNSType_SRV && x->SRV == answer) x->SRV = mDNSNULL;
+		if (answer->rrtype == kDNSType_TXT && x->TXT == answer) x->TXT = mDNSNULL;
+		}
+	else
+		{
+		if (answer->rrtype == kDNSType_SRV) x->SRV = answer;
+		if (answer->rrtype == kDNSType_TXT) x->TXT = answer;
+		if (x->SRV && x->TXT && x->callback)
+			{
+			char fullname[MAX_ESCAPED_DOMAIN_NAME], targethost[MAX_ESCAPED_DOMAIN_NAME];
+		    ConvertDomainNameToCString(answer->name, fullname);
+		    ConvertDomainNameToCString(&x->SRV->rdata->u.srv.target, targethost);
+			x->callback((DNSServiceRef)x, 0, 0, kDNSServiceErr_NoError, fullname, targethost,
+				x->SRV->rdata->u.srv.port.NotAnInteger, x->TXT->rdlength, (unsigned char*)x->TXT->rdata->u.txt.c, x->context);
+			}
+		}
+	}
+
+DNSServiceErrorType DNSServiceResolve
+	(
+	DNSServiceRef                       *sdRef,
+	DNSServiceFlags                     flags,
+	uint32_t                            interfaceIndex,
+	const char                          *name,
+	const char                          *regtype,
+	const char                          *domain,
+	DNSServiceResolveReply              callback,
+	void                                *context  /* may be NULL */
+	)
+	{
+	mStatus err = mStatus_NoError;
+	const char *errormsg = "Unknown";
+	domainlabel n;
+	domainname t, d, srv;
+	mDNS_DirectOP_Resolve *x;
+
+	(void)flags;			// Unused
+	(void)interfaceIndex;	// Unused
+
+	// Check parameters
+	if (!name[0]    || !MakeDomainLabelFromLiteralString(&n, name  )) { errormsg = "Bad Instance Name"; goto badparam; }
+	if (!regtype[0] || !MakeDomainNameFromDNSNameString(&t, regtype)) { errormsg = "Bad Service Type";  goto badparam; }
+	if (!domain[0]  || !MakeDomainNameFromDNSNameString(&d, domain )) { errormsg = "Bad Domain";        goto badparam; }
+	if (!ConstructServiceName(&srv, &n, &t, &d))                      { errormsg = "Bad Name";          goto badparam; }
+
+	// Allocate memory, and handle failure
+	x = (mDNS_DirectOP_Resolve *)mDNSPlatformMemAllocate(sizeof(*x));
+	if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
+
+	// Set up object
+	x->disposefn = DNSServiceResolveDispose;
+	x->callback  = callback;
+	x->context   = context;
+	x->SRV       = mDNSNULL;
+	x->TXT       = mDNSNULL;
+
+	x->qSRV.ThisQInterval       = -1;		// So that DNSServiceResolveDispose() knows whether to cancel this question
+	x->qSRV.InterfaceID         = mDNSInterface_Any;
+	x->qSRV.Target              = zeroAddr;
+	AssignDomainName(&x->qSRV.qname, &srv);
+	x->qSRV.qtype               = kDNSType_SRV;
+	x->qSRV.qclass              = kDNSClass_IN;
+	x->qSRV.LongLived           = mDNSfalse;
+	x->qSRV.ExpectUnique        = mDNStrue;
+	x->qSRV.ForceMCast          = mDNSfalse;
+	x->qSRV.ReturnIntermed      = mDNSfalse;
+	x->qSRV.SuppressUnusable    = mDNSfalse;
+	x->qSRV.SearchListIndex     = 0;
+	x->qSRV.AppendSearchDomains = 0;
+	x->qSRV.RetryWithSearchDomains = mDNSfalse;
+	x->qSRV.TimeoutQuestion     = 0;
+	x->qSRV.WakeOnResolve       = 0;
+	x->qSRV.qnameOrig           = mDNSNULL;
+	x->qSRV.QuestionCallback    = FoundServiceInfo;
+	x->qSRV.QuestionContext     = x;
+
+	x->qTXT.ThisQInterval       = -1;		// So that DNSServiceResolveDispose() knows whether to cancel this question
+	x->qTXT.InterfaceID         = mDNSInterface_Any;
+	x->qTXT.Target              = zeroAddr;
+	AssignDomainName(&x->qTXT.qname, &srv);
+	x->qTXT.qtype               = kDNSType_TXT;
+	x->qTXT.qclass              = kDNSClass_IN;
+	x->qTXT.LongLived           = mDNSfalse;
+	x->qTXT.ExpectUnique        = mDNStrue;
+	x->qTXT.ForceMCast          = mDNSfalse;
+	x->qTXT.ReturnIntermed      = mDNSfalse;
+	x->qTXT.SuppressUnusable    = mDNSfalse;
+	x->qTXT.SearchListIndex     = 0;
+	x->qTXT.AppendSearchDomains = 0;
+	x->qTXT.RetryWithSearchDomains = mDNSfalse;
+	x->qTXT.TimeoutQuestion     = 0;
+	x->qTXT.WakeOnResolve       = 0;
+	x->qTXT.qnameOrig           = mDNSNULL;
+	x->qTXT.QuestionCallback    = FoundServiceInfo;
+	x->qTXT.QuestionContext     = x;
+
+	err = mDNS_StartQuery(&mDNSStorage, &x->qSRV);
+	if (err) { DNSServiceResolveDispose((mDNS_DirectOP*)x); errormsg = "mDNS_StartQuery qSRV"; goto fail; }
+	err = mDNS_StartQuery(&mDNSStorage, &x->qTXT);
+	if (err) { DNSServiceResolveDispose((mDNS_DirectOP*)x); errormsg = "mDNS_StartQuery qTXT"; goto fail; }
+
+	// Succeeded: Wrap up and return
+	*sdRef = (DNSServiceRef)x;
+	return(mStatus_NoError);
+
+badparam:
+	err = mStatus_BadParamErr;
+fail:
+	LogMsg("DNSServiceResolve(\"%s\", \"%s\", \"%s\") failed: %s (%ld)", name, regtype, domain, errormsg, err);
+	return(err);
+	}
+
+//*************************************************************************************************************
+// Connection-oriented calls
+
+// Not yet implemented, so don't include in stub library
+// We DO include it in the actual Extension, so that if a later client compiled to use this
+// is run against this Extension, it will get a reasonable error code instead of just
+// failing to launch (Strong Link) or calling an unresolved symbol and crashing (Weak Link)
+#if !MDNS_BUILDINGSTUBLIBRARY
+DNSServiceErrorType DNSServiceCreateConnection(DNSServiceRef *sdRef)
+	{
+	(void)sdRef;	// Unused
+	return(kDNSServiceErr_Unsupported);
+	}
+
+DNSServiceErrorType DNSServiceRegisterRecord
+	(
+	DNSServiceRef                       sdRef,
+	DNSRecordRef                        *RecordRef,
+	DNSServiceFlags                     flags,
+	uint32_t                            interfaceIndex,
+	const char                          *fullname,
+	uint16_t                            rrtype,
+	uint16_t                            rrclass,
+	uint16_t                            rdlen,
+	const void                          *rdata,
+	uint32_t                            ttl,
+	DNSServiceRegisterRecordReply       callback,
+	void                                *context    /* may be NULL */
+	)
+	{
+	(void)sdRef;			// Unused
+	(void)RecordRef;		// Unused
+	(void)flags;			// Unused
+	(void)interfaceIndex;	// Unused
+	(void)fullname;			// Unused
+	(void)rrtype;			// Unused
+	(void)rrclass;			// Unused
+	(void)rdlen;			// Unused
+	(void)rdata;			// Unused
+	(void)ttl;				// Unused
+	(void)callback;			// Unused
+	(void)context;			// Unused
+	return(kDNSServiceErr_Unsupported);
+	}
+#endif
+
+//*************************************************************************************************************
+// DNSServiceQueryRecord
+
+static void DNSServiceQueryRecordDispose(mDNS_DirectOP *op)
+	{
+	mDNS_DirectOP_QueryRecord *x = (mDNS_DirectOP_QueryRecord*)op;
+	if (x->q.ThisQInterval >= 0) mDNS_StopQuery(&mDNSStorage, &x->q);
+	mDNSPlatformMemFree(x);
+	}
+
+mDNSlocal void DNSServiceQueryRecordResponse(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
+	{
+	mDNS_DirectOP_QueryRecord *x = (mDNS_DirectOP_QueryRecord*)question->QuestionContext;
+	char fullname[MAX_ESCAPED_DOMAIN_NAME];
+	(void)m;	// Unused
+	ConvertDomainNameToCString(answer->name, fullname);
+	x->callback((DNSServiceRef)x, AddRecord ? kDNSServiceFlagsAdd : (DNSServiceFlags)0, 0, kDNSServiceErr_NoError,
+		fullname, answer->rrtype, answer->rrclass, answer->rdlength, answer->rdata->u.data, answer->rroriginalttl, x->context);
+	}
+
+DNSServiceErrorType DNSServiceQueryRecord
+	(
+	DNSServiceRef                       *sdRef,
+	DNSServiceFlags                     flags,
+	uint32_t                            interfaceIndex,
+	const char                          *fullname,
+	uint16_t                            rrtype,
+	uint16_t                            rrclass,
+	DNSServiceQueryRecordReply          callback,
+	void                                *context  /* may be NULL */
+	)
+	{
+	mStatus err = mStatus_NoError;
+	const char *errormsg = "Unknown";
+	mDNS_DirectOP_QueryRecord *x;
+
+	(void)flags;			// Unused
+	(void)interfaceIndex;	// Unused
+
+	// Allocate memory, and handle failure
+	x = (mDNS_DirectOP_QueryRecord *)mDNSPlatformMemAllocate(sizeof(*x));
+	if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
+
+	// Set up object
+	x->disposefn = DNSServiceQueryRecordDispose;
+	x->callback  = callback;
+	x->context   = context;
+
+	x->q.ThisQInterval       = -1;		// So that DNSServiceResolveDispose() knows whether to cancel this question
+	x->q.InterfaceID         = mDNSInterface_Any;
+	x->q.Target              = zeroAddr;
+	MakeDomainNameFromDNSNameString(&x->q.qname, fullname);
+	x->q.qtype               = rrtype;
+	x->q.qclass              = rrclass;
+	x->q.LongLived           = (flags & kDNSServiceFlagsLongLivedQuery) != 0;
+	x->q.ExpectUnique        = mDNSfalse;
+	x->q.ForceMCast          = (flags & kDNSServiceFlagsForceMulticast) != 0;
+	x->q.ReturnIntermed      = (flags & kDNSServiceFlagsReturnIntermediates) != 0;
+	x->q.SuppressUnusable     = (flags & kDNSServiceFlagsSuppressUnusable) != 0;
+	x->q.SearchListIndex     = 0;
+	x->q.AppendSearchDomains = 0;
+	x->q.RetryWithSearchDomains = mDNSfalse;
+	x->q.WakeOnResolve       = 0;
+	x->q.qnameOrig           = mDNSNULL;
+	x->q.QuestionCallback    = DNSServiceQueryRecordResponse;
+	x->q.QuestionContext     = x;
+
+	err = mDNS_StartQuery(&mDNSStorage, &x->q);
+	if (err) { DNSServiceResolveDispose((mDNS_DirectOP*)x); errormsg = "mDNS_StartQuery"; goto fail; }
+
+	// Succeeded: Wrap up and return
+	*sdRef = (DNSServiceRef)x;
+	return(mStatus_NoError);
+
+fail:
+	LogMsg("DNSServiceQueryRecord(\"%s\", %d, %d) failed: %s (%ld)", fullname, rrtype, rrclass, errormsg, err);
+	return(err);
+	}
+
+//*************************************************************************************************************
+// DNSServiceGetAddrInfo
+
+static void DNSServiceGetAddrInfoDispose(mDNS_DirectOP *op)
+	{
+	mDNS_DirectOP_GetAddrInfo *x = (mDNS_DirectOP_GetAddrInfo*)op;
+	if (x->aQuery) DNSServiceRefDeallocate(x->aQuery);
+	mDNSPlatformMemFree(x);
+	}
+
+static void DNSSD_API DNSServiceGetAddrInfoResponse(
+	DNSServiceRef		inRef,
+	DNSServiceFlags		inFlags,
+	uint32_t			inInterfaceIndex,
+	DNSServiceErrorType	inErrorCode,
+	const char *		inFullName,
+	uint16_t			inRRType,
+	uint16_t			inRRClass,
+	uint16_t			inRDLen,
+	const void *		inRData,
+	uint32_t			inTTL,
+	void *				inContext )
+	{
+	mDNS_DirectOP_GetAddrInfo *		x = (mDNS_DirectOP_GetAddrInfo*)inContext;
+	struct sockaddr_in				sa4;
+
+	mDNSPlatformMemZero(&sa4, sizeof(sa4));
+	if (inErrorCode == kDNSServiceErr_NoError && inRRType == kDNSServiceType_A)
+		{
+		sa4.sin_family = AF_INET;
+		mDNSPlatformMemCopy(&sa4.sin_addr.s_addr, inRData, 4);
+		}
+	
+	x->callback((DNSServiceRef)x, inFlags, inInterfaceIndex, inErrorCode, inFullName, 
+		(const struct sockaddr *) &sa4, inTTL, x->context);
+	}
+
+DNSServiceErrorType DNSSD_API DNSServiceGetAddrInfo(
+	DNSServiceRef *				outRef,
+	DNSServiceFlags				inFlags,
+	uint32_t					inInterfaceIndex,
+	DNSServiceProtocol			inProtocol,
+	const char *				inHostName,
+	DNSServiceGetAddrInfoReply	inCallback,
+	void *						inContext )
+	{
+	const char *					errormsg = "Unknown";
+	DNSServiceErrorType				err;
+	mDNS_DirectOP_GetAddrInfo *		x;
+	
+	// Allocate memory, and handle failure
+	x = (mDNS_DirectOP_GetAddrInfo *)mDNSPlatformMemAllocate(sizeof(*x));
+	if (!x) { err = mStatus_NoMemoryErr; errormsg = "No memory"; goto fail; }
+	
+	// Set up object
+	x->disposefn = DNSServiceGetAddrInfoDispose;
+	x->callback  = inCallback;
+	x->context   = inContext;
+	x->aQuery    = mDNSNULL;
+	
+	// Start the query.
+	// (It would probably be more efficient to code this using mDNS_StartQuery directly,
+	// instead of wrapping DNSServiceQueryRecord, which then unnecessarily allocates
+	// more memory and then just calls through to mDNS_StartQuery. -- SC June 2010)
+	err = DNSServiceQueryRecord(&x->aQuery, inFlags, inInterfaceIndex, inHostName, kDNSServiceType_A, 
+		kDNSServiceClass_IN, DNSServiceGetAddrInfoResponse, x);
+	if (err) { DNSServiceGetAddrInfoDispose((mDNS_DirectOP*)x); errormsg = "DNSServiceQueryRecord"; goto fail; }
+	
+	*outRef = (DNSServiceRef)x;
+	return(mStatus_NoError);
+	
+fail:
+	LogMsg("DNSServiceGetAddrInfo(\"%s\", %d) failed: %s (%ld)", inHostName, inProtocol, errormsg, err);
+	return(err);
+	}
+
+//*************************************************************************************************************
+// DNSServiceReconfirmRecord
+
+// Not yet implemented, so don't include in stub library
+// We DO include it in the actual Extension, so that if a later client compiled to use this
+// is run against this Extension, it will get a reasonable error code instead of just
+// failing to launch (Strong Link) or calling an unresolved symbol and crashing (Weak Link)
+#if !MDNS_BUILDINGSTUBLIBRARY
+DNSServiceErrorType DNSSD_API DNSServiceReconfirmRecord
+	(
+	DNSServiceFlags                    flags,
+	uint32_t                           interfaceIndex,
+	const char                         *fullname,
+	uint16_t                           rrtype,
+	uint16_t                           rrclass,
+	uint16_t                           rdlen,
+	const void                         *rdata
+	)
+	{
+	(void)flags;			// Unused
+	(void)interfaceIndex;	// Unused
+	(void)fullname;			// Unused
+	(void)rrtype;			// Unused
+	(void)rrclass;			// Unused
+	(void)rdlen;			// Unused
+	(void)rdata;			// Unused
+	return(kDNSServiceErr_Unsupported);
+	}
+#endif
diff --git a/mdnsresponder/mDNSShared/dnssd_clientstub.c b/mdnsresponder/mDNSShared/dnssd_clientstub.c
new file mode 100644
index 0000000..88f20eb
--- /dev/null
+++ b/mdnsresponder/mDNSShared/dnssd_clientstub.c
@@ -0,0 +1,1988 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2003-2004, Apple Computer, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of its
+ *     contributors may be used to endorse or promote products derived from this
+ *     software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+
+#include "dnssd_ipc.h"
+
+static int gDaemonErr = kDNSServiceErr_NoError;
+
+#if defined(_WIN32)
+
+	#define _SSIZE_T
+	#include <CommonServices.h>
+	#include <DebugServices.h>
+	#include <winsock2.h>
+	#include <ws2tcpip.h>
+	#include <windows.h>
+	#include <stdarg.h>
+	
+	#define sockaddr_mdns sockaddr_in
+	#define AF_MDNS AF_INET
+	
+	// Disable warning: "'type cast' : from data pointer 'void *' to function pointer"
+	#pragma warning(disable:4055)
+	
+	// Disable warning: "nonstandard extension, function/data pointer conversion in expression"
+	#pragma warning(disable:4152)
+	
+	extern BOOL IsSystemServiceDisabled();
+	
+	#define sleep(X) Sleep((X) * 1000)
+	
+	static int g_initWinsock = 0;
+	#define LOG_WARNING kDebugLevelWarning
+	#define LOG_INFO kDebugLevelInfo
+	static void syslog( int priority, const char * message, ...)
+		{
+		va_list args;
+		int len;
+		char * buffer;
+		DWORD err = WSAGetLastError();
+		(void) priority;
+		va_start( args, message );
+		len = _vscprintf( message, args ) + 1;
+		buffer = malloc( len * sizeof(char) );
+		if ( buffer ) { vsprintf( buffer, message, args ); OutputDebugString( buffer ); free( buffer ); }
+		WSASetLastError( err );
+		}
+#else
+
+#ifndef __ANDROID__
+	#include <sys/fcntl.h>		// For O_RDWR etc.
+#else
+	#include <fcntl.h>
+	#define LOG_TAG "libmdns"
+#endif  // !__ANDROID__
+	#include <sys/time.h>
+	#include <sys/socket.h>
+	#include <syslog.h>
+	
+	#define sockaddr_mdns sockaddr_un
+	#define AF_MDNS AF_LOCAL
+
+#endif
+
+// <rdar://problem/4096913> Specifies how many times we'll try and connect to the server.
+
+#define DNSSD_CLIENT_MAXTRIES 4
+
+// Uncomment the line below to use the old error return mechanism of creating a temporary named socket (e.g. in /var/tmp)
+//#define USE_NAMED_ERROR_RETURN_SOCKET 1
+
+#define DNSSD_CLIENT_TIMEOUT 10  // In seconds
+
+#ifndef CTL_PATH_PREFIX
+#define CTL_PATH_PREFIX "/var/tmp/dnssd_result_socket."
+#endif
+
+typedef struct
+	{
+	ipc_msg_hdr         ipc_hdr;
+	DNSServiceFlags     cb_flags;
+	uint32_t            cb_interface;
+	DNSServiceErrorType cb_err;
+	} CallbackHeader;
+
+typedef struct _DNSServiceRef_t DNSServiceOp;
+typedef struct _DNSRecordRef_t DNSRecord;
+
+// client stub callback to process message from server and deliver results to client application
+typedef void (*ProcessReplyFn)(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *msg, const char *const end);
+
+#define ValidatorBits 0x12345678
+#define DNSServiceRefValid(X) (dnssd_SocketValid((X)->sockfd) && (((X)->sockfd ^ (X)->validator) == ValidatorBits))
+
+// When using kDNSServiceFlagsShareConnection, there is one primary _DNSServiceOp_t, and zero or more subordinates
+// For the primary, the 'next' field points to the first subordinate, and its 'next' field points to the next, and so on.
+// For the primary, the 'primary' field is NULL; for subordinates the 'primary' field points back to the associated primary
+//
+// _DNS_SD_LIBDISPATCH is defined where libdispatch/GCD is available. This does not mean that the application will use the
+// DNSServiceSetDispatchQueue API. Hence any new code guarded with _DNS_SD_LIBDISPATCH should still be backwards compatible.
+struct _DNSServiceRef_t
+	{
+	DNSServiceOp     *next;				// For shared connection
+	DNSServiceOp     *primary;			// For shared connection
+	dnssd_sock_t      sockfd;			// Connected socket between client and daemon
+	dnssd_sock_t      validator;		// Used to detect memory corruption, double disposals, etc.
+	client_context_t  uid;				// For shared connection requests, each subordinate DNSServiceRef has its own ID,
+										// unique within the scope of the same shared parent DNSServiceRef
+	uint32_t          op;				// request_op_t or reply_op_t
+	uint32_t          max_index;		// Largest assigned record index - 0 if no additional records registered
+	uint32_t          logcounter;		// Counter used to control number of syslog messages we write
+	int              *moreptr;			// Set while DNSServiceProcessResult working on this particular DNSServiceRef
+	ProcessReplyFn    ProcessReply;		// Function pointer to the code to handle received messages
+	void             *AppCallback;		// Client callback function and context
+	void             *AppContext;
+	DNSRecord        *rec;
+#if _DNS_SD_LIBDISPATCH
+	dispatch_source_t disp_source;
+	dispatch_queue_t  disp_queue;
+#endif
+	};
+
+struct _DNSRecordRef_t
+	{
+	DNSRecord		*recnext;
+	void *AppContext;
+	DNSServiceRegisterRecordReply AppCallback;
+	DNSRecordRef recref;
+	uint32_t record_index;  // index is unique to the ServiceDiscoveryRef
+	DNSServiceOp *sdr;
+	};
+
+// Write len bytes. Return 0 on success, -1 on error
+static int write_all(dnssd_sock_t sd, char *buf, size_t len)
+	{
+	// Don't use "MSG_WAITALL"; it returns "Invalid argument" on some Linux versions; use an explicit while() loop instead.
+	//if (send(sd, buf, len, MSG_WAITALL) != len) return -1;
+	while (len)
+		{
+		ssize_t num_written = send(sd, buf, (long)len, 0);
+		if (num_written < 0 || (size_t)num_written > len)
+			{
+			// Should never happen. If it does, it indicates some OS bug,
+			// or that the mDNSResponder daemon crashed (which should never happen).
+			#if !defined(__ppc__) && defined(SO_ISDEFUNCT)
+			int defunct;
+			socklen_t dlen = sizeof (defunct);
+			if (getsockopt(sd, SOL_SOCKET, SO_ISDEFUNCT, &defunct, &dlen) < 0)
+				syslog(LOG_WARNING, "dnssd_clientstub write_all: SO_ISDEFUNCT failed %d %s", dnssd_errno, dnssd_strerror(dnssd_errno));
+			if (!defunct)
+				syslog(LOG_WARNING, "dnssd_clientstub write_all(%d) failed %ld/%ld %d %s", sd,
+					(long)num_written, (long)len,
+					(num_written < 0) ? dnssd_errno                 : 0,
+					(num_written < 0) ? dnssd_strerror(dnssd_errno) : "");
+			else
+				syslog(LOG_INFO, "dnssd_clientstub write_all(%d) DEFUNCT", sd);
+			#else
+			syslog(LOG_WARNING, "dnssd_clientstub write_all(%d) failed %ld/%ld %d %s", sd,
+				(long)num_written, (long)len,
+				(num_written < 0) ? dnssd_errno                 : 0,
+				(num_written < 0) ? dnssd_strerror(dnssd_errno) : "");
+			#endif
+			return -1;
+			}
+		buf += num_written;
+		len -= num_written;
+		}
+	return 0;
+	}
+
+enum { read_all_success = 0, read_all_fail = -1, read_all_wouldblock = -2 };
+
+// Read len bytes. Return 0 on success, read_all_fail on error, or read_all_wouldblock for
+static int read_all(dnssd_sock_t sd, char *buf, int len)
+	{
+	// Don't use "MSG_WAITALL"; it returns "Invalid argument" on some Linux versions; use an explicit while() loop instead.
+	//if (recv(sd, buf, len, MSG_WAITALL) != len) return -1;
+
+	while (len)
+		{
+		ssize_t num_read = recv(sd, buf, len, 0);
+		// It is valid to get an interrupted system call error e.g., somebody attaching
+		// in a debugger, retry without failing
+		if ((num_read < 0) && (errno == EINTR)) { syslog(LOG_INFO, "dnssd_clientstub read_all: EINTR continue"); continue; }
+		if ((num_read == 0) || (num_read < 0) || (num_read > len))
+			{
+			int printWarn = 0;
+			int defunct = 0;
+			// Should never happen. If it does, it indicates some OS bug,
+			// or that the mDNSResponder daemon crashed (which should never happen).
+#if defined(WIN32)
+			// <rdar://problem/7481776> Suppress logs for "A non-blocking socket operation
+			//                          could not be completed immediately"
+			if (WSAGetLastError() != WSAEWOULDBLOCK)
+				printWarn = 1;
+#endif
+#if !defined(__ppc__) && defined(SO_ISDEFUNCT)
+			{
+			socklen_t dlen = sizeof (defunct);
+			if (getsockopt(sd, SOL_SOCKET, SO_ISDEFUNCT, &defunct, &dlen) < 0)
+				syslog(LOG_WARNING, "dnssd_clientstub read_all: SO_ISDEFUNCT failed %d %s", dnssd_errno, dnssd_strerror(dnssd_errno));
+			}
+			if (!defunct)
+				printWarn = 1;
+#endif
+			if (printWarn)
+				syslog(LOG_WARNING, "dnssd_clientstub read_all(%d) failed %ld/%ld %d %s", sd,
+					(long)num_read, (long)len,
+					(num_read < 0) ? dnssd_errno                 : 0,
+					(num_read < 0) ? dnssd_strerror(dnssd_errno) : "");
+			else if (defunct)
+				syslog(LOG_INFO, "dnssd_clientstub read_all(%d) DEFUNCT", sd);
+			return (num_read < 0 && dnssd_errno == dnssd_EWOULDBLOCK) ? read_all_wouldblock : read_all_fail;
+			}
+		buf += num_read;
+		len -= num_read;
+		}
+	return read_all_success;
+	}
+
+// Returns 1 if more bytes remain to be read on socket descriptor sd, 0 otherwise
+static int more_bytes(dnssd_sock_t sd)
+	{
+	struct timeval tv = { 0, 0 };
+	fd_set readfds;
+	fd_set *fs;
+	int ret;
+
+	if (sd < FD_SETSIZE)
+		{
+		fs = &readfds;
+		FD_ZERO(fs);
+		}
+	else 
+		{
+		// Compute the number of integers needed for storing "sd". Internally fd_set is stored
+		// as an array of ints with one bit for each fd and hence we need to compute
+		// the number of ints needed rather than the number of bytes. If "sd" is 32, we need
+		// two ints and not just one.
+		int nfdbits = sizeof (int) * 8;
+		int nints = (sd/nfdbits) + 1;
+		fs = (fd_set *)calloc(nints, sizeof(int));
+		if (fs == NULL) { syslog(LOG_WARNING, "dnssd_clientstub more_bytes: malloc failed"); return 0; }
+		}
+	FD_SET(sd, fs);
+	ret = select((int)sd+1, fs, (fd_set*)NULL, (fd_set*)NULL, &tv);
+	if (fs != &readfds) free(fs);
+	return (ret > 0);
+	}
+
+// Wait for daemon to write to socket
+static int wait_for_daemon(dnssd_sock_t sock, int timeout)
+	{
+#ifndef WIN32
+	// At this point the next operation (accept() or read()) on this socket may block for a few milliseconds waiting
+	// for the daemon to respond, but that's okay -- the daemon is a trusted service and we know if won't take more
+	// than a few milliseconds to respond.  So we'll forego checking for readability of the socket.
+	(void) sock;
+	(void) timeout;
+#else
+	// Windows on the other hand suffers from 3rd party software (primarily 3rd party firewall software) that
+	// interferes with proper functioning of the TCP protocol stack. Because of this and because we depend on TCP
+	// to communicate with the system service, we want to make sure that the next operation on this socket (accept() or
+	// read()) doesn't block indefinitely.
+	if (!gDaemonErr)
+		{
+		struct timeval tv;
+		fd_set set;
+
+		FD_ZERO(&set);
+		FD_SET(sock, &set);
+		tv.tv_sec = timeout;
+		tv.tv_usec = 0;
+		if (!select((int)(sock + 1), &set, NULL, NULL, &tv))
+			{
+				syslog(LOG_WARNING, "dnssd_clientstub wait_for_daemon timed out");
+				gDaemonErr = kDNSServiceErr_Timeout;
+			}
+		}
+#endif
+	return gDaemonErr;
+	}
+
+/* create_hdr
+ *
+ * allocate and initialize an ipc message header. Value of len should initially be the
+ * length of the data, and is set to the value of the data plus the header. data_start
+ * is set to point to the beginning of the data section. SeparateReturnSocket should be
+ * non-zero for calls that can't receive an immediate error return value on their primary
+ * socket, and therefore require a separate return path for the error code result.
+ * if zero, the path to a control socket is appended at the beginning of the message buffer.
+ * data_start is set past this string.
+ */
+static ipc_msg_hdr *create_hdr(uint32_t op, size_t *len, char **data_start, int SeparateReturnSocket, DNSServiceOp *ref)
+	{
+	char *msg = NULL;
+	ipc_msg_hdr *hdr;
+	int datalen;
+#if !defined(USE_TCP_LOOPBACK)
+	char ctrl_path[64] = "";	// "/var/tmp/dnssd_result_socket.xxxxxxxxxx-xxx-xxxxxx"
+#endif
+
+	if (SeparateReturnSocket)
+		{
+#if defined(USE_TCP_LOOPBACK)
+		*len += 2;  // Allocate space for two-byte port number
+#elif defined(USE_NAMED_ERROR_RETURN_SOCKET)
+		struct timeval tv;
+		if (gettimeofday(&tv, NULL) < 0)
+			{ syslog(LOG_WARNING, "dnssd_clientstub create_hdr: gettimeofday failed %d %s", dnssd_errno, dnssd_strerror(dnssd_errno)); return NULL; }
+		sprintf(ctrl_path, "%s%d-%.3lx-%.6lu", CTL_PATH_PREFIX, (int)getpid(),
+			(unsigned long)(tv.tv_sec & 0xFFF), (unsigned long)(tv.tv_usec));
+		*len += strlen(ctrl_path) + 1;
+#else
+		*len += 1;		// Allocate space for single zero byte (empty C string)
+#endif
+		}
+
+	datalen = (int) *len;
+	*len += sizeof(ipc_msg_hdr);
+
+	// Write message to buffer
+	msg = malloc(*len);
+	if (!msg) { syslog(LOG_WARNING, "dnssd_clientstub create_hdr: malloc failed"); return NULL; }
+
+	memset(msg, 0, *len);
+	hdr = (ipc_msg_hdr *)msg;
+	hdr->version                = VERSION;
+	hdr->datalen                = datalen;
+	hdr->ipc_flags              = 0;
+	hdr->op                     = op;
+	hdr->client_context         = ref->uid;
+	hdr->reg_index              = 0;
+	*data_start = msg + sizeof(ipc_msg_hdr);
+#if defined(USE_TCP_LOOPBACK)
+	// Put dummy data in for the port, since we don't know what it is yet.
+	// The data will get filled in before we send the message. This happens in deliver_request().
+	if (SeparateReturnSocket) put_uint16(0, data_start);
+#else
+	if (SeparateReturnSocket) put_string(ctrl_path, data_start);
+#endif
+	return hdr;
+	}
+
+static void FreeDNSRecords(DNSServiceOp *sdRef)
+	{
+	DNSRecord *rec = sdRef->rec;
+	while (rec)
+		{
+		DNSRecord *next = rec->recnext;
+		free(rec);
+		rec = next;
+		}
+	}
+
+static void FreeDNSServiceOp(DNSServiceOp *x)
+	{
+	// We don't use our DNSServiceRefValid macro here because if we're cleaning up after a socket() call failed
+	// then sockfd could legitimately contain a failing value (e.g. dnssd_InvalidSocket)
+	if ((x->sockfd ^ x->validator) != ValidatorBits)
+		syslog(LOG_WARNING, "dnssd_clientstub attempt to dispose invalid DNSServiceRef %p %08X %08X", x, x->sockfd, x->validator);
+	else
+		{
+		x->next         = NULL;
+		x->primary      = NULL;
+		x->sockfd       = dnssd_InvalidSocket;
+		x->validator    = 0xDDDDDDDD;
+		x->op           = request_op_none;
+		x->max_index    = 0;
+		x->logcounter   = 0;
+		x->moreptr      = NULL;
+		x->ProcessReply = NULL;
+		x->AppCallback  = NULL;
+		x->AppContext   = NULL;
+#if _DNS_SD_LIBDISPATCH
+		if (x->disp_source)	dispatch_release(x->disp_source);
+		x->disp_source  = NULL;
+		x->disp_queue   = NULL;
+#endif
+		// DNSRecords may have been added to subordinate sdRef e.g., DNSServiceRegister/DNSServiceAddRecord
+		// or on the main sdRef e.g., DNSServiceCreateConnection/DNSServiveRegisterRecord. DNSRecords may have
+		// been freed if the application called DNSRemoveRecord
+		FreeDNSRecords(x);
+		free(x);
+		}
+	}
+
+// Return a connected service ref (deallocate with DNSServiceRefDeallocate)
+static DNSServiceErrorType ConnectToServer(DNSServiceRef *ref, DNSServiceFlags flags, uint32_t op, ProcessReplyFn ProcessReply, void *AppCallback, void *AppContext)
+	{
+	#if APPLE_OSX_mDNSResponder
+	int NumTries = DNSSD_CLIENT_MAXTRIES;
+	#else
+	int NumTries = 0;
+	#endif
+
+	dnssd_sockaddr_t saddr;
+	DNSServiceOp *sdr;
+
+	if (!ref) { syslog(LOG_WARNING, "dnssd_clientstub DNSService operation with NULL DNSServiceRef"); return kDNSServiceErr_BadParam; }
+
+	if (flags & kDNSServiceFlagsShareConnection)
+		{
+		if (!*ref)
+			{
+			syslog(LOG_WARNING, "dnssd_clientstub kDNSServiceFlagsShareConnection used with NULL DNSServiceRef");
+			return kDNSServiceErr_BadParam;
+			}
+		if (!DNSServiceRefValid(*ref) || (*ref)->op != connection_request || (*ref)->primary)
+			{
+			syslog(LOG_WARNING, "dnssd_clientstub kDNSServiceFlagsShareConnection used with invalid DNSServiceRef %p %08X %08X",
+				(*ref), (*ref)->sockfd, (*ref)->validator);
+			*ref = NULL;
+			return kDNSServiceErr_BadReference;
+			}
+		}
+
+	#if defined(_WIN32)
+	if (!g_initWinsock)
+		{
+		WSADATA wsaData;
+		g_initWinsock = 1;
+		if (WSAStartup(MAKEWORD(2,2), &wsaData) != 0) { *ref = NULL; return kDNSServiceErr_ServiceNotRunning; }
+		}
+	// <rdar://problem/4096913> If the system service is disabled, we only want to try to connect once
+	if (IsSystemServiceDisabled()) NumTries = DNSSD_CLIENT_MAXTRIES;
+	#endif
+
+	sdr = malloc(sizeof(DNSServiceOp));
+	if (!sdr) { syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: malloc failed"); *ref = NULL; return kDNSServiceErr_NoMemory; }
+	sdr->next          = NULL;
+	sdr->primary       = NULL;
+	sdr->sockfd        = dnssd_InvalidSocket;
+	sdr->validator     = sdr->sockfd ^ ValidatorBits;
+	sdr->op            = op;
+	sdr->max_index     = 0;
+	sdr->logcounter    = 0;
+	sdr->moreptr       = NULL;
+	sdr->uid.u32[0]    = 0;
+	sdr->uid.u32[1]    = 0;
+	sdr->ProcessReply  = ProcessReply;
+	sdr->AppCallback   = AppCallback;
+	sdr->AppContext    = AppContext;
+	sdr->rec           = NULL;
+#if _DNS_SD_LIBDISPATCH
+	sdr->disp_source   = NULL;
+	sdr->disp_queue    = NULL;
+#endif
+
+	if (flags & kDNSServiceFlagsShareConnection)
+		{
+		DNSServiceOp **p = &(*ref)->next;		// Append ourselves to end of primary's list
+		while (*p) p = &(*p)->next;
+		*p = sdr;
+		// Preincrement counter before we use it -- it helps with debugging if we know the all-zeroes ID should never appear
+		if (++(*ref)->uid.u32[0] == 0) ++(*ref)->uid.u32[1];	// In parent DNSServiceOp increment UID counter
+		sdr->primary    = *ref;					// Set our primary pointer
+		sdr->sockfd     = (*ref)->sockfd;		// Inherit primary's socket
+		sdr->validator  = (*ref)->validator;
+		sdr->uid        = (*ref)->uid;
+		//printf("ConnectToServer sharing socket %d\n", sdr->sockfd);
+		}
+	else
+		{
+		#ifdef SO_NOSIGPIPE
+		const unsigned long optval = 1;
+		#endif
+		*ref = NULL;
+		sdr->sockfd    = socket(AF_DNSSD, SOCK_STREAM, 0);
+		sdr->validator = sdr->sockfd ^ ValidatorBits;
+		if (!dnssd_SocketValid(sdr->sockfd))
+			{
+			syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: socket failed %d %s", dnssd_errno, dnssd_strerror(dnssd_errno));
+			FreeDNSServiceOp(sdr);
+			return kDNSServiceErr_NoMemory;
+			}
+		#ifdef SO_NOSIGPIPE
+		// Some environments (e.g. OS X) support turning off SIGPIPE for a socket
+		if (setsockopt(sdr->sockfd, SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval)) < 0)
+			syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: SO_NOSIGPIPE failed %d %s", dnssd_errno, dnssd_strerror(dnssd_errno));
+		#endif
+		#if defined(USE_TCP_LOOPBACK)
+		saddr.sin_family      = AF_INET;
+		saddr.sin_addr.s_addr = inet_addr(MDNS_TCP_SERVERADDR);
+		saddr.sin_port        = htons(MDNS_TCP_SERVERPORT);
+		#else
+		saddr.sun_family      = AF_LOCAL;
+		strcpy(saddr.sun_path, MDNS_UDS_SERVERPATH);
+		#if !defined(__ppc__) && defined(SO_DEFUNCTOK)
+		{
+		int defunct = 1;
+		if (setsockopt(sdr->sockfd, SOL_SOCKET, SO_DEFUNCTOK, &defunct, sizeof(defunct)) < 0)
+			syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: SO_DEFUNCTOK failed %d %s", dnssd_errno, dnssd_strerror(dnssd_errno));
+		}
+		#endif	
+		#endif
+	
+		while (1)
+			{
+			int err = connect(sdr->sockfd, (struct sockaddr *) &saddr, sizeof(saddr));
+			if (!err) break; // If we succeeded, return sdr
+			// If we failed, then it may be because the daemon is still launching.
+			// This can happen for processes that launch early in the boot process, while the
+			// daemon is still coming up. Rather than fail here, we'll wait a bit and try again.
+			// If, after four seconds, we still can't connect to the daemon,
+			// then we give up and return a failure code.
+			if (++NumTries < DNSSD_CLIENT_MAXTRIES) sleep(1); // Sleep a bit, then try again
+			else { dnssd_close(sdr->sockfd); FreeDNSServiceOp(sdr); return kDNSServiceErr_ServiceNotRunning; }
+			}
+		//printf("ConnectToServer opened socket %d\n", sdr->sockfd);
+		}
+
+	*ref = sdr;
+	return kDNSServiceErr_NoError;
+	}
+
+#define deliver_request_bailout(MSG) \
+	do { syslog(LOG_WARNING, "dnssd_clientstub deliver_request: %s failed %d (%s)", (MSG), dnssd_errno, dnssd_strerror(dnssd_errno)); goto cleanup; } while(0)
+
+static DNSServiceErrorType deliver_request(ipc_msg_hdr *hdr, DNSServiceOp *sdr)
+	{
+	uint32_t datalen = hdr->datalen;	// We take a copy here because we're going to convert hdr->datalen to network byte order
+	#if defined(USE_TCP_LOOPBACK) || defined(USE_NAMED_ERROR_RETURN_SOCKET)
+	char *const data = (char *)hdr + sizeof(ipc_msg_hdr);
+	#endif
+	dnssd_sock_t listenfd = dnssd_InvalidSocket, errsd = dnssd_InvalidSocket;
+	DNSServiceErrorType err = kDNSServiceErr_Unknown;	// Default for the "goto cleanup" cases
+	int MakeSeparateReturnSocket = 0;
+
+	// Note: need to check hdr->op, not sdr->op.
+	// hdr->op contains the code for the specific operation we're currently doing, whereas sdr->op
+	// contains the original parent DNSServiceOp (e.g. for an add_record_request, hdr->op will be
+	// add_record_request but the parent sdr->op will be connection_request or reg_service_request)
+	if (sdr->primary ||
+		hdr->op == reg_record_request || hdr->op == add_record_request || hdr->op == update_record_request || hdr->op == remove_record_request)
+		MakeSeparateReturnSocket = 1;
+
+	if (!DNSServiceRefValid(sdr))
+		{
+		syslog(LOG_WARNING, "dnssd_clientstub deliver_request: invalid DNSServiceRef %p %08X %08X", sdr, sdr->sockfd, sdr->validator);
+		return kDNSServiceErr_BadReference;
+		}
+
+	if (!hdr) { syslog(LOG_WARNING, "dnssd_clientstub deliver_request: !hdr"); return kDNSServiceErr_Unknown; }
+
+	if (MakeSeparateReturnSocket)
+		{
+		#if defined(USE_TCP_LOOPBACK)
+			{
+			union { uint16_t s; u_char b[2]; } port;
+			dnssd_sockaddr_t caddr;
+			dnssd_socklen_t len = (dnssd_socklen_t) sizeof(caddr);
+			listenfd = socket(AF_DNSSD, SOCK_STREAM, 0);
+			if (!dnssd_SocketValid(listenfd)) deliver_request_bailout("TCP socket");
+
+			caddr.sin_family      = AF_INET;
+			caddr.sin_port        = 0;
+			caddr.sin_addr.s_addr = inet_addr(MDNS_TCP_SERVERADDR);
+			if (bind(listenfd, (struct sockaddr*) &caddr, sizeof(caddr)) < 0) deliver_request_bailout("TCP bind");
+			if (getsockname(listenfd, (struct sockaddr*) &caddr, &len)   < 0) deliver_request_bailout("TCP getsockname");
+			if (listen(listenfd, 1)                                      < 0) deliver_request_bailout("TCP listen");
+			port.s = caddr.sin_port;
+			data[0] = port.b[0];  // don't switch the byte order, as the
+			data[1] = port.b[1];  // daemon expects it in network byte order
+			}
+		#elif defined(USE_NAMED_ERROR_RETURN_SOCKET)
+			{
+			mode_t mask;
+			int bindresult;
+			dnssd_sockaddr_t caddr;
+			listenfd = socket(AF_DNSSD, SOCK_STREAM, 0);
+			if (!dnssd_SocketValid(listenfd)) deliver_request_bailout("USE_NAMED_ERROR_RETURN_SOCKET socket");
+
+			caddr.sun_family = AF_LOCAL;
+			// According to Stevens (section 3.2), there is no portable way to
+			// determine whether sa_len is defined on a particular platform.
+			#ifndef NOT_HAVE_SA_LEN
+			caddr.sun_len = sizeof(struct sockaddr_un);
+			#endif
+			strcpy(caddr.sun_path, data);
+			mask = umask(0);
+			bindresult = bind(listenfd, (struct sockaddr *)&caddr, sizeof(caddr));
+			umask(mask);
+			if (bindresult          < 0) deliver_request_bailout("USE_NAMED_ERROR_RETURN_SOCKET bind");
+			if (listen(listenfd, 1) < 0) deliver_request_bailout("USE_NAMED_ERROR_RETURN_SOCKET listen");
+			}
+		#else
+			{
+			dnssd_sock_t sp[2];
+			if (socketpair(AF_DNSSD, SOCK_STREAM, 0, sp) < 0) deliver_request_bailout("socketpair");
+			else
+				{
+				errsd    = sp[0];	// We'll read our four-byte error code from sp[0]
+				listenfd = sp[1];	// We'll send sp[1] to the daemon
+				#if !defined(__ppc__) && defined(SO_DEFUNCTOK)
+				{
+				int defunct = 1;
+				if (setsockopt(errsd, SOL_SOCKET, SO_DEFUNCTOK, &defunct, sizeof(defunct)) < 0)
+					syslog(LOG_WARNING, "dnssd_clientstub ConnectToServer: SO_DEFUNCTOK failed %d %s", dnssd_errno, dnssd_strerror(dnssd_errno));
+				}
+				#endif	
+				}
+			}
+		#endif
+		}
+
+#if !defined(USE_TCP_LOOPBACK) && !defined(USE_NAMED_ERROR_RETURN_SOCKET)
+	// If we're going to make a separate error return socket, and pass it to the daemon
+	// using sendmsg, then we'll hold back one data byte to go with it.
+	// On some versions of Unix (including Leopard) sending a control message without
+	// any associated data does not work reliably -- e.g. one particular issue we ran
+	// into is that if the receiving program is in a kqueue loop waiting to be notified
+	// of the received message, it doesn't get woken up when the control message arrives.
+	if (MakeSeparateReturnSocket || sdr->op == send_bpf) datalen--;		// Okay to use sdr->op when checking for op == send_bpf
+#endif
+
+	// At this point, our listening socket is set up and waiting, if necessary, for the daemon to connect back to
+	ConvertHeaderBytes(hdr);
+	//syslog(LOG_WARNING, "dnssd_clientstub deliver_request writing %lu bytes", (unsigned long)(datalen + sizeof(ipc_msg_hdr)));
+	//if (MakeSeparateReturnSocket) syslog(LOG_WARNING, "dnssd_clientstub deliver_request name is %s", data);
+#if TEST_SENDING_ONE_BYTE_AT_A_TIME
+	unsigned int i;
+	for (i=0; i<datalen + sizeof(ipc_msg_hdr); i++)
+		{
+		syslog(LOG_WARNING, "dnssd_clientstub deliver_request writing %d", i);
+		if (write_all(sdr->sockfd, ((char *)hdr)+i, 1) < 0)
+			{ syslog(LOG_WARNING, "write_all (byte %u) failed", i); goto cleanup; }
+		usleep(10000);
+		}
+#else
+	if (write_all(sdr->sockfd, (char *)hdr, datalen + sizeof(ipc_msg_hdr)) < 0)
+		{
+		// write_all already prints an error message if there is an error writing to
+		// the socket except for DEFUNCT. Logging here is unnecessary and also wrong
+		// in the case of DEFUNCT sockets
+		syslog(LOG_INFO, "dnssd_clientstub deliver_request ERROR: write_all(%d, %lu bytes) failed",
+			sdr->sockfd, (unsigned long)(datalen + sizeof(ipc_msg_hdr)));
+		goto cleanup;
+		}
+#endif
+
+	if (!MakeSeparateReturnSocket) errsd = sdr->sockfd;
+	if (MakeSeparateReturnSocket || sdr->op == send_bpf)	// Okay to use sdr->op when checking for op == send_bpf
+		{
+#if defined(USE_TCP_LOOPBACK) || defined(USE_NAMED_ERROR_RETURN_SOCKET)
+		// At this point we may block in accept for a few milliseconds waiting for the daemon to connect back to us,
+		// but that's okay -- the daemon is a trusted service and we know if won't take more than a few milliseconds to respond.
+		dnssd_sockaddr_t daddr;
+		dnssd_socklen_t len = sizeof(daddr);
+		if ((err = wait_for_daemon(listenfd, DNSSD_CLIENT_TIMEOUT)) != kDNSServiceErr_NoError) goto cleanup;
+		errsd = accept(listenfd, (struct sockaddr *)&daddr, &len);
+		if (!dnssd_SocketValid(errsd)) deliver_request_bailout("accept");
+#else
+
+#if APPLE_OSX_mDNSResponder
+// On Leopard, the stock definitions of the CMSG_* macros in /usr/include/sys/socket.h,
+// while arguably correct in theory, nonetheless in practice produce code that doesn't work on 64-bit machines
+// For details see <rdar://problem/5565787> Bonjour API broken for 64-bit apps (SCM_RIGHTS sendmsg fails)
+#undef  CMSG_DATA
+#define CMSG_DATA(cmsg) ((unsigned char *)(cmsg) + (sizeof(struct cmsghdr)))
+#undef  CMSG_SPACE
+#define CMSG_SPACE(l)   ((sizeof(struct cmsghdr)) + (l))
+#undef  CMSG_LEN
+#define CMSG_LEN(l)     ((sizeof(struct cmsghdr)) + (l))
+#endif
+
+		struct iovec vec = { ((char *)hdr) + sizeof(ipc_msg_hdr) + datalen, 1 }; // Send the last byte along with the SCM_RIGHTS
+		struct msghdr msg;
+		struct cmsghdr *cmsg;
+		char cbuf[CMSG_SPACE(sizeof(dnssd_sock_t))];
+
+		if (sdr->op == send_bpf)	// Okay to use sdr->op when checking for op == send_bpf
+			{
+			int i;
+			char p[12];		// Room for "/dev/bpf999" with terminating null
+			for (i=0; i<100; i++)
+				{
+				snprintf(p, sizeof(p), "/dev/bpf%d", i);
+				listenfd = open(p, O_RDWR, 0);
+				//if (dnssd_SocketValid(listenfd)) syslog(LOG_WARNING, "Sending fd %d for %s", listenfd, p);
+				if (!dnssd_SocketValid(listenfd) && dnssd_errno != EBUSY)
+					syslog(LOG_WARNING, "Error opening %s %d (%s)", p, dnssd_errno, dnssd_strerror(dnssd_errno));
+				if (dnssd_SocketValid(listenfd) || dnssd_errno != EBUSY) break;
+				}
+			}
+
+		msg.msg_name       = 0;
+		msg.msg_namelen    = 0;
+		msg.msg_iov        = &vec;
+		msg.msg_iovlen     = 1;
+		msg.msg_control    = cbuf;
+		msg.msg_controllen = CMSG_LEN(sizeof(dnssd_sock_t));
+		msg.msg_flags      = 0;
+		cmsg = CMSG_FIRSTHDR(&msg);
+		cmsg->cmsg_len     = CMSG_LEN(sizeof(dnssd_sock_t));
+		cmsg->cmsg_level   = SOL_SOCKET;
+		cmsg->cmsg_type    = SCM_RIGHTS;
+		*((dnssd_sock_t *)CMSG_DATA(cmsg)) = listenfd;
+
+#if TEST_KQUEUE_CONTROL_MESSAGE_BUG
+		sleep(1);
+#endif
+
+#if DEBUG_64BIT_SCM_RIGHTS
+		syslog(LOG_WARNING, "dnssd_clientstub sendmsg read sd=%d write sd=%d %ld %ld %ld/%ld/%ld/%ld",
+			errsd, listenfd, sizeof(dnssd_sock_t), sizeof(void*),
+			sizeof(struct cmsghdr) + sizeof(dnssd_sock_t),
+			CMSG_LEN(sizeof(dnssd_sock_t)), (long)CMSG_SPACE(sizeof(dnssd_sock_t)),
+			(long)((char*)CMSG_DATA(cmsg) + 4 - cbuf));
+#endif // DEBUG_64BIT_SCM_RIGHTS
+
+		if (sendmsg(sdr->sockfd, &msg, 0) < 0)
+			{
+			syslog(LOG_WARNING, "dnssd_clientstub deliver_request ERROR: sendmsg failed read sd=%d write sd=%d errno %d (%s)",
+				errsd, listenfd, dnssd_errno, dnssd_strerror(dnssd_errno));
+			err = kDNSServiceErr_Incompatible;
+			goto cleanup;
+			}
+
+#if DEBUG_64BIT_SCM_RIGHTS
+		syslog(LOG_WARNING, "dnssd_clientstub sendmsg read sd=%d write sd=%d okay", errsd, listenfd);
+#endif // DEBUG_64BIT_SCM_RIGHTS
+
+#endif
+		// Close our end of the socketpair *before* blocking in read_all to get the four-byte error code.
+		// Otherwise, if the daemon closes our socket (or crashes), we block in read_all() forever
+		// because the socket is not closed (we still have an open reference to it ourselves).
+		dnssd_close(listenfd);
+		listenfd = dnssd_InvalidSocket;		// Make sure we don't close it a second time in the cleanup handling below
+		}
+
+	// At this point we may block in read_all for a few milliseconds waiting for the daemon to send us the error code,
+	// but that's okay -- the daemon is a trusted service and we know if won't take more than a few milliseconds to respond.
+	if (sdr->op == send_bpf)	// Okay to use sdr->op when checking for op == send_bpf
+		err = kDNSServiceErr_NoError;
+	else if ((err = wait_for_daemon(errsd, DNSSD_CLIENT_TIMEOUT)) == kDNSServiceErr_NoError)
+		{
+		if (read_all(errsd, (char*)&err, (int)sizeof(err)) < 0)
+			err = kDNSServiceErr_ServiceNotRunning; // On failure read_all will have written a message to syslog for us
+		else
+			err = ntohl(err);
+		}
+
+	//syslog(LOG_WARNING, "dnssd_clientstub deliver_request: retrieved error code %d", err);
+
+cleanup:
+	if (MakeSeparateReturnSocket)
+		{
+		if (dnssd_SocketValid(listenfd)) dnssd_close(listenfd);
+		if (dnssd_SocketValid(errsd))    dnssd_close(errsd);
+#if defined(USE_NAMED_ERROR_RETURN_SOCKET)
+		// syslog(LOG_WARNING, "dnssd_clientstub deliver_request: removing UDS: %s", data);
+		if (unlink(data) != 0)
+			syslog(LOG_WARNING, "dnssd_clientstub WARNING: unlink(\"%s\") failed errno %d (%s)", data, dnssd_errno, dnssd_strerror(dnssd_errno));
+		// else syslog(LOG_WARNING, "dnssd_clientstub deliver_request: removed UDS: %s", data);
+#endif
+		}
+
+	free(hdr);
+	return err;
+	}
+
+int DNSSD_API DNSServiceRefSockFD(DNSServiceRef sdRef)
+	{
+	if (!sdRef) { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRefSockFD called with NULL DNSServiceRef"); return dnssd_InvalidSocket; }
+
+	if (!DNSServiceRefValid(sdRef))
+		{
+		syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRefSockFD called with invalid DNSServiceRef %p %08X %08X",
+			sdRef, sdRef->sockfd, sdRef->validator);
+		return dnssd_InvalidSocket;
+		}
+
+	if (sdRef->primary)
+		{
+		syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRefSockFD undefined for kDNSServiceFlagsShareConnection subordinate DNSServiceRef %p", sdRef);
+		return dnssd_InvalidSocket;
+		}
+
+	return (int) sdRef->sockfd;
+	}
+
+#if _DNS_SD_LIBDISPATCH
+static void CallbackWithError(DNSServiceRef sdRef, DNSServiceErrorType error)
+	{
+	DNSServiceOp *sdr = sdRef;
+	DNSServiceOp *sdrNext;
+	DNSRecord *rec;
+	DNSRecord *recnext;
+	int morebytes;
+	
+	while (sdr)
+		{
+		// We can't touch the sdr after the callback as it can be deallocated in the callback
+		sdrNext = sdr->next;
+		morebytes = 1;
+		sdr->moreptr = &morebytes;
+		switch (sdr->op)
+			{
+			case resolve_request:
+				if (sdr->AppCallback)((DNSServiceResolveReply)    sdr->AppCallback)(sdr, 0, 0, error, NULL, 0, 0, 0, NULL,    sdr->AppContext);
+				break;
+			case query_request:
+				if (sdr->AppCallback)((DNSServiceQueryRecordReply)sdr->AppCallback)(sdr, 0, 0, error, NULL, 0, 0, 0, NULL, 0, sdr->AppContext);
+				break;
+			case addrinfo_request:
+				if (sdr->AppCallback)((DNSServiceGetAddrInfoReply)sdr->AppCallback)(sdr, 0, 0, error, NULL, NULL, 0,          sdr->AppContext);
+				break;
+			case browse_request:
+				if (sdr->AppCallback)((DNSServiceBrowseReply)     sdr->AppCallback)(sdr, 0, 0, error, NULL, 0, NULL,          sdr->AppContext);
+				break;
+			case reg_service_request:
+				if (sdr->AppCallback)((DNSServiceRegisterReply)   sdr->AppCallback)(sdr, 0,    error, NULL, 0, NULL,          sdr->AppContext);
+				break;
+			case enumeration_request:
+				if (sdr->AppCallback)((DNSServiceDomainEnumReply) sdr->AppCallback)(sdr, 0, 0, error, NULL,                   sdr->AppContext);
+				break;
+			case sethost_request:
+				if (sdr->AppCallback)((DNSServiceSetHostReply)    sdr->AppCallback)(sdr, 0,    error, NULL,                   sdr->AppContext);
+				break;
+			case connection_request:
+				// This means Register Record, walk the list of DNSRecords to do the callback
+				rec = sdr->rec;
+				while (rec)
+					{
+					recnext = rec->recnext;
+					if (rec->AppCallback) ((DNSServiceRegisterRecordReply)rec->AppCallback)(sdr, 0, 0, error, rec->AppContext);
+					// The Callback can call DNSServiceRefDeallocate which in turn frees sdr and all the records.
+					// Detect that and return early
+					if (!morebytes){syslog(LOG_WARNING, "dnssdclientstub:Record: CallbackwithError morebytes zero"); return;}
+					rec = recnext;
+					}
+				break;
+			case port_mapping_request:
+				if (sdr->AppCallback)((DNSServiceNATPortMappingReply)sdr->AppCallback)(sdr, 0, 0, error, 0, 0, 0, 0, 0, sdr->AppContext);
+				break;
+			default:
+				syslog(LOG_WARNING, "dnssd_clientstub CallbackWithError called with bad op %d", sdr->op);
+			}
+		// If DNSServiceRefDeallocate was called in the callback, morebytes will be zero. It means
+		// all other sdrefs have been freed. This happens for shared connections where the
+		// DNSServiceRefDeallocate on the first sdRef frees all other sdrefs.
+		if (!morebytes){syslog(LOG_WARNING, "dnssdclientstub:sdRef: CallbackwithError morebytes zero"); return;}
+		sdr = sdrNext;
+		}
+	}
+#endif // _DNS_SD_LIBDISPATCH
+
+// Handle reply from server, calling application client callback. If there is no reply
+// from the daemon on the socket contained in sdRef, the call will block.
+DNSServiceErrorType DNSSD_API DNSServiceProcessResult(DNSServiceRef sdRef)
+	{
+	int morebytes = 0;
+
+	if (!sdRef) { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceProcessResult called with NULL DNSServiceRef"); return kDNSServiceErr_BadParam; }
+
+	if (!DNSServiceRefValid(sdRef))
+		{
+		syslog(LOG_WARNING, "dnssd_clientstub DNSServiceProcessResult called with invalid DNSServiceRef %p %08X %08X", sdRef, sdRef->sockfd, sdRef->validator);
+		return kDNSServiceErr_BadReference;
+		}
+
+	if (sdRef->primary)
+		{
+		syslog(LOG_WARNING, "dnssd_clientstub DNSServiceProcessResult undefined for kDNSServiceFlagsShareConnection subordinate DNSServiceRef %p", sdRef);
+		return kDNSServiceErr_BadReference;
+		}
+
+	if (!sdRef->ProcessReply)
+		{
+		static int num_logs = 0;
+		if (num_logs < 10) syslog(LOG_WARNING, "dnssd_clientstub DNSServiceProcessResult called with DNSServiceRef with no ProcessReply function");
+		if (num_logs < 1000) num_logs++; else sleep(1);
+		return kDNSServiceErr_BadReference;
+		}
+
+	do
+		{
+		CallbackHeader cbh;
+		char *data;
+	
+		// return NoError on EWOULDBLOCK. This will handle the case
+		// where a non-blocking socket is told there is data, but it was a false positive.
+		// On error, read_all will write a message to syslog for us, so don't need to duplicate that here
+		// Note: If we want to properly support using non-blocking sockets in the future
+		int result = read_all(sdRef->sockfd, (void *)&cbh.ipc_hdr, sizeof(cbh.ipc_hdr));
+		if (result == read_all_fail)
+			{
+			// Set the ProcessReply to NULL before callback as the sdRef can get deallocated
+			// in the callback.
+			sdRef->ProcessReply = NULL;
+#if _DNS_SD_LIBDISPATCH
+			// Call the callbacks with an error if using the dispatch API, as DNSServiceProcessResult
+			// is not called by the application and hence need to communicate the error. Cancel the
+			// source so that we don't get any more events
+			if (sdRef->disp_source)
+				{
+				dispatch_source_cancel(sdRef->disp_source);
+				dispatch_release(sdRef->disp_source);
+				sdRef->disp_source = NULL;
+				CallbackWithError(sdRef, kDNSServiceErr_ServiceNotRunning);
+				}
+#endif
+			// Don't touch sdRef anymore as it might have been deallocated
+			return kDNSServiceErr_ServiceNotRunning;
+			}
+		else if (result == read_all_wouldblock)
+			{
+			if (morebytes && sdRef->logcounter < 100)
+				{
+				sdRef->logcounter++;
+				syslog(LOG_WARNING, "dnssd_clientstub DNSServiceProcessResult error: select indicated data was waiting but read_all returned EWOULDBLOCK");
+				}
+			return kDNSServiceErr_NoError;
+			}
+	
+		ConvertHeaderBytes(&cbh.ipc_hdr);
+		if (cbh.ipc_hdr.version != VERSION)
+			{
+			syslog(LOG_WARNING, "dnssd_clientstub DNSServiceProcessResult daemon version %d does not match client version %d", cbh.ipc_hdr.version, VERSION);
+			sdRef->ProcessReply = NULL;
+			return kDNSServiceErr_Incompatible;
+			}
+	
+		data = malloc(cbh.ipc_hdr.datalen);
+		if (!data) return kDNSServiceErr_NoMemory;
+		if (read_all(sdRef->sockfd, data, cbh.ipc_hdr.datalen) < 0) // On error, read_all will write a message to syslog for us
+			{
+			// Set the ProcessReply to NULL before callback as the sdRef can get deallocated
+			// in the callback.
+			sdRef->ProcessReply = NULL;
+#if _DNS_SD_LIBDISPATCH
+			// Call the callbacks with an error if using the dispatch API, as DNSServiceProcessResult
+			// is not called by the application and hence need to communicate the error. Cancel the
+			// source so that we don't get any more events
+			if (sdRef->disp_source)
+				{
+				dispatch_source_cancel(sdRef->disp_source);
+				dispatch_release(sdRef->disp_source);
+				sdRef->disp_source = NULL;
+				CallbackWithError(sdRef, kDNSServiceErr_ServiceNotRunning);
+				}
+#endif
+			// Don't touch sdRef anymore as it might have been deallocated
+			free(data);
+			return kDNSServiceErr_ServiceNotRunning;
+			}
+		else
+			{
+			const char *ptr = data;
+			cbh.cb_flags     = get_flags     (&ptr, data + cbh.ipc_hdr.datalen);
+			cbh.cb_interface = get_uint32    (&ptr, data + cbh.ipc_hdr.datalen);
+			cbh.cb_err       = get_error_code(&ptr, data + cbh.ipc_hdr.datalen);
+
+			// CAUTION: We have to handle the case where the client calls DNSServiceRefDeallocate from within the callback function.
+			// To do this we set moreptr to point to morebytes. If the client does call DNSServiceRefDeallocate(),
+			// then that routine will clear morebytes for us, and cause us to exit our loop.
+			morebytes = more_bytes(sdRef->sockfd);
+			if (morebytes)
+				{
+				cbh.cb_flags |= kDNSServiceFlagsMoreComing;
+				sdRef->moreptr = &morebytes;
+				}
+			if (ptr) sdRef->ProcessReply(sdRef, &cbh, ptr, data + cbh.ipc_hdr.datalen);
+			// Careful code here:
+			// If morebytes is non-zero, that means we set sdRef->moreptr above, and the operation was not
+			// cancelled out from under us, so now we need to clear sdRef->moreptr so we don't leave a stray
+			// dangling pointer pointing to a long-gone stack variable.
+			// If morebytes is zero, then one of two thing happened:
+			// (a) morebytes was 0 above, so we didn't set sdRef->moreptr, so we don't need to clear it
+			// (b) morebytes was 1 above, and we set sdRef->moreptr, but the operation was cancelled (with DNSServiceRefDeallocate()),
+			//     so we MUST NOT try to dereference our stale sdRef pointer.
+			if (morebytes) sdRef->moreptr = NULL;
+			}
+		free(data);
+		} while (morebytes);
+
+	return kDNSServiceErr_NoError;
+	}
+
+void DNSSD_API DNSServiceRefDeallocate(DNSServiceRef sdRef)
+	{
+	if (!sdRef) { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRefDeallocate called with NULL DNSServiceRef"); return; }
+
+	if (!DNSServiceRefValid(sdRef))		// Also verifies dnssd_SocketValid(sdRef->sockfd) for us too
+		{
+		syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRefDeallocate called with invalid DNSServiceRef %p %08X %08X", sdRef, sdRef->sockfd, sdRef->validator);
+		return;
+		}
+
+	// If we're in the middle of a DNSServiceProcessResult() invocation for this DNSServiceRef, clear its morebytes flag to break it out of its while loop
+	if (sdRef->moreptr) *(sdRef->moreptr) = 0;
+
+	if (sdRef->primary)		// If this is a subordinate DNSServiceOp, just send a 'stop' command
+		{
+		DNSServiceOp **p = &sdRef->primary->next;
+		while (*p && *p != sdRef) p = &(*p)->next;
+		if (*p)
+			{
+			char *ptr;
+			size_t len = 0;
+			ipc_msg_hdr *hdr = create_hdr(cancel_request, &len, &ptr, 0, sdRef);
+			if (hdr)
+				{
+				ConvertHeaderBytes(hdr);
+				write_all(sdRef->sockfd, (char *)hdr, len);
+				free(hdr);
+				}
+			*p = sdRef->next;
+			FreeDNSServiceOp(sdRef);
+			}
+		}
+	else					// else, make sure to terminate all subordinates as well
+		{
+#if _DNS_SD_LIBDISPATCH
+		// The cancel handler will close the fd if a dispatch source has been set
+		if (sdRef->disp_source)
+			{
+			// By setting the ProcessReply to NULL, we make sure that we never call
+			// the application callbacks ever, after returning from this function. We
+			// assume that DNSServiceRefDeallocate is called from the serial queue
+			// that was passed to DNSServiceSetDispatchQueue. Hence, dispatch_source_cancel
+			// should cancel all the blocks on the queue and hence there should be no more
+			// callbacks when we return from this function. Setting ProcessReply to NULL
+			// provides extra protection.
+			sdRef->ProcessReply = NULL;
+			dispatch_source_cancel(sdRef->disp_source);
+			dispatch_release(sdRef->disp_source);
+			sdRef->disp_source = NULL;
+			}
+			// if disp_queue is set, it means it used the DNSServiceSetDispatchQueue API. In that case,
+			// when the source was cancelled, the fd was closed in the handler. Currently the source
+			// is cancelled only when the mDNSResponder daemon dies
+		else if (!sdRef->disp_queue) dnssd_close(sdRef->sockfd);
+#else
+		dnssd_close(sdRef->sockfd);
+#endif
+		// Free DNSRecords added in DNSRegisterRecord if they have not
+		// been freed in DNSRemoveRecord
+		while (sdRef)
+			{
+			DNSServiceOp *p = sdRef;
+			sdRef = sdRef->next;
+			FreeDNSServiceOp(p);
+			}
+		}
+	}
+
+DNSServiceErrorType DNSSD_API DNSServiceGetProperty(const char *property, void *result, uint32_t *size)
+	{
+	char *ptr;
+	size_t len = strlen(property) + 1;
+	ipc_msg_hdr *hdr;
+	DNSServiceOp *tmp;
+	uint32_t actualsize;
+
+	DNSServiceErrorType err = ConnectToServer(&tmp, 0, getproperty_request, NULL, NULL, NULL);
+	if (err) return err;
+
+	hdr = create_hdr(getproperty_request, &len, &ptr, 0, tmp);
+	if (!hdr) { DNSServiceRefDeallocate(tmp); return kDNSServiceErr_NoMemory; }
+
+	put_string(property, &ptr);
+	err = deliver_request(hdr, tmp);		// Will free hdr for us
+	if (read_all(tmp->sockfd, (char*)&actualsize, (int)sizeof(actualsize)) < 0)
+		{ DNSServiceRefDeallocate(tmp); return kDNSServiceErr_ServiceNotRunning; }
+
+	actualsize = ntohl(actualsize);
+	if (read_all(tmp->sockfd, (char*)result, actualsize < *size ? actualsize : *size) < 0)
+		{ DNSServiceRefDeallocate(tmp); return kDNSServiceErr_ServiceNotRunning; }
+	DNSServiceRefDeallocate(tmp);
+
+	// Swap version result back to local process byte order
+	if (!strcmp(property, kDNSServiceProperty_DaemonVersion) && *size >= 4)
+		*(uint32_t*)result = ntohl(*(uint32_t*)result);
+
+	*size = actualsize;
+	return kDNSServiceErr_NoError;
+	}
+
+static void handle_resolve_response(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *data, const char *end)
+	{
+	char fullname[kDNSServiceMaxDomainName];
+	char target[kDNSServiceMaxDomainName];
+	uint16_t txtlen;
+	union { uint16_t s; u_char b[2]; } port;
+	unsigned char *txtrecord;
+
+	get_string(&data, end, fullname, kDNSServiceMaxDomainName);
+	get_string(&data, end, target,   kDNSServiceMaxDomainName);
+	if (!data || data + 2 > end) goto fail;
+
+	port.b[0] = *data++;
+	port.b[1] = *data++;
+	txtlen = get_uint16(&data, end);
+	txtrecord = (unsigned char *)get_rdata(&data, end, txtlen);
+
+	if (!data) goto fail;
+	((DNSServiceResolveReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, fullname, target, port.s, txtlen, txtrecord, sdr->AppContext);
+	return;
+	// MUST NOT touch sdr after invoking AppCallback -- client is allowed to dispose it from within callback function
+fail:
+	syslog(LOG_WARNING, "dnssd_clientstub handle_resolve_response: error reading result from daemon");
+	}
+
+DNSServiceErrorType DNSSD_API DNSServiceResolve
+	(
+	DNSServiceRef                 *sdRef,
+	DNSServiceFlags               flags,
+	uint32_t                      interfaceIndex,
+	const char                    *name,
+	const char                    *regtype,
+	const char                    *domain,
+	DNSServiceResolveReply        callBack,
+	void                          *context
+	)
+	{
+	char *ptr;
+	size_t len;
+	ipc_msg_hdr *hdr;
+	DNSServiceErrorType err;
+
+	if (!name || !regtype || !domain || !callBack) return kDNSServiceErr_BadParam;
+
+	// Need a real InterfaceID for WakeOnResolve
+	if ((flags & kDNSServiceFlagsWakeOnResolve) != 0 &&
+		((interfaceIndex == kDNSServiceInterfaceIndexAny) ||
+		(interfaceIndex == kDNSServiceInterfaceIndexLocalOnly) ||
+		(interfaceIndex == kDNSServiceInterfaceIndexUnicast) ||
+		(interfaceIndex == kDNSServiceInterfaceIndexP2P)))
+		{
+		return kDNSServiceErr_BadParam;
+		}
+		
+	err = ConnectToServer(sdRef, flags, resolve_request, handle_resolve_response, callBack, context);
+	if (err) return err;	// On error ConnectToServer leaves *sdRef set to NULL
+
+	// Calculate total message length
+	len = sizeof(flags);
+	len += sizeof(interfaceIndex);
+	len += strlen(name) + 1;
+	len += strlen(regtype) + 1;
+	len += strlen(domain) + 1;
+
+	hdr = create_hdr(resolve_request, &len, &ptr, (*sdRef)->primary ? 1 : 0, *sdRef);
+	if (!hdr) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; return kDNSServiceErr_NoMemory; }
+
+	put_flags(flags, &ptr);
+	put_uint32(interfaceIndex, &ptr);
+	put_string(name, &ptr);
+	put_string(regtype, &ptr);
+	put_string(domain, &ptr);
+
+	err = deliver_request(hdr, *sdRef);		// Will free hdr for us
+	if (err) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; }
+	return err;
+	}
+
+static void handle_query_response(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *data, const char *const end)
+	{
+	uint32_t ttl;
+	char name[kDNSServiceMaxDomainName];
+	uint16_t rrtype, rrclass, rdlen;
+	const char *rdata;
+
+	get_string(&data, end, name, kDNSServiceMaxDomainName);
+	rrtype  = get_uint16(&data, end);
+	rrclass = get_uint16(&data, end);
+	rdlen   = get_uint16(&data, end);
+	rdata   = get_rdata(&data, end, rdlen);
+	ttl     = get_uint32(&data, end);
+
+	if (!data) syslog(LOG_WARNING, "dnssd_clientstub handle_query_response: error reading result from daemon");
+	else ((DNSServiceQueryRecordReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, name, rrtype, rrclass, rdlen, rdata, ttl, sdr->AppContext);
+	// MUST NOT touch sdr after invoking AppCallback -- client is allowed to dispose it from within callback function
+	}
+
+DNSServiceErrorType DNSSD_API DNSServiceQueryRecord
+	(
+	DNSServiceRef              *sdRef,
+	DNSServiceFlags             flags,
+	uint32_t                    interfaceIndex,
+	const char                 *name,
+	uint16_t                    rrtype,
+	uint16_t                    rrclass,
+	DNSServiceQueryRecordReply  callBack,
+	void                       *context
+	)
+	{
+	char *ptr;
+	size_t len;
+	ipc_msg_hdr *hdr;
+	DNSServiceErrorType err = ConnectToServer(sdRef, flags, query_request, handle_query_response, callBack, context);
+	if (err) return err;	// On error ConnectToServer leaves *sdRef set to NULL
+
+	if (!name) name = "\0";
+
+	// Calculate total message length
+	len = sizeof(flags);
+	len += sizeof(uint32_t);  // interfaceIndex
+	len += strlen(name) + 1;
+	len += 2 * sizeof(uint16_t);  // rrtype, rrclass
+
+	hdr = create_hdr(query_request, &len, &ptr, (*sdRef)->primary ? 1 : 0, *sdRef);
+	if (!hdr) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; return kDNSServiceErr_NoMemory; }
+
+	put_flags(flags, &ptr);
+	put_uint32(interfaceIndex, &ptr);
+	put_string(name, &ptr);
+	put_uint16(rrtype, &ptr);
+	put_uint16(rrclass, &ptr);
+
+	err = deliver_request(hdr, *sdRef);		// Will free hdr for us
+	if (err) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; }
+	return err;
+	}
+
+static void handle_addrinfo_response(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *data, const char *const end)
+	{
+	char hostname[kDNSServiceMaxDomainName];
+	uint16_t rrtype, rrclass, rdlen;
+	const char *rdata;
+	uint32_t ttl;
+
+	get_string(&data, end, hostname, kDNSServiceMaxDomainName);
+	rrtype  = get_uint16(&data, end);
+	rrclass = get_uint16(&data, end);
+	rdlen   = get_uint16(&data, end);
+	rdata   = get_rdata (&data, end, rdlen);
+	ttl     = get_uint32(&data, end);
+
+	// We only generate client callbacks for A and AAAA results (including NXDOMAIN results for
+	// those types, if the client has requested those with the kDNSServiceFlagsReturnIntermediates).
+	// Other result types, specifically CNAME referrals, are not communicated to the client, because
+	// the DNSServiceGetAddrInfoReply interface doesn't have any meaningful way to communiate CNAME referrals.
+	if (!data) syslog(LOG_WARNING, "dnssd_clientstub handle_addrinfo_response: error reading result from daemon");
+	else if (rrtype == kDNSServiceType_A || rrtype == kDNSServiceType_AAAA)
+		{
+		struct sockaddr_in  sa4;
+		struct sockaddr_in6 sa6;
+		const struct sockaddr *const sa = (rrtype == kDNSServiceType_A) ? (struct sockaddr*)&sa4 : (struct sockaddr*)&sa6;
+		if (rrtype == kDNSServiceType_A)
+			{
+			memset(&sa4, 0, sizeof(sa4));
+			#ifndef NOT_HAVE_SA_LEN
+			sa4.sin_len = sizeof(struct sockaddr_in);
+			#endif
+			sa4.sin_family = AF_INET;
+			//  sin_port   = 0;
+			if (!cbh->cb_err) memcpy(&sa4.sin_addr, rdata, rdlen);
+			}
+		else
+			{
+			memset(&sa6, 0, sizeof(sa6));
+			#ifndef NOT_HAVE_SA_LEN
+			sa6.sin6_len = sizeof(struct sockaddr_in6);
+			#endif
+			sa6.sin6_family     = AF_INET6;
+			//  sin6_port     = 0;
+			//  sin6_flowinfo = 0;
+			//  sin6_scope_id = 0;
+			if (!cbh->cb_err)
+				{
+				memcpy(&sa6.sin6_addr, rdata, rdlen);
+				if (IN6_IS_ADDR_LINKLOCAL(&sa6.sin6_addr)) sa6.sin6_scope_id = cbh->cb_interface;
+				}
+			}
+		((DNSServiceGetAddrInfoReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, hostname, sa, ttl, sdr->AppContext);
+		// MUST NOT touch sdr after invoking AppCallback -- client is allowed to dispose it from within callback function
+		}
+	}
+
+DNSServiceErrorType DNSSD_API DNSServiceGetAddrInfo
+	(
+	DNSServiceRef                    *sdRef,
+	DNSServiceFlags                  flags,
+	uint32_t                         interfaceIndex,
+	uint32_t                         protocol,
+	const char                       *hostname,
+	DNSServiceGetAddrInfoReply       callBack,
+	void                             *context          /* may be NULL */
+	)
+	{
+	char *ptr;
+	size_t len;
+	ipc_msg_hdr *hdr;
+	DNSServiceErrorType err;
+
+	if (!hostname) return kDNSServiceErr_BadParam;
+
+	err = ConnectToServer(sdRef, flags, addrinfo_request, handle_addrinfo_response, callBack, context);
+	if (err) return err;	// On error ConnectToServer leaves *sdRef set to NULL
+
+	// Calculate total message length
+	len = sizeof(flags);
+	len += sizeof(uint32_t);      // interfaceIndex
+	len += sizeof(uint32_t);      // protocol
+	len += strlen(hostname) + 1;
+
+	hdr = create_hdr(addrinfo_request, &len, &ptr, (*sdRef)->primary ? 1 : 0, *sdRef);
+	if (!hdr) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; return kDNSServiceErr_NoMemory; }
+
+	put_flags(flags, &ptr);
+	put_uint32(interfaceIndex, &ptr);
+	put_uint32(protocol, &ptr);
+	put_string(hostname, &ptr);
+
+	err = deliver_request(hdr, *sdRef);		// Will free hdr for us
+	if (err) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; }
+	return err;
+	}
+	
+static void handle_browse_response(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *data, const char *const end)
+	{
+	char replyName[256], replyType[kDNSServiceMaxDomainName], replyDomain[kDNSServiceMaxDomainName];
+	get_string(&data, end, replyName, 256);
+	get_string(&data, end, replyType, kDNSServiceMaxDomainName);
+	get_string(&data, end, replyDomain, kDNSServiceMaxDomainName);
+	if (!data) syslog(LOG_WARNING, "dnssd_clientstub handle_browse_response: error reading result from daemon");
+	else ((DNSServiceBrowseReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, replyName, replyType, replyDomain, sdr->AppContext);
+	// MUST NOT touch sdr after invoking AppCallback -- client is allowed to dispose it from within callback function
+	}
+
+DNSServiceErrorType DNSSD_API DNSServiceBrowse
+	(
+	DNSServiceRef         *sdRef,
+	DNSServiceFlags        flags,
+	uint32_t               interfaceIndex,
+	const char            *regtype,
+	const char            *domain,
+	DNSServiceBrowseReply  callBack,
+	void                  *context
+	)
+	{
+	char *ptr;
+	size_t len;
+	ipc_msg_hdr *hdr;
+	DNSServiceErrorType err = ConnectToServer(sdRef, flags, browse_request, handle_browse_response, callBack, context);
+	if (err) return err;	// On error ConnectToServer leaves *sdRef set to NULL
+
+	if (!domain) domain = "";
+	len = sizeof(flags);
+	len += sizeof(interfaceIndex);
+	len += strlen(regtype) + 1;
+	len += strlen(domain) + 1;
+
+	hdr = create_hdr(browse_request, &len, &ptr, (*sdRef)->primary ? 1 : 0, *sdRef);
+	if (!hdr) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; return kDNSServiceErr_NoMemory; }
+
+	put_flags(flags, &ptr);
+	put_uint32(interfaceIndex, &ptr);
+	put_string(regtype, &ptr);
+	put_string(domain, &ptr);
+
+	err = deliver_request(hdr, *sdRef);		// Will free hdr for us
+	if (err) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; }
+	return err;
+	}
+
+static void handle_hostname_changed_response(
+	DNSServiceOp         *const sdr,
+	const CallbackHeader *const cbh, 
+	const char           *data, 
+	const char           *const end)
+{
+	char replyHostname[256];
+	get_string(&data, end, replyHostname, sizeof(replyHostname));
+	if (!data) syslog(LOG_WARNING,
+		"dnssd_clientstub handle_hostname_changed_response: error reading result from daemon");
+	else ((DNSHostnameChangedReply)sdr->AppCallback)(
+		sdr, cbh->cb_flags, cbh->cb_err, replyHostname, sdr->AppContext);
+}
+
+DNSServiceErrorType DNSSD_API DNSSetHostname
+(
+	DNSServiceRef           *sdRef,
+	DNSServiceFlags         flags,
+	const char              *hostname,
+	DNSHostnameChangedReply callBack,
+	void                    *context
+)
+{
+	char *ptr;
+	size_t len;
+	ipc_msg_hdr *hdr;
+	DNSServiceErrorType err = ConnectToServer(sdRef, flags, sethost_request,
+		handle_hostname_changed_response, callBack, context);
+	if (err) return err;
+	len = sizeof(flags);
+	len += strlen(hostname) + 1;
+	hdr = create_hdr(sethost_request, &len, &ptr,
+		(*sdRef)->primary ? 1 : 0, *sdRef);
+	if (!hdr)
+ 	{
+		DNSServiceRefDeallocate(*sdRef);
+		*sdRef = NULL;
+		return kDNSServiceErr_NoMemory;
+	}
+	put_flags(flags, &ptr);
+	put_string(hostname, &ptr);
+	err = deliver_request(hdr, *sdRef);
+	if (err) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; }
+	return err;
+}
+
+DNSServiceErrorType DNSSD_API DNSServiceSetDefaultDomainForUser(DNSServiceFlags flags, const char *domain);
+DNSServiceErrorType DNSSD_API DNSServiceSetDefaultDomainForUser(DNSServiceFlags flags, const char *domain)
+	{
+	DNSServiceOp *tmp;
+	char *ptr;
+	size_t len = sizeof(flags) + strlen(domain) + 1;
+	ipc_msg_hdr *hdr;
+	DNSServiceErrorType err = ConnectToServer(&tmp, 0, setdomain_request, NULL, NULL, NULL);
+	if (err) return err;
+
+	hdr = create_hdr(setdomain_request, &len, &ptr, 0, tmp);
+	if (!hdr) { DNSServiceRefDeallocate(tmp); return kDNSServiceErr_NoMemory; }
+
+	put_flags(flags, &ptr);
+	put_string(domain, &ptr);
+	err = deliver_request(hdr, tmp);		// Will free hdr for us
+	DNSServiceRefDeallocate(tmp);
+	return err;
+	}
+
+static void handle_regservice_response(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *data, const char *const end)
+	{
+	char name[256], regtype[kDNSServiceMaxDomainName], domain[kDNSServiceMaxDomainName];
+	get_string(&data, end, name, 256);
+	get_string(&data, end, regtype, kDNSServiceMaxDomainName);
+	get_string(&data, end, domain,  kDNSServiceMaxDomainName);
+	if (!data) syslog(LOG_WARNING, "dnssd_clientstub handle_regservice_response: error reading result from daemon");
+	else ((DNSServiceRegisterReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_err, name, regtype, domain, sdr->AppContext);
+	// MUST NOT touch sdr after invoking AppCallback -- client is allowed to dispose it from within callback function
+	}
+
+DNSServiceErrorType DNSSD_API DNSServiceRegister
+	(
+	DNSServiceRef                       *sdRef,
+	DNSServiceFlags                     flags,
+	uint32_t                            interfaceIndex,
+	const char                          *name,
+	const char                          *regtype,
+	const char                          *domain,
+	const char                          *host,
+	uint16_t                            PortInNetworkByteOrder,
+	uint16_t                            txtLen,
+	const void                          *txtRecord,
+	DNSServiceRegisterReply             callBack,
+	void                                *context
+	)
+	{
+	char *ptr;
+	size_t len;
+	ipc_msg_hdr *hdr;
+	DNSServiceErrorType err;
+	union { uint16_t s; u_char b[2]; } port = { PortInNetworkByteOrder };
+
+	if (!name) name = "";
+	if (!regtype) return kDNSServiceErr_BadParam;
+	if (!domain) domain = "";
+	if (!host) host = "";
+	if (!txtRecord) txtRecord = (void*)"";
+
+	// No callback must have auto-rename
+	if (!callBack && (flags & kDNSServiceFlagsNoAutoRename)) return kDNSServiceErr_BadParam;
+
+	err = ConnectToServer(sdRef, flags, reg_service_request, callBack ? handle_regservice_response : NULL, callBack, context);
+	if (err) return err;	// On error ConnectToServer leaves *sdRef set to NULL
+
+	len = sizeof(DNSServiceFlags);
+	len += sizeof(uint32_t);  // interfaceIndex
+	len += strlen(name) + strlen(regtype) + strlen(domain) + strlen(host) + 4;
+	len += 2 * sizeof(uint16_t);  // port, txtLen
+	len += txtLen;
+
+	hdr = create_hdr(reg_service_request, &len, &ptr, (*sdRef)->primary ? 1 : 0, *sdRef);
+	if (!hdr) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; return kDNSServiceErr_NoMemory; }
+	if (!callBack) hdr->ipc_flags |= IPC_FLAGS_NOREPLY;
+
+	put_flags(flags, &ptr);
+	put_uint32(interfaceIndex, &ptr);
+	put_string(name, &ptr);
+	put_string(regtype, &ptr);
+	put_string(domain, &ptr);
+	put_string(host, &ptr);
+	*ptr++ = port.b[0];
+	*ptr++ = port.b[1];
+	put_uint16(txtLen, &ptr);
+	put_rdata(txtLen, txtRecord, &ptr);
+
+	err = deliver_request(hdr, *sdRef);		// Will free hdr for us
+	if (err) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; }
+	return err;
+	}
+
+static void handle_enumeration_response(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *data, const char *const end)
+	{
+	char domain[kDNSServiceMaxDomainName];
+	get_string(&data, end, domain, kDNSServiceMaxDomainName);
+	if (!data) syslog(LOG_WARNING, "dnssd_clientstub handle_enumeration_response: error reading result from daemon");
+	else ((DNSServiceDomainEnumReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, domain, sdr->AppContext);
+	// MUST NOT touch sdr after invoking AppCallback -- client is allowed to dispose it from within callback function
+	}
+
+DNSServiceErrorType DNSSD_API DNSServiceEnumerateDomains
+	(
+	DNSServiceRef             *sdRef,
+	DNSServiceFlags            flags,
+	uint32_t                   interfaceIndex,
+	DNSServiceDomainEnumReply  callBack,
+	void                      *context
+	)
+	{
+	char *ptr;
+	size_t len;
+	ipc_msg_hdr *hdr;
+	DNSServiceErrorType err;
+
+	int f1 = (flags & kDNSServiceFlagsBrowseDomains) != 0;
+	int f2 = (flags & kDNSServiceFlagsRegistrationDomains) != 0;
+	if (f1 + f2 != 1) return kDNSServiceErr_BadParam;
+
+	err = ConnectToServer(sdRef, flags, enumeration_request, handle_enumeration_response, callBack, context);
+	if (err) return err;	// On error ConnectToServer leaves *sdRef set to NULL
+
+	len = sizeof(DNSServiceFlags);
+	len += sizeof(uint32_t);
+
+	hdr = create_hdr(enumeration_request, &len, &ptr, (*sdRef)->primary ? 1 : 0, *sdRef);
+	if (!hdr) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; return kDNSServiceErr_NoMemory; }
+
+	put_flags(flags, &ptr);
+	put_uint32(interfaceIndex, &ptr);
+
+	err = deliver_request(hdr, *sdRef);		// Will free hdr for us
+	if (err) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; }
+	return err;
+	}
+
+static void ConnectionResponse(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *const data, const char *const end)
+	{
+	DNSRecordRef rref = cbh->ipc_hdr.client_context.context;
+	(void)data; // Unused
+
+	//printf("ConnectionResponse got %d\n", cbh->ipc_hdr.op);
+	if (cbh->ipc_hdr.op != reg_record_reply_op)
+		{
+		// When using kDNSServiceFlagsShareConnection, need to search the list of associated DNSServiceOps
+		// to find the one this response is intended for, and then call through to its ProcessReply handler.
+		// We start with our first subordinate DNSServiceRef -- don't want to accidentally match the parent DNSServiceRef.
+		DNSServiceOp *op = sdr->next;
+		while (op && (op->uid.u32[0] != cbh->ipc_hdr.client_context.u32[0] || op->uid.u32[1] != cbh->ipc_hdr.client_context.u32[1]))
+			op = op->next;
+		// Note: We may sometimes not find a matching DNSServiceOp, in the case where the client has
+		// cancelled the subordinate DNSServiceOp, but there are still messages in the pipeline from the daemon
+		if (op && op->ProcessReply) op->ProcessReply(op, cbh, data, end);
+		// WARNING: Don't touch op or sdr after this -- client may have called DNSServiceRefDeallocate
+		return;
+		}
+
+	if (sdr->op == connection_request)
+		rref->AppCallback(rref->sdr, rref, cbh->cb_flags, cbh->cb_err, rref->AppContext);
+	else
+		{
+		syslog(LOG_WARNING, "dnssd_clientstub ConnectionResponse: sdr->op != connection_request");
+		rref->AppCallback(rref->sdr, rref, 0, kDNSServiceErr_Unknown, rref->AppContext);
+		}
+	// MUST NOT touch sdr after invoking AppCallback -- client is allowed to dispose it from within callback function
+	}
+
+DNSServiceErrorType DNSSD_API DNSServiceCreateConnection(DNSServiceRef *sdRef)
+	{
+	char *ptr;
+	size_t len = 0;
+	ipc_msg_hdr *hdr;
+	DNSServiceErrorType err = ConnectToServer(sdRef, 0, connection_request, ConnectionResponse, NULL, NULL);
+	if (err) return err;	// On error ConnectToServer leaves *sdRef set to NULL
+	
+	hdr = create_hdr(connection_request, &len, &ptr, 0, *sdRef);
+	if (!hdr) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; return kDNSServiceErr_NoMemory; }
+
+	err = deliver_request(hdr, *sdRef);		// Will free hdr for us
+	if (err) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; }
+	return err;
+	}
+
+DNSServiceErrorType DNSSD_API DNSServiceRegisterRecord
+	(
+	DNSServiceRef                  sdRef,
+	DNSRecordRef                  *RecordRef,
+	DNSServiceFlags                flags,
+	uint32_t                       interfaceIndex,
+	const char                    *fullname,
+	uint16_t                       rrtype,
+	uint16_t                       rrclass,
+	uint16_t                       rdlen,
+	const void                    *rdata,
+	uint32_t                       ttl,
+	DNSServiceRegisterRecordReply  callBack,
+	void                          *context
+	)
+	{
+	char *ptr;
+	size_t len;
+	ipc_msg_hdr *hdr = NULL;
+	DNSRecordRef rref = NULL;
+	DNSRecord **p;
+	int f1 = (flags & kDNSServiceFlagsShared) != 0;
+	int f2 = (flags & kDNSServiceFlagsUnique) != 0;
+	if (f1 + f2 != 1) return kDNSServiceErr_BadParam;
+
+	if (!sdRef) { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRegisterRecord called with NULL DNSServiceRef"); return kDNSServiceErr_BadParam; }
+
+	if (!DNSServiceRefValid(sdRef))
+		{
+		syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRegisterRecord called with invalid DNSServiceRef %p %08X %08X", sdRef, sdRef->sockfd, sdRef->validator);
+		return kDNSServiceErr_BadReference;
+		}
+
+	if (sdRef->op != connection_request)
+		{
+		syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRegisterRecord called with non-DNSServiceCreateConnection DNSServiceRef %p %d", sdRef, sdRef->op);
+		return kDNSServiceErr_BadReference;
+		}
+
+	*RecordRef = NULL;
+
+	len = sizeof(DNSServiceFlags);
+	len += 2 * sizeof(uint32_t);  // interfaceIndex, ttl
+	len += 3 * sizeof(uint16_t);  // rrtype, rrclass, rdlen
+	len += strlen(fullname) + 1;
+	len += rdlen;
+
+	hdr = create_hdr(reg_record_request, &len, &ptr, 1, sdRef);
+	if (!hdr) return kDNSServiceErr_NoMemory;
+
+	put_flags(flags, &ptr);
+	put_uint32(interfaceIndex, &ptr);
+	put_string(fullname, &ptr);
+	put_uint16(rrtype, &ptr);
+	put_uint16(rrclass, &ptr);
+	put_uint16(rdlen, &ptr);
+	put_rdata(rdlen, rdata, &ptr);
+	put_uint32(ttl, &ptr);
+
+	rref = malloc(sizeof(DNSRecord));
+	if (!rref) { free(hdr); return kDNSServiceErr_NoMemory; }
+	rref->AppContext = context;
+	rref->AppCallback = callBack;
+	rref->record_index = sdRef->max_index++;
+	rref->sdr = sdRef;
+	rref->recnext = NULL;
+	*RecordRef = rref;
+	hdr->client_context.context = rref;
+	hdr->reg_index = rref->record_index;
+
+	p = &(sdRef)->rec;
+	while (*p) p = &(*p)->recnext;
+	*p = rref;
+
+	return deliver_request(hdr, sdRef);		// Will free hdr for us
+	}
+
+// sdRef returned by DNSServiceRegister()
+DNSServiceErrorType DNSSD_API DNSServiceAddRecord
+	(
+	DNSServiceRef    sdRef,
+	DNSRecordRef    *RecordRef,
+	DNSServiceFlags  flags,
+	uint16_t         rrtype,
+	uint16_t         rdlen,
+	const void      *rdata,
+	uint32_t         ttl
+	)
+	{
+	ipc_msg_hdr *hdr;
+	size_t len = 0;
+	char *ptr;
+	DNSRecordRef rref;
+	DNSRecord **p;
+
+	if (!sdRef)     { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceAddRecord called with NULL DNSServiceRef");        return kDNSServiceErr_BadParam; }
+	if (!RecordRef) { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceAddRecord called with NULL DNSRecordRef pointer"); return kDNSServiceErr_BadParam; }
+	if (sdRef->op != reg_service_request)
+		{
+		syslog(LOG_WARNING, "dnssd_clientstub DNSServiceAddRecord called with non-DNSServiceRegister DNSServiceRef %p %d", sdRef, sdRef->op);
+		return kDNSServiceErr_BadReference;
+		}
+
+	if (!DNSServiceRefValid(sdRef))
+		{
+		syslog(LOG_WARNING, "dnssd_clientstub DNSServiceAddRecord called with invalid DNSServiceRef %p %08X %08X", sdRef, sdRef->sockfd, sdRef->validator);
+		return kDNSServiceErr_BadReference;
+		}
+
+	*RecordRef = NULL;
+
+	len += 2 * sizeof(uint16_t);  // rrtype, rdlen
+	len += rdlen;
+	len += sizeof(uint32_t);
+	len += sizeof(DNSServiceFlags);
+
+	hdr = create_hdr(add_record_request, &len, &ptr, 1, sdRef);
+	if (!hdr) return kDNSServiceErr_NoMemory;
+	put_flags(flags, &ptr);
+	put_uint16(rrtype, &ptr);
+	put_uint16(rdlen, &ptr);
+	put_rdata(rdlen, rdata, &ptr);
+	put_uint32(ttl, &ptr);
+
+	rref = malloc(sizeof(DNSRecord));
+	if (!rref) { free(hdr); return kDNSServiceErr_NoMemory; }
+	rref->AppContext = NULL;
+	rref->AppCallback = NULL;
+	rref->record_index = sdRef->max_index++;
+	rref->sdr = sdRef;
+	rref->recnext = NULL;
+	*RecordRef = rref;
+	hdr->reg_index = rref->record_index;
+
+	p = &(sdRef)->rec;
+	while (*p) p = &(*p)->recnext;
+	*p = rref;
+
+	return deliver_request(hdr, sdRef);		// Will free hdr for us
+	}
+
+// DNSRecordRef returned by DNSServiceRegisterRecord or DNSServiceAddRecord
+DNSServiceErrorType DNSSD_API DNSServiceUpdateRecord
+	(
+	DNSServiceRef    sdRef,
+	DNSRecordRef     RecordRef,
+	DNSServiceFlags  flags,
+	uint16_t         rdlen,
+	const void      *rdata,
+	uint32_t         ttl
+	)
+	{
+	ipc_msg_hdr *hdr;
+	size_t len = 0;
+	char *ptr;
+
+	if (!sdRef) { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceUpdateRecord called with NULL DNSServiceRef"); return kDNSServiceErr_BadParam; }
+
+	if (!DNSServiceRefValid(sdRef))
+		{
+		syslog(LOG_WARNING, "dnssd_clientstub DNSServiceUpdateRecord called with invalid DNSServiceRef %p %08X %08X", sdRef, sdRef->sockfd, sdRef->validator);
+		return kDNSServiceErr_BadReference;
+		}
+
+	// Note: RecordRef is allowed to be NULL
+
+	len += sizeof(uint16_t);
+	len += rdlen;
+	len += sizeof(uint32_t);
+	len += sizeof(DNSServiceFlags);
+
+	hdr = create_hdr(update_record_request, &len, &ptr, 1, sdRef);
+	if (!hdr) return kDNSServiceErr_NoMemory;
+	hdr->reg_index = RecordRef ? RecordRef->record_index : TXT_RECORD_INDEX;
+	put_flags(flags, &ptr);
+	put_uint16(rdlen, &ptr);
+	put_rdata(rdlen, rdata, &ptr);
+	put_uint32(ttl, &ptr);
+	return deliver_request(hdr, sdRef);		// Will free hdr for us
+	}
+
+DNSServiceErrorType DNSSD_API DNSServiceRemoveRecord
+	(
+	DNSServiceRef    sdRef,
+	DNSRecordRef     RecordRef,
+	DNSServiceFlags  flags
+	)
+	{
+	ipc_msg_hdr *hdr;
+	size_t len = 0;
+	char *ptr;
+	DNSServiceErrorType err;
+
+	if (!sdRef)            { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRemoveRecord called with NULL DNSServiceRef"); return kDNSServiceErr_BadParam; }
+	if (!RecordRef)        { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRemoveRecord called with NULL DNSRecordRef");  return kDNSServiceErr_BadParam; }
+	if (!sdRef->max_index) { syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRemoveRecord called with bad DNSServiceRef");  return kDNSServiceErr_BadReference; }
+
+	if (!DNSServiceRefValid(sdRef))
+		{
+		syslog(LOG_WARNING, "dnssd_clientstub DNSServiceRemoveRecord called with invalid DNSServiceRef %p %08X %08X", sdRef, sdRef->sockfd, sdRef->validator);
+		return kDNSServiceErr_BadReference;
+		}
+
+	len += sizeof(flags);
+	hdr = create_hdr(remove_record_request, &len, &ptr, 1, sdRef);
+	if (!hdr) return kDNSServiceErr_NoMemory;
+	hdr->reg_index = RecordRef->record_index;
+	put_flags(flags, &ptr);
+	err = deliver_request(hdr, sdRef);		// Will free hdr for us
+	if (!err)
+		{
+		// This RecordRef could have been allocated in DNSServiceRegisterRecord or DNSServiceAddRecord.
+		// If so, delink from the list before freeing
+		DNSRecord **p = &sdRef->rec;
+		while (*p && *p != RecordRef) p = &(*p)->recnext;
+		if (*p) *p = RecordRef->recnext;
+		free(RecordRef);
+		}
+	return err;
+	}
+
+DNSServiceErrorType DNSSD_API DNSServiceReconfirmRecord
+	(
+	DNSServiceFlags  flags,
+	uint32_t         interfaceIndex,
+	const char      *fullname,
+	uint16_t         rrtype,
+	uint16_t         rrclass,
+	uint16_t         rdlen,
+	const void      *rdata
+	)
+	{
+	char *ptr;
+	size_t len;
+	ipc_msg_hdr *hdr;
+	DNSServiceOp *tmp;
+
+	DNSServiceErrorType err = ConnectToServer(&tmp, flags, reconfirm_record_request, NULL, NULL, NULL);
+	if (err) return err;
+
+	len = sizeof(DNSServiceFlags);
+	len += sizeof(uint32_t);
+	len += strlen(fullname) + 1;
+	len += 3 * sizeof(uint16_t);
+	len += rdlen;
+	hdr = create_hdr(reconfirm_record_request, &len, &ptr, 0, tmp);
+	if (!hdr) { DNSServiceRefDeallocate(tmp); return kDNSServiceErr_NoMemory; }
+
+	put_flags(flags, &ptr);
+	put_uint32(interfaceIndex, &ptr);
+	put_string(fullname, &ptr);
+	put_uint16(rrtype, &ptr);
+	put_uint16(rrclass, &ptr);
+	put_uint16(rdlen, &ptr);
+	put_rdata(rdlen, rdata, &ptr);
+
+	err = deliver_request(hdr, tmp);		// Will free hdr for us
+	DNSServiceRefDeallocate(tmp);
+	return err;
+	}
+
+static void handle_port_mapping_response(DNSServiceOp *const sdr, const CallbackHeader *const cbh, const char *data, const char *const end)
+	{
+	union { uint32_t l; u_char b[4]; } addr;
+	uint8_t protocol;
+	union { uint16_t s; u_char b[2]; } internalPort;
+	union { uint16_t s; u_char b[2]; } externalPort;
+	uint32_t ttl;
+
+	if (!data || data + 13 > end) goto fail;
+
+	addr        .b[0] = *data++;
+	addr        .b[1] = *data++;
+	addr        .b[2] = *data++;
+	addr        .b[3] = *data++;
+	protocol          = *data++;
+	internalPort.b[0] = *data++;
+	internalPort.b[1] = *data++;
+	externalPort.b[0] = *data++;
+	externalPort.b[1] = *data++;
+	ttl               = get_uint32(&data, end);
+	if (!data) goto fail;
+
+	((DNSServiceNATPortMappingReply)sdr->AppCallback)(sdr, cbh->cb_flags, cbh->cb_interface, cbh->cb_err, addr.l, protocol, internalPort.s, externalPort.s, ttl, sdr->AppContext);
+	return;
+	// MUST NOT touch sdr after invoking AppCallback -- client is allowed to dispose it from within callback function
+
+fail:
+	syslog(LOG_WARNING, "dnssd_clientstub handle_port_mapping_response: error reading result from daemon");
+	}
+
+DNSServiceErrorType DNSSD_API DNSServiceNATPortMappingCreate
+	(
+	DNSServiceRef                       *sdRef,
+	DNSServiceFlags                     flags,
+	uint32_t                            interfaceIndex,
+	uint32_t                            protocol,     /* TCP and/or UDP */
+	uint16_t                            internalPortInNetworkByteOrder,
+	uint16_t                            externalPortInNetworkByteOrder,
+	uint32_t                            ttl,          /* time to live in seconds */
+	DNSServiceNATPortMappingReply       callBack,
+	void                                *context      /* may be NULL */
+	)
+	{
+	char *ptr;
+	size_t len;
+	ipc_msg_hdr *hdr;
+	union { uint16_t s; u_char b[2]; } internalPort = { internalPortInNetworkByteOrder };
+	union { uint16_t s; u_char b[2]; } externalPort = { externalPortInNetworkByteOrder };
+
+	DNSServiceErrorType err = ConnectToServer(sdRef, flags, port_mapping_request, handle_port_mapping_response, callBack, context);
+	if (err) return err;	// On error ConnectToServer leaves *sdRef set to NULL
+
+	len = sizeof(flags);
+	len += sizeof(interfaceIndex);
+	len += sizeof(protocol);
+	len += sizeof(internalPort);
+	len += sizeof(externalPort);
+	len += sizeof(ttl);
+
+	hdr = create_hdr(port_mapping_request, &len, &ptr, (*sdRef)->primary ? 1 : 0, *sdRef);
+	if (!hdr) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; return kDNSServiceErr_NoMemory; }
+
+	put_flags(flags, &ptr);
+	put_uint32(interfaceIndex, &ptr);
+	put_uint32(protocol, &ptr);
+	*ptr++ = internalPort.b[0];
+	*ptr++ = internalPort.b[1];
+	*ptr++ = externalPort.b[0];
+	*ptr++ = externalPort.b[1];
+	put_uint32(ttl, &ptr);
+
+	err = deliver_request(hdr, *sdRef);		// Will free hdr for us
+	if (err) { DNSServiceRefDeallocate(*sdRef); *sdRef = NULL; }
+	return err;
+	}
+
+#if _DNS_SD_LIBDISPATCH
+DNSServiceErrorType DNSSD_API DNSServiceSetDispatchQueue
+	(
+	DNSServiceRef service,
+	dispatch_queue_t queue
+	)
+	{
+	int dnssd_fd  = DNSServiceRefSockFD(service);
+	if (dnssd_fd == dnssd_InvalidSocket) return kDNSServiceErr_BadParam;
+	if (!queue)
+		{
+		syslog(LOG_WARNING, "dnssd_clientstub: DNSServiceSetDispatchQueue dispatch queue NULL");
+		return kDNSServiceErr_BadParam;
+		}
+	if (service->disp_queue)
+		{
+		syslog(LOG_WARNING, "dnssd_clientstub DNSServiceSetDispatchQueue dispatch queue set already");
+		return kDNSServiceErr_BadParam;
+		}
+	if (service->disp_source)
+		{
+		syslog(LOG_WARNING, "DNSServiceSetDispatchQueue dispatch source set already");
+		return kDNSServiceErr_BadParam;
+		}
+	service->disp_source = dispatch_source_create(DISPATCH_SOURCE_TYPE_READ, dnssd_fd, 0, queue);
+	if (!service->disp_source)
+		{
+		syslog(LOG_WARNING, "DNSServiceSetDispatchQueue dispatch_source_create failed");
+		return kDNSServiceErr_NoMemory;
+		}
+	service->disp_queue = queue;
+	dispatch_source_set_event_handler(service->disp_source, ^{DNSServiceProcessResult(service);});
+	dispatch_source_set_cancel_handler(service->disp_source, ^{dnssd_close(dnssd_fd);});
+	dispatch_resume(service->disp_source);
+	return kDNSServiceErr_NoError;
+	}
+#endif // _DNS_SD_LIBDISPATCH
diff --git a/mdnsresponder/mDNSShared/dnssd_ipc.c b/mdnsresponder/mDNSShared/dnssd_ipc.c
new file mode 100644
index 0000000..131510c
--- /dev/null
+++ b/mdnsresponder/mDNSShared/dnssd_ipc.c
@@ -0,0 +1,161 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2003-2004, Apple Computer, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of its
+ *     contributors may be used to endorse or promote products derived from this
+ *     software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "dnssd_ipc.h"
+
+#if defined(_WIN32)
+
+char *win32_strerror(int inErrorCode)
+	{
+	static char buffer[1024];
+	DWORD       n;
+	memset(buffer, 0, sizeof(buffer));
+	n = FormatMessageA(
+			FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS,
+			NULL,
+			(DWORD) inErrorCode,
+			MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
+			buffer,
+			sizeof(buffer),
+			NULL);
+	if (n > 0)
+		{
+		// Remove any trailing CR's or LF's since some messages have them.
+		while ((n > 0) && isspace(((unsigned char *) buffer)[n - 1]))
+			buffer[--n] = '\0';
+		}
+	return buffer;
+	}
+
+#endif
+
+void put_uint32(const uint32_t l, char **ptr)
+	{
+	(*ptr)[0] = (char)((l >> 24) &  0xFF);
+	(*ptr)[1] = (char)((l >> 16) &  0xFF);
+	(*ptr)[2] = (char)((l >>  8) &  0xFF);
+	(*ptr)[3] = (char)((l      ) &  0xFF);
+	*ptr += sizeof(uint32_t);
+	}
+
+uint32_t get_uint32(const char **ptr, const char *end)
+	{
+	if (!*ptr || *ptr + sizeof(uint32_t) > end)
+		{
+		*ptr = NULL;
+		return(0);
+		}
+	else
+		{
+		uint8_t *p = (uint8_t*) *ptr;
+		*ptr += sizeof(uint32_t);
+		return((uint32_t) ((uint32_t)p[0] << 24 | (uint32_t)p[1] << 16 | (uint32_t)p[2] << 8 | p[3]));
+		}
+	}
+
+void put_uint16(uint16_t s, char **ptr)
+	{
+	(*ptr)[0] = (char)((s >>  8) &  0xFF);
+	(*ptr)[1] = (char)((s      ) &  0xFF);
+	*ptr += sizeof(uint16_t);
+	}
+
+uint16_t get_uint16(const char **ptr, const char *end)
+	{
+	if (!*ptr || *ptr + sizeof(uint16_t) > end)
+		{
+		*ptr = NULL;
+		return(0);
+		}
+	else
+		{
+		uint8_t *p = (uint8_t*) *ptr;
+		*ptr += sizeof(uint16_t);
+		return((uint16_t) ((uint16_t)p[0] << 8 | p[1]));
+		}
+	}
+
+int put_string(const char *str, char **ptr)
+	{
+	if (!str) str = "";
+	strcpy(*ptr, str);
+	*ptr += strlen(str) + 1;
+	return 0;
+	}
+
+int get_string(const char **ptr, const char *const end, char *buffer, int buflen)
+	{
+	if (!*ptr)
+		{
+		*buffer = 0;
+		return(-1);
+		}
+	else
+		{
+		char *lim = buffer + buflen;	// Calculate limit
+		while (*ptr < end && buffer < lim)
+			{
+			char c = *buffer++ = *(*ptr)++;
+			if (c == 0) return(0);		// Success
+			}
+		if (buffer == lim) buffer--;
+		*buffer = 0;					// Failed, so terminate string,
+		*ptr = NULL;					// clear pointer,
+		return(-1);						// and return failure indication
+		}
+	}
+
+void put_rdata(const int rdlen, const unsigned char *rdata, char **ptr)
+	{
+	memcpy(*ptr, rdata, rdlen);
+	*ptr += rdlen;
+	}
+
+const char *get_rdata(const char **ptr, const char *end, int rdlen)
+	{
+	if (!*ptr || *ptr + rdlen > end)
+		{
+		*ptr = NULL;
+		return(0);
+		}
+	else
+		{
+		const char *rd = *ptr;
+		*ptr += rdlen;
+		return rd;
+		}
+	}
+
+void ConvertHeaderBytes(ipc_msg_hdr *hdr)
+	{
+	hdr->version   = htonl(hdr->version);
+	hdr->datalen   = htonl(hdr->datalen);
+	hdr->ipc_flags = htonl(hdr->ipc_flags);
+	hdr->op        = htonl(hdr->op );
+	hdr->reg_index = htonl(hdr->reg_index);
+	}
diff --git a/mdnsresponder/mDNSShared/dnssd_ipc.h b/mdnsresponder/mDNSShared/dnssd_ipc.h
new file mode 100644
index 0000000..0c8695b
--- /dev/null
+++ b/mdnsresponder/mDNSShared/dnssd_ipc.h
@@ -0,0 +1,219 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2003-2004, Apple Computer, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of its
+ *     contributors may be used to endorse or promote products derived from this
+ *     software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef DNSSD_IPC_H
+#define DNSSD_IPC_H
+
+#include "dns_sd.h"
+
+//
+// Common cross platform services
+//
+#if defined(WIN32)
+#	include <winsock2.h>
+#	define dnssd_InvalidSocket	INVALID_SOCKET
+#	define dnssd_SocketValid(s) ((s) != INVALID_SOCKET)
+#	define dnssd_EWOULDBLOCK	WSAEWOULDBLOCK
+#	define dnssd_EINTR			WSAEINTR
+#	define dnssd_ECONNRESET		WSAECONNRESET
+#	define dnssd_sock_t			SOCKET
+#	define dnssd_socklen_t		int
+#	define dnssd_close(sock)	closesocket(sock)
+#	define dnssd_errno			WSAGetLastError()
+#	define dnssd_strerror(X)	win32_strerror(X)
+#	define ssize_t				int
+#	define getpid				_getpid
+#	define unlink				_unlink
+extern char *win32_strerror(int inErrorCode);
+#else
+#	include <sys/types.h>
+#	include <unistd.h>
+#ifdef __ANDROID__
+#	include <sys/socket.h>
+#endif  // __ANDROID__
+#	include <sys/un.h>
+#	include <string.h>
+#	include <stdio.h>
+#	include <stdlib.h>
+#	include <sys/stat.h>
+#ifndef __ANDROID__
+#	include <sys/socket.h>
+#endif  // !__ANDROID__
+#	include <netinet/in.h>
+#	define dnssd_InvalidSocket	-1
+#	define dnssd_SocketValid(s) ((s) >= 0)
+#	define dnssd_EWOULDBLOCK	EWOULDBLOCK
+#	define dnssd_EINTR			EINTR
+#	define dnssd_ECONNRESET		ECONNRESET
+#	define dnssd_EPIPE			EPIPE
+#	define dnssd_sock_t			int
+#	define dnssd_socklen_t		unsigned int
+#	define dnssd_close(sock)	close(sock)
+#	define dnssd_errno			errno
+#	define dnssd_strerror(X)	strerror(X)
+#endif
+
+#if defined(USE_TCP_LOOPBACK)
+#	define AF_DNSSD				AF_INET
+#	define MDNS_TCP_SERVERADDR	"127.0.0.1"
+#	define MDNS_TCP_SERVERPORT	5354
+#	define LISTENQ				5
+#	define dnssd_sockaddr_t		struct sockaddr_in
+#else
+#	define AF_DNSSD				AF_LOCAL
+#	ifndef MDNS_UDS_SERVERPATH
+#		define MDNS_UDS_SERVERPATH	"/var/run/mDNSResponder"
+#	endif
+#	define LISTENQ				100
+    // longest legal control path length
+#	define MAX_CTLPATH			256
+#	define dnssd_sockaddr_t		struct sockaddr_un
+#endif
+
+// Compatibility workaround
+#ifndef AF_LOCAL
+#define	AF_LOCAL	AF_UNIX
+#endif
+
+// General UDS constants
+#define TXT_RECORD_INDEX ((uint32_t)(-1))	// record index for default text record
+
+// IPC data encoding constants and types
+#define VERSION 1
+#define IPC_FLAGS_NOREPLY 1	// set flag if no asynchronous replies are to be sent to client
+
+// Structure packing macro. If we're not using GNUC, it's not fatal. Most compilers naturally pack the on-the-wire
+// structures correctly anyway, so a plain "struct" is usually fine. In the event that structures are not packed
+// correctly, our compile-time assertion checks will catch it and prevent inadvertent generation of non-working code.
+#ifndef packedstruct
+ #if ((__GNUC__ > 2) || ((__GNUC__ == 2) && (__GNUC_MINOR__ >= 9)))
+  #define packedstruct struct __attribute__((__packed__))
+  #define packedunion  union  __attribute__((__packed__))
+ #else
+  #define packedstruct struct
+  #define packedunion  union
+ #endif
+#endif
+
+typedef enum
+    {
+    request_op_none = 0,	// No request yet received on this connection
+    connection_request = 1,	// connected socket via DNSServiceConnect()
+    reg_record_request,		// reg/remove record only valid for connected sockets
+    remove_record_request,
+    enumeration_request,
+    reg_service_request,
+    browse_request,
+    resolve_request,
+    query_request,
+    reconfirm_record_request,
+    add_record_request,
+    update_record_request,
+    setdomain_request,		// Up to here is in Tiger and B4W 1.0.3
+	getproperty_request,	// New in B4W 1.0.4
+    port_mapping_request,	// New in Leopard and B4W 2.0
+	addrinfo_request,
+	send_bpf,				// New in SL
+    sethost_request,
+	cancel_request = 63
+    } request_op_t;
+
+typedef enum
+    {
+    enumeration_reply_op = 64,
+    reg_service_reply_op,
+    browse_reply_op,
+    resolve_reply_op,
+    query_reply_op,
+    reg_record_reply_op,	// Up to here is in Tiger and B4W 1.0.3
+    getproperty_reply_op,	// New in B4W 1.0.4
+    port_mapping_reply_op,	// New in Leopard and B4W 2.0
+	addrinfo_reply_op,
+    sethost_reply,
+    } reply_op_t;
+
+#if defined(_WIN64)
+#	pragma pack(4)
+#endif
+
+// Define context object big enough to hold a 64-bit pointer,
+// to accomodate 64-bit clients communicating with 32-bit daemon.
+// There's no reason for the daemon to ever be a 64-bit process, but its clients might be
+typedef packedunion
+    {
+    void *context;
+    uint32_t u32[2];
+    } client_context_t;
+
+typedef packedstruct
+    {
+    uint32_t version;
+    uint32_t datalen;
+    uint32_t ipc_flags;
+    uint32_t op;		// request_op_t or reply_op_t
+    client_context_t client_context; // context passed from client, returned by server in corresponding reply
+    uint32_t reg_index;            // identifier for a record registered via DNSServiceRegisterRecord() on a
+    // socket connected by DNSServiceCreateConnection().  Must be unique in the scope of the connection, such that and
+    // index/socket pair uniquely identifies a record.  (Used to select records for removal by DNSServiceRemoveRecord())
+    } ipc_msg_hdr;
+
+// routines to write to and extract data from message buffers.
+// caller responsible for bounds checking.
+// ptr is the address of the pointer to the start of the field.
+// it is advanced to point to the next field, or the end of the message
+
+void put_uint32(const uint32_t l, char **ptr);
+uint32_t get_uint32(const char **ptr, const char *end);
+
+void put_uint16(uint16_t s, char **ptr);
+uint16_t get_uint16(const char **ptr, const char *end);
+
+#define put_flags put_uint32
+#define get_flags get_uint32
+
+#define put_error_code put_uint32
+#define get_error_code get_uint32
+
+int put_string(const char *str, char **ptr);
+int get_string(const char **ptr, const char *const end, char *buffer, int buflen);
+
+void put_rdata(const int rdlen, const unsigned char *rdata, char **ptr);
+const char *get_rdata(const char **ptr, const char *end, int rdlen);  // return value is rdata pointed to by *ptr -
+                                         // rdata is not copied from buffer.
+
+void ConvertHeaderBytes(ipc_msg_hdr *hdr);
+
+struct CompileTimeAssertionChecks_dnssd_ipc
+	{
+	// Check that the compiler generated our on-the-wire packet format structure definitions
+	// properly packed, without adding padding bytes to align fields on 32-bit or 64-bit boundaries.
+	char assert0[(sizeof(client_context_t) ==  8) ? 1 : -1];
+	char assert1[(sizeof(ipc_msg_hdr)      == 28) ? 1 : -1];
+	};
+
+#endif // DNSSD_IPC_H
diff --git a/mdnsresponder/mDNSShared/mDNS.1 b/mdnsresponder/mDNSShared/mDNS.1
new file mode 100644
index 0000000..fd85c9c
--- /dev/null
+++ b/mdnsresponder/mDNSShared/mDNS.1
@@ -0,0 +1,198 @@
+.\" -*- tab-width: 4 -*-
+.\" 
+.\" Copyright (c) 2004 Apple Computer, Inc. All Rights Reserved.
+.\" 
+.\" Licensed 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.
+.\"
+.Dd April 2004              \" Date
+.Dt mDNS 1                  \" Document Title
+.Os Darwin                  \" Operating System
+.\"
+.Sh NAME
+.Nm mDNS
+.Nd Multicast DNS (mDNS) & DNS Service Discovery (DNS-SD) Test Tool \" For whatis
+.\" 
+.Sh SYNOPSIS
+.Nm Fl R Ar name type domain port Op Ar key=value ...
+.Pp
+.Nm Fl B Ar      type domain
+.Pp
+.Nm Fl L Ar name type domain
+.\"
+.Sh DESCRIPTION
+The
+.Nm
+command is a network diagnostic tool, much like
+.Xr ping 8
+or
+.Xr traceroute 8 .
+However, unlike those tools, most of its functionality is not implemented in the
+.Nm
+executable itself, but in library code that is available to any application.
+The library API that
+.Nm
+uses is documented in
+.Pa /usr/include/DNSServiceDiscovery/DNSServiceDiscovery.h .
+Note that this Mach-based API, first introduced in Mac OS X 10.2,
+is now deprecated in favour of the newer
+.Pa /usr/include/dns_sd.h
+API, which is built on Unix Domain Sockets and is supported on
+multiple platforms.
+The command-line tool to exercise the cross-platform
+.Pa dns_sd.h
+API is
+.Xr dns-sd 1 .
+.Pp
+The
+.Nm
+command is primarily intended for interactive use.
+Because its command-line arguments and output format are subject to change,
+invoking it from a shell script will generally be fragile. Additionally,
+the asynchronous nature of DNS Service Discovery does
+not lend itself easily to script-oriented programming. For example,
+calls like "browse" never complete; the action of performing a "browse"
+sets in motion machinery to notify the client whenever instances of
+that service type appear or disappear from the network. These
+notifications continue to be delivered indefinitely, for minutes,
+hours, or even days, as services come and go, until the client
+explicitly terminates the call. This style of asynchronous interaction
+works best with applications that are either multi-threaded, or use a
+main event-handling loop to receive keystrokes, network data, and other
+asynchronous event notifications as they happen.
+.br
+If you wish to perform DNS Service Discovery operations from a
+scripting language, then the best way to do this is not to execute the
+.Nm
+command and then attempt to decipher the textual output, but instead to
+directly call the DNS-SD APIs using a binding for your chosen language.
+.br
+For example, if you are programming in Ruby, then you can
+directly call DNS-SD APIs using the dnssd package documented at
+.Pa <http://rubyforge.org/projects/dnssd/> .
+.br
+Similar bindings for other languages are also in development.
+.Pp
+.Bl -tag -width R
+.It Nm Fl R Ar name type domain port Op Ar key=value ...
+register (advertise) a service in the specified
+.Ar domain 
+with the given
+.Ar name
+and
+.Ar type
+as listening (on the current machine) on
+.Ar port.
+.Pp
+.Ar name
+can be arbitrary unicode text, containing any legal unicode characters
+(including dots, spaces, slashes, colons, etc. without restriction),
+up to 63 UTF-8 bytes long.
+.Ar type
+must be of the form "_app-proto._tcp" or "_app-proto._udp", where
+"app-proto" is an application protocol name registered at
+.Pa http://www.dns-sd.org/ServiceTypes.html .
+.Pp
+.Ar domain
+is the domain in which to register the service.
+In current implementations, only the local multicast domain "local" is
+supported. In the future, registering will be supported in any arbitrary
+domain that has a working DNS Update server [RFC 2136]. The
+.Ar domain
+"." is a synonym for "pick a sensible default" which today
+means "local".
+.Pp
+.Ar port
+is a number from 0 to 65535, and is the TCP or UDP port number upon
+which the service is listening.
+.Pp 
+Additional attributes of the service may optionally be described by
+key/value pairs, which are stored in the advertised service's DNS TXT
+record. Allowable keys and values are listed with the service
+registration at
+.Pa http://www.dns-sd.org/ServiceTypes.html .
+.It Nm Fl B Ar type domain
+browse for instances of service
+.Ar type
+in
+.Ar domain .
+.Pp
+For valid 
+.Ar type Ns s
+see
+.Pa http://www.dns-sd.org/ServiceTypes.html
+as described above. Omitting the
+.Ar domain
+or using "." means "pick a sensible default."
+.It Nm Fl L Ar name type domain
+look up and display the information necessary to contact and use the
+named service: the hostname of the machine where that service is
+available, the port number on which the service is listening, and (if
+present) TXT record attributes describing properties of the service.
+.Pp
+Note that in a typical application, browsing happens rarely, while lookup
+(or "resolving") happens every time the service is used. For example, a
+user browses the network to pick a default printer fairly rarely, but once
+a default printer has been picked, that named service is resolved to its
+current IP address and port number every time the user presses Cmd-P to
+print.
+.El
+.Sh EXAMPLES
+.Pp
+To advertise the existence of LPR printing service on port 515 on this
+machine, such that it will be discovered by the Mac OS X printing software
+and other DNS-SD compatible printing clients, use:
+.Pp
+.Dl Nm Fl R Ns \ \&"My Test\&" _printer._tcp. \&. 515 pdl=application/postscript
+.Pp
+For this registration to be useful, you need to actually have LPR service
+available on port 515. Advertising a service that does not exist is not
+very useful, and will be confusing and annoying to other people on the
+network.
+.Pp
+Similarly, to advertise a web page being served by an HTTP
+server on port 80 on this machine, such that it will show up in the
+Bonjour list in Safari and other DNS-SD compatible Web clients, use:
+.Pp
+.Dl Nm Fl R Ns \ \&"My Test\&" _http._tcp \&. 80 path=/path-to-page.html
+.Pp
+To find the advertised web pages on the local network (the same list that
+Safari shows), use:
+.Pp
+.Dl Nm Fl B Ns \ _http._tcp
+.Pp
+While that command is running, in another window, try the
+.Nm Fl R
+example given above to advertise a web page, and you should see the
+"Add" event reported to the
+.Nm Fl B
+window. Now press Ctrl-C in the
+.Nm Fl R
+window and you should see the "Remove" event reported to the
+.Nm Fl B
+window.
+.Pp
+.Sh FILES
+.Pa /usr/bin/mDNS \" Pathname
+.\"
+.Sh SEE ALSO
+.Xr dns-sd 1
+.Xr mDNSResponder 8
+.\"
+.Sh BUGS
+.Nm
+bugs are tracked in Apple Radar component "mDNSResponder".
+.\"
+.Sh HISTORY
+The
+.Nm
+command first appeared in Mac OS X 10.3 (Panther).
diff --git a/mdnsresponder/mDNSShared/mDNSDebug.c b/mdnsresponder/mDNSShared/mDNSDebug.c
new file mode 100644
index 0000000..4932904
--- /dev/null
+++ b/mdnsresponder/mDNSShared/mDNSDebug.c
@@ -0,0 +1,93 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2003 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+
+ 	File:		mDNSDebug.c
+
+ 	Contains:	Implementation of debugging utilities. Requires a POSIX environment.
+
+ 	Version:	1.0
+
+ */
+
+#include "mDNSDebug.h"
+
+#include <stdio.h>
+
+#if defined(WIN32) || defined(EFI32) || defined(EFI64) || defined(EFIX64)
+// Need to add Windows/EFI syslog support here
+#define LOG_PID 0x01
+#define LOG_CONS 0x02
+#define LOG_PERROR 0x20
+#else
+#include <syslog.h>
+#endif
+
+#include "mDNSEmbeddedAPI.h"
+
+mDNSexport int mDNS_LoggingEnabled = 0;
+mDNSexport int mDNS_PacketLoggingEnabled = 0;
+
+#if MDNS_DEBUGMSGS && !defined(__ANDROID__)
+mDNSexport int mDNS_DebugMode = mDNStrue;
+#else
+mDNSexport int mDNS_DebugMode = mDNSfalse;
+#endif
+
+// Note, this uses mDNS_vsnprintf instead of standard "vsnprintf", because mDNS_vsnprintf knows
+// how to print special data types like IP addresses and length-prefixed domain names
+#if MDNS_DEBUGMSGS > 1
+mDNSexport void verbosedebugf_(const char *format, ...)
+	{
+	char buffer[512];
+	va_list ptr;
+	va_start(ptr,format);
+	buffer[mDNS_vsnprintf(buffer, sizeof(buffer), format, ptr)] = 0;
+	va_end(ptr);
+	mDNSPlatformWriteDebugMsg(buffer);
+	}
+#endif
+
+// Log message with default "mDNSResponder" ident string at the start
+mDNSlocal void LogMsgWithLevelv(mDNSLogLevel_t logLevel, const char *format, va_list ptr)
+	{
+	char buffer[512];
+	buffer[mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr)] = 0;
+	mDNSPlatformWriteLogMsg(ProgramName, buffer, logLevel);
+	}
+
+#define LOG_HELPER_BODY(L) \
+	{ \
+	va_list ptr; \
+	va_start(ptr,format); \
+	LogMsgWithLevelv(L, format, ptr); \
+	va_end(ptr); \
+	}
+
+// see mDNSDebug.h
+#if !MDNS_HAS_VA_ARG_MACROS
+void LogMsg_(const char *format, ...)       LOG_HELPER_BODY(MDNS_LOG_MSG)
+void LogOperation_(const char *format, ...) LOG_HELPER_BODY(MDNS_LOG_OPERATION)
+void LogSPS_(const char *format, ...)       LOG_HELPER_BODY(MDNS_LOG_SPS)
+void LogInfo_(const char *format, ...)      LOG_HELPER_BODY(MDNS_LOG_INFO)
+#endif
+
+#if MDNS_DEBUGMSGS
+void debugf_(const char *format, ...)       LOG_HELPER_BODY(MDNS_LOG_DEBUG)
+#endif
+
+// Log message with default "mDNSResponder" ident string at the start
+mDNSexport void LogMsgWithLevel(mDNSLogLevel_t logLevel, const char *format, ...)
+	LOG_HELPER_BODY(logLevel)
diff --git a/mdnsresponder/mDNSShared/mDNSResponder.8 b/mdnsresponder/mDNSShared/mDNSResponder.8
new file mode 100644
index 0000000..48fcbd5
--- /dev/null
+++ b/mdnsresponder/mDNSShared/mDNSResponder.8
@@ -0,0 +1,116 @@
+.\" -*- tab-width: 4 -*-
+.\" 
+.\" Copyright (c) 2003-2004 Apple Computer, Inc. All Rights Reserved.
+.\" 
+.\" Licensed 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.
+.\"
+.Dd April 2004              \" Date
+.Dt mDNSResponder 8         \" Document Title
+.Os Darwin                  \" Operating System
+.\"
+.Sh NAME
+.Nm mDNSResponder
+.Nd Multicast and Unicast DNS daemon    \" Name Description for whatis database
+.\" 
+.Sh SYNOPSIS
+.Nm
+.\"
+.Sh DESCRIPTION
+.Nm
+(also known as
+.Nm mdnsd
+on some systems)
+is a daemon invoked at boot time to implement Multicast DNS and DNS Service Discovery. On
+Mac OS X 10.6 (Snow Leopard), 
+.Nm 
+is also the system-wide Unicast DNS Resolver.
+.Pp
+.Nm
+listens on UDP port 5353 for Multicast DNS Query packets.
+When it receives a query for which it knows an answer,
+.Nm
+issues the appropriate Multicast DNS Reply packet.
+.Pp
+.Nm
+also performs Unicast and Multicast DNS Queries on behalf of client processes, and 
+maintains a cache of the replies.
+.Pp
+.Nm
+has no user-specifiable command-line argument, and users should not run
+.Nm
+manually.
+.Pp
+.Ss LOGGING
+There are several methods with which to examine 
+.Nm Ns 's internal state for debugging and diagnostic purposes. The syslog(1)
+logging levels map as follows:
+.Pp
+.Dl Error - Error messages
+.Dl Warning - Client-initiated operations
+.Dl Notice - Sleep proxy operations
+.Dl Info - Informational messages
+.Pp
+By default, only log level Error is logged.
+.Pp
+A SIGUSR1 signal toggles additional logging, with Warning and Notice
+enabled by default:
+.Pp
+.Dl % sudo killall -USR1 mDNSResponder
+.Pp
+Once this logging is enabled, users can additionally use syslog(1)
+to change the log filter for the process. For example, to enable log levels Emergency - Debug:
+.Pp
+.Dl % sudo syslog -c mDNSResponder -d
+.Pp
+A SIGUSR2 signal toggles packet logging:
+.Pp
+.Dl % sudo killall -USR2 mDNSResponder
+.Pp
+A SIGINFO signal will dump a snapshot summary of the internal state to 
+.Pa /var/log/system.log Ns :
+.Pp
+.Dl % sudo killall -INFO mDNSResponder
+.Sh FILES
+.Pa /usr/sbin/mDNSResponder \" Pathname
+.\"
+.Sh SEE ALSO
+.Xr mDNS 1
+.Pp
+For information on Multicast DNS, see
+.Pa http://www.multicastdns.org/
+.Pp
+For information on DNS Service Discovery, see
+.Pa http://www.dns-sd.org/
+.Pp
+For information on how to use the Multicast DNS and the
+DNS Service Discovery APIs on Mac OS X and other platforms, see
+.Pa http://developer.apple.com/bonjour/
+.Pp
+For the source code to
+.Nm , see
+.Pa http://developer.apple.com/darwin/projects/bonjour/
+.\"
+.Sh BUGS
+.Nm
+bugs are tracked in Apple Radar component "mDNSResponder".
+.\"
+.Sh HISTORY
+The
+.Nm
+daemon first appeared in Mac OS X 10.2 (Jaguar).
+.Pp
+Also available from the Darwin open source repository
+(though not officially supported by Apple) are 
+.Nm
+daemons for other platforms, including Mac OS 9, Microsoft Windows,
+Linux, FreeBSD, NetBSD, Solaris, and other POSIX systems.
diff --git a/mdnsresponder/mDNSShared/uds_daemon.c b/mdnsresponder/mDNSShared/uds_daemon.c
new file mode 100644
index 0000000..fd214ac
--- /dev/null
+++ b/mdnsresponder/mDNSShared/uds_daemon.c
@@ -0,0 +1,4756 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2003-2006 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+#if defined(_WIN32)
+#include <process.h>
+#define usleep(X) Sleep(((X)+999)/1000)
+#else
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+
+#include "mDNSEmbeddedAPI.h"
+#include "DNSCommon.h"
+#include "uDNS.h"
+#include "uds_daemon.h"
+
+#ifdef __ANDROID__
+#include "cutils/sockets.h"
+#endif
+
+// Normally we append search domains only for queries with a single label that are not
+// fully qualified. This can be overridden to apply search domains for queries (that are
+// not fully qualified) with any number of labels e.g., moon, moon.cs, moon.cs.be, etc.
+mDNSBool AlwaysAppendSearchDomains = mDNSfalse;
+
+// Apple-specific functionality, not required for other platforms
+#if APPLE_OSX_mDNSResponder
+#include <sys/ucred.h>
+#ifndef PID_FILE
+#define PID_FILE ""
+#endif
+#endif
+
+#if APPLE_OSX_mDNSResponder
+#include <WebFilterDNS/WebFilterDNS.h>
+
+#if ! NO_WCF
+
+int WCFIsServerRunning(WCFConnection *conn) __attribute__((weak_import));
+int WCFNameResolvesToAddr(WCFConnection *conn, char* domainName, struct sockaddr* address, uid_t userid) __attribute__((weak_import));
+int WCFNameResolvesToName(WCFConnection *conn, char* fromName, char* toName, uid_t userid) __attribute__((weak_import));
+
+// Do we really need to define a macro for "if"?
+#define CHECK_WCF_FUNCTION(X) if (X)
+#endif // ! NO_WCF
+
+#else
+#define NO_WCF 1
+#endif // APPLE_OSX_mDNSResponder
+
+// User IDs 0-500 are system-wide processes, not actual users in the usual sense
+// User IDs for real user accounts start at 501 and count up from there
+#define SystemUID(X) ((X) <= 500)
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - Types and Data Structures
+#endif
+
+typedef enum
+	{
+	t_uninitialized,
+	t_morecoming,
+	t_complete,
+	t_error,
+	t_terminated
+	} transfer_state;
+
+typedef struct request_state request_state;
+
+typedef void (*req_termination_fn)(request_state *request);
+
+typedef struct registered_record_entry
+	{
+	struct registered_record_entry *next;
+	mDNSu32 key;
+	client_context_t regrec_client_context;
+	request_state *request;
+	mDNSBool external_advertise;
+	mDNSInterfaceID origInterfaceID;
+	AuthRecord *rr;				// Pointer to variable-sized AuthRecord (Why a pointer? Why not just embed it here?)
+	} registered_record_entry;
+
+// A single registered service: ServiceRecordSet + bookkeeping
+// Note that we duplicate some fields from parent service_info object
+// to facilitate cleanup, when instances and parent may be deallocated at different times.
+typedef struct service_instance
+	{
+	struct service_instance *next;
+	request_state *request;
+	AuthRecord *subtypes;
+	mDNSBool renameonmemfree;  		// Set on config change when we deregister original name
+    mDNSBool clientnotified;		// Has client been notified of successful registration yet?
+	mDNSBool default_local;			// is this the "local." from an empty-string registration?
+	mDNSBool external_advertise;	// is this is being advertised externally?
+	domainname domain;
+	ServiceRecordSet srs;			// note -- variable-sized object -- must be last field in struct
+	} service_instance;
+
+// for multi-domain default browsing
+typedef struct browser_t
+	{
+	struct browser_t *next;
+	domainname domain;
+	DNSQuestion q;
+	} browser_t;
+
+struct request_state
+	{
+	request_state *next;
+	request_state *primary;			// If this operation is on a shared socket, pointer to primary
+									// request_state for the original DNSServiceCreateConnection() operation
+	dnssd_sock_t sd;
+	dnssd_sock_t errsd;
+	mDNSu32 uid;
+	void * platform_data;
+
+	// Note: On a shared connection these fields in the primary structure, including hdr, are re-used
+	// for each new request. This is because, until we've read the ipc_msg_hdr to find out what the
+	// operation is, we don't know if we're going to need to allocate a new request_state or not.
+	transfer_state ts;
+	mDNSu32        hdr_bytes;		// bytes of header already read
+	ipc_msg_hdr    hdr;
+	mDNSu32        data_bytes;		// bytes of message data already read
+	char          *msgbuf;			// pointer to data storage to pass to free()
+	const char    *msgptr;			// pointer to data to be read from (may be modified)
+	char          *msgend;			// pointer to byte after last byte of message
+
+	// reply, termination, error, and client context info
+	int no_reply;					// don't send asynchronous replies to client
+	mDNSs32 time_blocked;			// record time of a blocked client
+	int unresponsiveness_reports;
+	struct reply_state *replies;	// corresponding (active) reply list
+	req_termination_fn terminate;
+	DNSServiceFlags		flags;	
+
+	union
+		{
+		registered_record_entry *reg_recs;  // list of registrations for a connection-oriented request
+		struct
+			{
+			mDNSInterfaceID interface_id;
+			mDNSBool default_domain;
+			mDNSBool ForceMCast;
+			domainname regtype;
+			browser_t *browsers;
+			} browser;
+		struct
+			{
+			mDNSInterfaceID InterfaceID;
+			mDNSu16 txtlen;
+			void *txtdata;
+			mDNSIPPort port;
+			domainlabel name;
+			char type_as_string[MAX_ESCAPED_DOMAIN_NAME];
+			domainname type;
+			mDNSBool default_domain;
+			domainname host;
+			mDNSBool autoname;				// Set if this name is tied to the Computer Name
+			mDNSBool autorename;			// Set if this client wants us to automatically rename on conflict
+			mDNSBool allowremotequery;		// Respond to unicast queries from outside the local link?
+			int num_subtypes;
+			service_instance *instances;
+			} servicereg;
+		struct
+			{
+			mDNSInterfaceID      interface_id;
+			mDNSu32              flags;
+			mDNSu32              protocol;
+			DNSQuestion          q4;
+			DNSQuestion          *q42;
+			DNSQuestion          q6;
+			DNSQuestion          *q62;
+			} addrinfo;
+		struct
+			{
+			mDNSIPPort           ReqExt;	// External port we originally requested, for logging purposes
+			NATTraversalInfo     NATinfo;
+			} pm;
+		struct
+			{
+#if 0
+			DNSServiceFlags flags;
+#endif
+			DNSQuestion q_all;
+			DNSQuestion q_default;
+			} enumeration;
+		struct
+			{
+			DNSQuestion q;
+			DNSQuestion *q2;
+			} queryrecord;
+		struct
+			{
+			DNSQuestion qtxt;
+			DNSQuestion qsrv;
+			const ResourceRecord *txt;
+			const ResourceRecord *srv;
+			mDNSs32 ReportTime;
+			mDNSBool external_advertise;
+			} resolve;
+		} u;
+	};
+
+// struct physically sits between ipc message header and call-specific fields in the message buffer
+typedef struct
+	{
+	DNSServiceFlags flags;			// Note: This field is in NETWORK byte order
+	mDNSu32 ifi;					// Note: This field is in NETWORK byte order
+	DNSServiceErrorType error;		// Note: This field is in NETWORK byte order
+	} reply_hdr;
+
+typedef struct reply_state
+	{
+	struct reply_state *next;		// If there are multiple unsent replies
+	mDNSu32 totallen;
+	mDNSu32 nwriten;
+	ipc_msg_hdr mhdr[1];
+	reply_hdr rhdr[1];
+	} reply_state;
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - Globals
+#endif
+
+// globals
+mDNSexport mDNS mDNSStorage;
+mDNSexport const char ProgramName[] = "mDNSResponder";
+
+static dnssd_sock_t listenfd = dnssd_InvalidSocket;
+static request_state *all_requests = NULL;
+
+// Note asymmetry here between registration and browsing.
+// For service registrations we only automatically register in domains that explicitly appear in local configuration data
+// (so AutoRegistrationDomains could equally well be called SCPrefRegDomains)
+// For service browsing we also learn automatic browsing domains from the network, so for that case we have:
+// 1. SCPrefBrowseDomains (local configuration data)
+// 2. LocalDomainEnumRecords (locally-generated local-only PTR records -- equivalent to slElem->AuthRecs in uDNS.c)
+// 3. AutoBrowseDomains, which is populated by tracking add/rmv events in AutomaticBrowseDomainChange, the callback function for our mDNS_GetDomains call.
+// By creating and removing our own LocalDomainEnumRecords, we trigger AutomaticBrowseDomainChange callbacks just like domains learned from the network would.
+
+mDNSexport DNameListElem *AutoRegistrationDomains;	// Domains where we automatically register for empty-string registrations
+
+static DNameListElem *SCPrefBrowseDomains;			// List of automatic browsing domains read from SCPreferences for "empty string" browsing
+static ARListElem    *LocalDomainEnumRecords;		// List of locally-generated PTR records to augment those we learn from the network
+mDNSexport DNameListElem *AutoBrowseDomains;		// List created from those local-only PTR records plus records we get from the network
+
+#define MSG_PAD_BYTES 5		// pad message buffer (read from client) with n zero'd bytes to guarantee
+							// n get_string() calls w/o buffer overrun
+// initialization, setup/teardown functions
+
+// If a platform specifies its own PID file name, we use that
+#ifndef PID_FILE
+#define PID_FILE "/var/run/mDNSResponder.pid"
+#endif
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - General Utility Functions
+#endif
+
+mDNSlocal void FatalError(char *errmsg)
+	{
+	LogMsg("%s: %s", errmsg, dnssd_strerror(dnssd_errno));
+	*(volatile long*)0 = 0;	// On OS X abort() doesn't generate a crash log, but writing to zero does
+	abort();		// On platforms where writing to zero doesn't generate an exception, abort instead
+	}
+
+mDNSlocal mDNSu32 dnssd_htonl(mDNSu32 l)
+	{
+	mDNSu32 ret;
+	char *data = (char*) &ret;
+	put_uint32(l, &data);
+	return ret;
+	}
+
+// hack to search-replace perror's to LogMsg's
+mDNSlocal void my_perror(char *errmsg)
+	{
+	LogMsg("%s: %d (%s)", errmsg, dnssd_errno, dnssd_strerror(dnssd_errno));
+	}
+
+mDNSlocal void abort_request(request_state *req)
+	{
+	if (req->terminate == (req_termination_fn)~0)
+		{ LogMsg("abort_request: ERROR: Attempt to abort operation %p with req->terminate %p", req, req->terminate); return; }
+	
+	// First stop whatever mDNSCore operation we were doing
+	// If this is actually a shared connection operation, then its req->terminate function will scan
+	// the all_requests list and terminate any subbordinate operations sharing this file descriptor
+	if (req->terminate) req->terminate(req);
+
+	if (!dnssd_SocketValid(req->sd))
+		{ LogMsg("abort_request: ERROR: Attempt to abort operation %p with invalid fd %d",     req, req->sd);        return; }
+	
+	// Now, if this request_state is not subordinate to some other primary, close file descriptor and discard replies
+	if (!req->primary)
+		{
+		if (req->errsd != req->sd) LogOperation("%3d: Removing FD and closing errsd %d", req->sd, req->errsd);
+		else                       LogOperation("%3d: Removing FD", req->sd);
+		udsSupportRemoveFDFromEventLoop(req->sd, req->platform_data);		// Note: This also closes file descriptor req->sd for us
+		if (req->errsd != req->sd) { dnssd_close(req->errsd); req->errsd = req->sd; }
+
+		while (req->replies)	// free pending replies
+			{
+			reply_state *ptr = req->replies;
+			req->replies = req->replies->next;
+			freeL("reply_state (abort)", ptr);
+			}
+		}
+
+	// Set req->sd to something invalid, so that udsserver_idle knows to unlink and free this structure
+#if APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING
+	// Don't use dnssd_InvalidSocket (-1) because that's the sentinel value MACOSX_MDNS_MALLOC_DEBUGGING uses
+	// for detecting when the memory for an object is inadvertently freed while the object is still on some list
+	req->sd = req->errsd = -2;
+#else
+	req->sd = req->errsd = dnssd_InvalidSocket;
+#endif
+	// We also set req->terminate to a bogus value so we know if abort_request() gets called again for this request
+	req->terminate = (req_termination_fn)~0;
+	}
+
+mDNSlocal void AbortUnlinkAndFree(request_state *req)
+	{
+	request_state **p = &all_requests;
+	abort_request(req);
+	while (*p && *p != req) p=&(*p)->next;
+	if (*p) { *p = req->next; freeL("request_state/AbortUnlinkAndFree", req); }
+	else LogMsg("AbortUnlinkAndFree: ERROR: Attempt to abort operation %p not in list", req);
+	}
+
+mDNSlocal reply_state *create_reply(const reply_op_t op, const size_t datalen, request_state *const request)
+	{
+	reply_state *reply;
+
+	if ((unsigned)datalen < sizeof(reply_hdr))
+		{
+		LogMsg("ERROR: create_reply - data length less than length of required fields");
+		return NULL;
+		}
+
+	reply = mallocL("reply_state", sizeof(reply_state) + datalen - sizeof(reply_hdr));
+	if (!reply) FatalError("ERROR: malloc");
+	
+	reply->next     = mDNSNULL;
+	reply->totallen = (mDNSu32)datalen + sizeof(ipc_msg_hdr);
+	reply->nwriten  = 0;
+
+	reply->mhdr->version        = VERSION;
+	reply->mhdr->datalen        = (mDNSu32)datalen;
+	reply->mhdr->ipc_flags      = 0;
+	reply->mhdr->op             = op;
+	reply->mhdr->client_context = request->hdr.client_context;
+	reply->mhdr->reg_index      = 0;
+
+	return reply;
+	}
+
+// Append a reply to the list in a request object
+// If our request is sharing a connection, then we append our reply_state onto the primary's list
+mDNSlocal void append_reply(request_state *req, reply_state *rep)
+	{
+	request_state *r = req->primary ? req->primary : req;
+	reply_state **ptr = &r->replies;
+	while (*ptr) ptr = &(*ptr)->next;
+	*ptr = rep;
+	rep->next = NULL;
+	}
+
+// Generates a response message giving name, type, domain, plus interface index,
+// suitable for a browse result or service registration result.
+// On successful completion rep is set to point to a malloc'd reply_state struct
+mDNSlocal mStatus GenerateNTDResponse(const domainname *const servicename, const mDNSInterfaceID id,
+	request_state *const request, reply_state **const rep, reply_op_t op, DNSServiceFlags flags, mStatus err)
+	{
+	domainlabel name;
+	domainname type, dom;
+	*rep = NULL;
+	if (!DeconstructServiceName(servicename, &name, &type, &dom))
+		return kDNSServiceErr_Invalid;
+	else
+		{
+		char namestr[MAX_DOMAIN_LABEL+1];
+		char typestr[MAX_ESCAPED_DOMAIN_NAME];
+		char domstr [MAX_ESCAPED_DOMAIN_NAME];
+		int len;
+		char *data;
+
+		ConvertDomainLabelToCString_unescaped(&name, namestr);
+		ConvertDomainNameToCString(&type, typestr);
+		ConvertDomainNameToCString(&dom, domstr);
+
+		// Calculate reply data length
+		len = sizeof(DNSServiceFlags);
+		len += sizeof(mDNSu32);  // if index
+		len += sizeof(DNSServiceErrorType);
+		len += (int) (strlen(namestr) + 1);
+		len += (int) (strlen(typestr) + 1);
+		len += (int) (strlen(domstr) + 1);
+
+		// Build reply header
+		*rep = create_reply(op, len, request);
+		(*rep)->rhdr->flags = dnssd_htonl(flags);
+		(*rep)->rhdr->ifi   = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage, id, mDNSfalse));
+		(*rep)->rhdr->error = dnssd_htonl(err);
+
+		// Build reply body
+		data = (char *)&(*rep)->rhdr[1];
+		put_string(namestr, &data);
+		put_string(typestr, &data);
+		put_string(domstr, &data);
+
+		return mStatus_NoError;
+		}
+	}
+
+// Special support to enable the DNSServiceBrowse call made by Bonjour Browser
+// Remove after Bonjour Browser is updated to use DNSServiceQueryRecord instead of DNSServiceBrowse
+mDNSlocal void GenerateBonjourBrowserResponse(const domainname *const servicename, const mDNSInterfaceID id,
+	request_state *const request, reply_state **const rep, reply_op_t op, DNSServiceFlags flags, mStatus err)
+	{
+	char namestr[MAX_DOMAIN_LABEL+1];
+	char typestr[MAX_ESCAPED_DOMAIN_NAME];
+	static const char domstr[] = ".";
+	int len;
+	char *data;
+
+	*rep = NULL;
+
+	// 1. Put first label in namestr
+	ConvertDomainLabelToCString_unescaped((const domainlabel *)servicename, namestr);
+
+	// 2. Put second label and "local" into typestr
+	mDNS_snprintf(typestr, sizeof(typestr), "%#s.local.", SecondLabel(servicename));
+
+	// Calculate reply data length
+	len = sizeof(DNSServiceFlags);
+	len += sizeof(mDNSu32);  // if index
+	len += sizeof(DNSServiceErrorType);
+	len += (int) (strlen(namestr) + 1);
+	len += (int) (strlen(typestr) + 1);
+	len += (int) (strlen(domstr) + 1);
+
+	// Build reply header
+	*rep = create_reply(op, len, request);
+	(*rep)->rhdr->flags = dnssd_htonl(flags);
+	(*rep)->rhdr->ifi   = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage, id, mDNSfalse));
+	(*rep)->rhdr->error = dnssd_htonl(err);
+
+	// Build reply body
+	data = (char *)&(*rep)->rhdr[1];
+	put_string(namestr, &data);
+	put_string(typestr, &data);
+	put_string(domstr, &data);
+	}
+
+// Returns a resource record (allocated w/ malloc) containing the data found in an IPC message
+// Data must be in the following format: flags, interfaceIndex, name, rrtype, rrclass, rdlen, rdata, (optional) ttl
+// (ttl only extracted/set if ttl argument is non-zero). Returns NULL for a bad-parameter error
+mDNSlocal AuthRecord *read_rr_from_ipc_msg(request_state *request, int GetTTL, int validate_flags)
+	{
+	DNSServiceFlags flags  = get_flags(&request->msgptr, request->msgend);
+	mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend);
+	char name[256];
+	int         str_err = get_string(&request->msgptr, request->msgend, name, sizeof(name));
+	mDNSu16     type    = get_uint16(&request->msgptr, request->msgend);
+	mDNSu16     class   = get_uint16(&request->msgptr, request->msgend);
+	mDNSu16     rdlen   = get_uint16(&request->msgptr, request->msgend);
+	const char *rdata   = get_rdata (&request->msgptr, request->msgend, rdlen);
+	mDNSu32 ttl   = GetTTL ? get_uint32(&request->msgptr, request->msgend) : 0;
+	int storage_size = rdlen > sizeof(RDataBody) ? rdlen : sizeof(RDataBody);
+	AuthRecord *rr;
+	mDNSInterfaceID InterfaceID;
+	AuthRecType artype;
+
+	request->flags = flags;
+
+	if (str_err) { LogMsg("ERROR: read_rr_from_ipc_msg - get_string"); return NULL; }
+
+	if (!request->msgptr) { LogMsg("Error reading Resource Record from client"); return NULL; }
+
+	if (validate_flags &&
+		!((flags & kDNSServiceFlagsShared) == kDNSServiceFlagsShared) &&
+		!((flags & kDNSServiceFlagsUnique) == kDNSServiceFlagsUnique))
+		{
+		LogMsg("ERROR: Bad resource record flags (must be kDNSServiceFlagsShared or kDNSServiceFlagsUnique)");
+		return NULL;
+		}
+
+	rr = mallocL("AuthRecord/read_rr_from_ipc_msg", sizeof(AuthRecord) - sizeof(RDataBody) + storage_size);
+	if (!rr) FatalError("ERROR: malloc");
+
+	InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex);
+	if (InterfaceID == mDNSInterface_LocalOnly)
+		artype = AuthRecordLocalOnly;
+	else if (InterfaceID == mDNSInterface_P2P)
+		artype = AuthRecordP2P;
+	else if ((InterfaceID == mDNSInterface_Any) && (flags & kDNSServiceFlagsIncludeP2P))
+		artype = AuthRecordAnyIncludeP2P;
+	else
+		artype = AuthRecordAny;
+
+	mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceID, type, 0,
+		(mDNSu8) ((flags & kDNSServiceFlagsShared) ? kDNSRecordTypeShared : kDNSRecordTypeUnique), artype, mDNSNULL, mDNSNULL);
+
+	if (!MakeDomainNameFromDNSNameString(&rr->namestorage, name))
+		{
+		LogMsg("ERROR: bad name: %s", name);
+		freeL("AuthRecord/read_rr_from_ipc_msg", rr);
+		return NULL;
+		}
+
+	if (flags & kDNSServiceFlagsAllowRemoteQuery) rr->AllowRemoteQuery = mDNStrue;
+	rr->resrec.rrclass = class;
+	rr->resrec.rdlength = rdlen;
+	rr->resrec.rdata->MaxRDLength = rdlen;
+	mDNSPlatformMemCopy(rr->resrec.rdata->u.data, rdata, rdlen);
+	if (GetTTL) rr->resrec.rroriginalttl = ttl;
+	rr->resrec.namehash = DomainNameHashValue(rr->resrec.name);
+	SetNewRData(&rr->resrec, mDNSNULL, 0);	// Sets rr->rdatahash for us
+	return rr;
+	}
+
+mDNSlocal int build_domainname_from_strings(domainname *srv, char *name, char *regtype, char *domain)
+	{
+	domainlabel n;
+	domainname d, t;
+
+	if (!MakeDomainLabelFromLiteralString(&n, name)) return -1;
+	if (!MakeDomainNameFromDNSNameString(&t, regtype)) return -1;
+	if (!MakeDomainNameFromDNSNameString(&d, domain)) return -1;
+	if (!ConstructServiceName(srv, &n, &t, &d)) return -1;
+	return 0;
+	}
+
+mDNSlocal void send_all(dnssd_sock_t s, const char *ptr, int len)
+	{
+	int n = send(s, ptr, len, 0);
+	// On a freshly-created Unix Domain Socket, the kernel should *never* fail to buffer a small write for us
+	// (four bytes for a typical error code return, 12 bytes for DNSServiceGetProperty(DaemonVersion)).
+	// If it does fail, we don't attempt to handle this failure, but we do log it so we know something is wrong.
+	if (n < len)
+		LogMsg("ERROR: send_all(%d) wrote %d of %d errno %d (%s)",
+			s, n, len, dnssd_errno, dnssd_strerror(dnssd_errno));
+	}
+
+#if 0
+mDNSlocal mDNSBool AuthorizedDomain(const request_state * const request, const domainname * const d, const DNameListElem * const doms)
+{
+	const 		DNameListElem 	*delem = mDNSNULL;
+	int 		bestDelta 	= -1; 					// the delta of the best match, lower is better
+	int 		dLabels 	= 0;
+	mDNSBool	allow 		= mDNSfalse;
+	
+	if (SystemUID(request->uid)) return mDNStrue;
+	
+	dLabels = CountLabels(d);
+	for (delem = doms; delem; delem = delem->next)
+		{
+		if (delem->uid)
+			{
+			int	delemLabels = CountLabels(&delem->name);
+			int delta 		= dLabels - delemLabels;
+			if ((bestDelta == -1 || delta <= bestDelta) && SameDomainName(&delem->name, SkipLeadingLabels(d, delta)))
+				{
+				bestDelta = delta;
+				allow = (allow || (delem->uid == request->uid));
+				}
+			}
+		}
+	
+	return bestDelta == -1 ? mDNStrue : allow;
+}
+#endif
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - external helpers
+#endif
+
+mDNSlocal void external_start_advertising_helper(service_instance *const instance)
+	{
+	AuthRecord *st = instance->subtypes;
+	ExtraResourceRecord *e;
+	int i;
+	
+	if (mDNSIPPortIsZero(instance->request->u.servicereg.port))
+		{
+		LogInfo("external_start_advertising_helper: Not registering service with port number zero");
+		return;
+		}
+
+#if APPLE_OSX_mDNSResponder
+	// Update packet filter if p2p interface already exists, otherwise,
+	// if will be updated when we get the KEV_DL_IF_ATTACHED event for
+	// the interface.  Called here since we don't call external_start_advertising_service()
+	// with the SRV record when advertising a service.
+	mDNSInitPacketFilter();
+#endif // APPLE_OSX_mDNSResponder
+
+	if (instance->external_advertise) LogMsg("external_start_advertising_helper: external_advertise already set!");
+	
+	for ( i = 0; i < instance->request->u.servicereg.num_subtypes; i++)
+		external_start_advertising_service(&st[i].resrec);
+	
+	external_start_advertising_service(&instance->srs.RR_PTR.resrec);
+	external_start_advertising_service(&instance->srs.RR_TXT.resrec);
+	
+	for (e = instance->srs.Extras; e; e = e->next)
+		external_start_advertising_service(&e->r.resrec);
+	
+	instance->external_advertise = mDNStrue;
+	}
+
+mDNSlocal void external_stop_advertising_helper(service_instance *const instance)
+	{
+	AuthRecord *st = instance->subtypes;
+	ExtraResourceRecord *e;
+	int i;
+	
+	if (!instance->external_advertise) return;
+
+	LogInfo("external_stop_advertising_helper: calling external_stop_advertising_service");
+	
+	for ( i = 0; i < instance->request->u.servicereg.num_subtypes; i++)
+		external_stop_advertising_service(&st[i].resrec);
+	
+	external_stop_advertising_service(&instance->srs.RR_PTR.resrec);
+	external_stop_advertising_service(&instance->srs.RR_TXT.resrec);
+	
+	for (e = instance->srs.Extras; e; e = e->next)
+		external_stop_advertising_service(&e->r.resrec);
+	
+	instance->external_advertise = mDNSfalse;
+	}
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - DNSServiceRegister
+#endif
+
+mDNSexport void FreeExtraRR(mDNS *const m, AuthRecord *const rr, mStatus result)
+	{
+	ExtraResourceRecord *extra = (ExtraResourceRecord *)rr->RecordContext;
+	(void)m;  // Unused
+
+	if (result != mStatus_MemFree) { LogMsg("Error: FreeExtraRR invoked with unexpected error %d", result); return; }
+
+	LogInfo("     FreeExtraRR %s", RRDisplayString(m, &rr->resrec));
+
+	if (rr->resrec.rdata != &rr->rdatastorage)
+		freeL("Extra RData", rr->resrec.rdata);
+	freeL("ExtraResourceRecord/FreeExtraRR", extra);
+	}
+
+mDNSlocal void unlink_and_free_service_instance(service_instance *srv)
+	{
+	ExtraResourceRecord *e = srv->srs.Extras, *tmp;
+
+	external_stop_advertising_helper(srv);
+
+	// clear pointers from parent struct
+	if (srv->request)
+		{
+		service_instance **p = &srv->request->u.servicereg.instances;
+		while (*p)
+			{
+			if (*p == srv) { *p = (*p)->next; break; }
+			p = &(*p)->next;
+			}
+		}
+
+	while (e)
+		{
+		e->r.RecordContext = e;
+		tmp = e;
+		e = e->next;
+		FreeExtraRR(&mDNSStorage, &tmp->r, mStatus_MemFree);
+		}
+
+	if (srv->srs.RR_TXT.resrec.rdata != &srv->srs.RR_TXT.rdatastorage)
+		freeL("TXT RData", srv->srs.RR_TXT.resrec.rdata);
+
+	if (srv->subtypes) { freeL("ServiceSubTypes", srv->subtypes); srv->subtypes = NULL; }
+	freeL("service_instance", srv);
+	}
+
+// Count how many other service records we have locally with the same name, but different rdata.
+// For auto-named services, we can have at most one per machine -- if we allowed two auto-named services of
+// the same type on the same machine, we'd get into an infinite autoimmune-response loop of continuous renaming.
+mDNSexport int CountPeerRegistrations(mDNS *const m, ServiceRecordSet *const srs)
+	{
+	int count = 0;
+	ResourceRecord *r = &srs->RR_SRV.resrec;
+	AuthRecord *rr;
+
+	for (rr = m->ResourceRecords; rr; rr=rr->next)
+		if (rr->resrec.rrtype == kDNSType_SRV && SameDomainName(rr->resrec.name, r->name) && !IdenticalSameNameRecord(&rr->resrec, r))
+			count++;
+
+	verbosedebugf("%d peer registrations for %##s", count, r->name->c);
+	return(count);
+	}
+
+mDNSexport int CountExistingRegistrations(domainname *srv, mDNSIPPort port)
+	{
+	int count = 0;
+	AuthRecord *rr;
+	for (rr = mDNSStorage.ResourceRecords; rr; rr=rr->next)
+		if (rr->resrec.rrtype == kDNSType_SRV &&
+			mDNSSameIPPort(rr->resrec.rdata->u.srv.port, port) &&
+			SameDomainName(rr->resrec.name, srv))
+			count++;
+	return(count);
+	}
+
+mDNSlocal void SendServiceRemovalNotification(ServiceRecordSet *const srs)
+	{
+	reply_state *rep;
+	service_instance *instance = srs->ServiceContext;
+	if (GenerateNTDResponse(srs->RR_SRV.resrec.name, srs->RR_SRV.resrec.InterfaceID, instance->request, &rep, reg_service_reply_op, 0, mStatus_NoError) != mStatus_NoError)
+		LogMsg("%3d: SendServiceRemovalNotification: %##s is not valid DNS-SD SRV name", instance->request->sd, srs->RR_SRV.resrec.name->c);
+	else { append_reply(instance->request, rep); instance->clientnotified = mDNSfalse; }
+	}
+
+// service registration callback performs three duties - frees memory for deregistered services,
+// handles name conflicts, and delivers completed registration information to the client
+mDNSlocal void regservice_callback(mDNS *const m, ServiceRecordSet *const srs, mStatus result)
+	{
+	mStatus err;
+	mDNSBool SuppressError = mDNSfalse;
+	service_instance *instance;
+	reply_state         *rep;
+	(void)m; // Unused
+
+	if (!srs)      { LogMsg("regservice_callback: srs is NULL %d",                 result); return; }
+
+	instance = srs->ServiceContext;
+	if (!instance) { LogMsg("regservice_callback: srs->ServiceContext is NULL %d", result); return; }
+
+	// don't send errors up to client for wide-area, empty-string registrations
+	if (instance->request &&
+		instance->request->u.servicereg.default_domain &&
+		!instance->default_local)
+		SuppressError = mDNStrue;
+
+	if (mDNS_LoggingEnabled)
+		{
+		const char *const fmt =
+			(result == mStatus_NoError)      ? "%s DNSServiceRegister(%##s, %u) REGISTERED"    :
+			(result == mStatus_MemFree)      ? "%s DNSServiceRegister(%##s, %u) DEREGISTERED"  :
+			(result == mStatus_NameConflict) ? "%s DNSServiceRegister(%##s, %u) NAME CONFLICT" :
+			                                   "%s DNSServiceRegister(%##s, %u) %s %d";
+		char prefix[16] = "---:";
+		if (instance->request) mDNS_snprintf(prefix, sizeof(prefix), "%3d:", instance->request->sd);
+		LogOperation(fmt, prefix, srs->RR_SRV.resrec.name->c, mDNSVal16(srs->RR_SRV.resrec.rdata->u.srv.port),
+			SuppressError ? "suppressed error" : "CALLBACK", result);
+		}
+
+	if (!instance->request && result != mStatus_MemFree) { LogMsg("regservice_callback: instance->request is NULL %d", result); return; }
+
+	if (result == mStatus_NoError)
+		{
+		if (instance->request->u.servicereg.allowremotequery)
+			{
+			ExtraResourceRecord *e;
+			srs->RR_ADV.AllowRemoteQuery = mDNStrue;
+			srs->RR_PTR.AllowRemoteQuery = mDNStrue;
+			srs->RR_SRV.AllowRemoteQuery = mDNStrue;
+			srs->RR_TXT.AllowRemoteQuery = mDNStrue;
+			for (e = instance->srs.Extras; e; e = e->next) e->r.AllowRemoteQuery = mDNStrue;
+			}
+
+		if (GenerateNTDResponse(srs->RR_SRV.resrec.name, srs->RR_SRV.resrec.InterfaceID, instance->request, &rep, reg_service_reply_op, kDNSServiceFlagsAdd, result) != mStatus_NoError)
+			LogMsg("%3d: regservice_callback: %##s is not valid DNS-SD SRV name", instance->request->sd, srs->RR_SRV.resrec.name->c);
+		else { append_reply(instance->request, rep); instance->clientnotified = mDNStrue; }
+
+		if (instance->request->u.servicereg.InterfaceID == mDNSInterface_P2P || (!instance->request->u.servicereg.InterfaceID && SameDomainName(&instance->domain, &localdomain) && (instance->request->flags & kDNSServiceFlagsIncludeP2P)))
+			{
+			LogInfo("regservice_callback: calling external_start_advertising_helper()");
+			external_start_advertising_helper(instance);
+			}
+		if (instance->request->u.servicereg.autoname && CountPeerRegistrations(m, srs) == 0)
+			RecordUpdatedNiceLabel(m, 0);	// Successfully got new name, tell user immediately
+		}
+	else if (result == mStatus_MemFree)
+		{
+		if (instance->request && instance->renameonmemfree)
+			{
+			external_stop_advertising_helper(instance);
+			instance->renameonmemfree = 0;
+			err = mDNS_RenameAndReregisterService(m, srs, &instance->request->u.servicereg.name);
+			if (err) LogMsg("ERROR: regservice_callback - RenameAndReregisterService returned %d", err);
+			// error should never happen - safest to log and continue
+			}
+		else
+			unlink_and_free_service_instance(instance);
+		}
+	else if (result == mStatus_NameConflict)
+		{
+		if (instance->request->u.servicereg.autorename)
+			{
+			external_stop_advertising_helper(instance);
+			if (instance->request->u.servicereg.autoname && CountPeerRegistrations(m, srs) == 0)
+				{
+				// On conflict for an autoname service, rename and reregister *all* autoname services
+				IncrementLabelSuffix(&m->nicelabel, mDNStrue);
+				mDNS_ConfigChanged(m);	// Will call back into udsserver_handle_configchange()
+				}
+			else	// On conflict for a non-autoname service, rename and reregister just that one service
+				{
+				if (instance->clientnotified) SendServiceRemovalNotification(srs);
+				mDNS_RenameAndReregisterService(m, srs, mDNSNULL);
+				}
+			}
+		else
+			{
+			if (!SuppressError) 
+				{
+				if (GenerateNTDResponse(srs->RR_SRV.resrec.name, srs->RR_SRV.resrec.InterfaceID, instance->request, &rep, reg_service_reply_op, kDNSServiceFlagsAdd, result) != mStatus_NoError)
+					LogMsg("%3d: regservice_callback: %##s is not valid DNS-SD SRV name", instance->request->sd, srs->RR_SRV.resrec.name->c);
+				else { append_reply(instance->request, rep); instance->clientnotified = mDNStrue; }
+				}
+			unlink_and_free_service_instance(instance);
+			}
+		}
+	else		// Not mStatus_NoError, mStatus_MemFree, or mStatus_NameConflict
+		{
+		if (!SuppressError) 
+			{
+			if (GenerateNTDResponse(srs->RR_SRV.resrec.name, srs->RR_SRV.resrec.InterfaceID, instance->request, &rep, reg_service_reply_op, kDNSServiceFlagsAdd, result) != mStatus_NoError)
+				LogMsg("%3d: regservice_callback: %##s is not valid DNS-SD SRV name", instance->request->sd, srs->RR_SRV.resrec.name->c);
+			else { append_reply(instance->request, rep); instance->clientnotified = mDNStrue; }
+			}
+		}
+	}
+
+mDNSlocal void regrecord_callback(mDNS *const m, AuthRecord *rr, mStatus result)
+	{
+	(void)m; // Unused
+	if (!rr->RecordContext)		// parent struct already freed by termination callback
+		{
+		if (result == mStatus_NoError)
+			LogMsg("Error: regrecord_callback: successful registration of orphaned record %s", ARDisplayString(m, rr));
+		else
+			{
+			if (result != mStatus_MemFree) LogMsg("regrecord_callback: error %d received after parent termination", result);
+
+			// We come here when the record is being deregistered either from DNSServiceRemoveRecord or connection_termination.
+			// If the record has been updated, we need to free the rdata. Everytime we call mDNS_Update, it calls update_callback
+			// with the old rdata (so that we can free it) and stores the new rdata in "rr->resrec.rdata". This means, we need
+			// to free the latest rdata for which the update_callback was never called with.
+			if (rr->resrec.rdata != &rr->rdatastorage) freeL("RData/regrecord_callback", rr->resrec.rdata);
+			freeL("AuthRecord/regrecord_callback", rr);
+			}
+		}
+	else
+		{
+		registered_record_entry *re = rr->RecordContext;
+		request_state *request = re->request;
+
+		if (mDNS_LoggingEnabled)
+			{
+			char *fmt = (result == mStatus_NoError)      ? "%3d: DNSServiceRegisterRecord(%u %s) REGISTERED"    :
+						(result == mStatus_MemFree)      ? "%3d: DNSServiceRegisterRecord(%u %s) DEREGISTERED"  :
+						(result == mStatus_NameConflict) ? "%3d: DNSServiceRegisterRecord(%u %s) NAME CONFLICT" :
+														   "%3d: DNSServiceRegisterRecord(%u %s) %d";
+			LogOperation(fmt, request->sd, re->key, RRDisplayString(m, &rr->resrec), result);
+			}
+
+		if (result != mStatus_MemFree)
+			{
+			int len = sizeof(DNSServiceFlags) + sizeof(mDNSu32) + sizeof(DNSServiceErrorType);
+			reply_state *reply = create_reply(reg_record_reply_op, len, request);
+			reply->mhdr->client_context = re->regrec_client_context;
+			reply->rhdr->flags = dnssd_htonl(0);
+			reply->rhdr->ifi   = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(m, rr->resrec.InterfaceID, mDNSfalse));
+			reply->rhdr->error = dnssd_htonl(result);
+			append_reply(request, reply);
+			}
+
+		if (result)
+			{
+			// unlink from list, free memory
+			registered_record_entry **ptr = &request->u.reg_recs;
+			while (*ptr && (*ptr) != re) ptr = &(*ptr)->next;
+			if (!*ptr) { LogMsg("regrecord_callback - record not in list!"); return; }
+			*ptr = (*ptr)->next;
+			freeL("registered_record_entry AuthRecord regrecord_callback", re->rr);
+			freeL("registered_record_entry regrecord_callback", re);
+			}
+		else
+			{
+			if (re->external_advertise) LogMsg("regrecord_callback: external_advertise already set!");
+
+			if (re->origInterfaceID == mDNSInterface_P2P || (!re->origInterfaceID && IsLocalDomain(&rr->namestorage) && (request->flags & kDNSServiceFlagsIncludeP2P)))
+				{
+				LogInfo("regrecord_callback: calling external_start_advertising_service");
+				external_start_advertising_service(&rr->resrec);
+				re->external_advertise = mDNStrue;
+				}
+			}
+		}
+	}
+
+mDNSlocal void connection_termination(request_state *request)
+	{
+	// When terminating a shared connection, we need to scan the all_requests list
+	// and terminate any subbordinate operations sharing this file descriptor
+	request_state **req = &all_requests;
+	
+	LogOperation("%3d: DNSServiceCreateConnection STOP", request->sd);
+	
+	while (*req)
+		{
+		if ((*req)->primary == request)
+			{
+			// Since we're already doing a list traversal, we unlink the request directly instead of using AbortUnlinkAndFree()
+			request_state *tmp = *req;
+			if (tmp->primary == tmp) LogMsg("connection_termination ERROR (*req)->primary == *req for %p %d",                  tmp, tmp->sd);
+			if (tmp->replies)        LogMsg("connection_termination ERROR How can subordinate req %p %d have replies queued?", tmp, tmp->sd);
+			abort_request(tmp);
+			*req = tmp->next;
+			freeL("request_state/connection_termination", tmp);
+			}
+		else
+			req = &(*req)->next;
+		}
+
+	while (request->u.reg_recs)
+		{
+		registered_record_entry *ptr = request->u.reg_recs;
+		LogOperation("%3d: DNSServiceRegisterRecord(%u %s) STOP", request->sd, ptr->key, RRDisplayString(&mDNSStorage, &ptr->rr->resrec));
+		request->u.reg_recs = request->u.reg_recs->next;
+		ptr->rr->RecordContext = NULL;
+		if (ptr->external_advertise)
+			{
+			ptr->external_advertise = mDNSfalse;
+			external_stop_advertising_service(&ptr->rr->resrec);
+			}
+		mDNS_Deregister(&mDNSStorage, ptr->rr);		// Will free ptr->rr for us
+		freeL("registered_record_entry/connection_termination", ptr);
+		}
+	}
+
+mDNSlocal void handle_cancel_request(request_state *request)
+	{
+	request_state **req = &all_requests;
+	LogOperation("%3d: Cancel %08X %08X", request->sd, request->hdr.client_context.u32[1], request->hdr.client_context.u32[0]);
+	while (*req)
+		{
+		if ((*req)->primary == request &&
+			(*req)->hdr.client_context.u32[0] == request->hdr.client_context.u32[0] &&
+			(*req)->hdr.client_context.u32[1] == request->hdr.client_context.u32[1])
+			{
+			// Since we're already doing a list traversal, we unlink the request directly instead of using AbortUnlinkAndFree()
+			request_state *tmp = *req;
+			abort_request(tmp);
+			*req = tmp->next;
+			freeL("request_state/handle_cancel_request", tmp);
+			}
+		else
+			req = &(*req)->next;
+		}
+	}
+
+mDNSlocal mStatus handle_regrecord_request(request_state *request)
+	{
+	mStatus err = mStatus_BadParamErr;
+	AuthRecord *rr = read_rr_from_ipc_msg(request, 1, 1);
+	if (rr)
+		{
+		registered_record_entry *re;
+		// Don't allow non-local domains to be regsitered as LocalOnly. Allowing this would permit
+		// clients to register records such as www.bigbank.com A w.x.y.z to redirect Safari.
+		if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly && !IsLocalDomain(rr->resrec.name) &&
+			rr->resrec.rrclass == kDNSClass_IN && (rr->resrec.rrtype == kDNSType_A || rr->resrec.rrtype == kDNSType_AAAA ||
+			rr->resrec.rrtype == kDNSType_CNAME))
+			{
+			freeL("AuthRecord/handle_regrecord_request", rr);
+			return (mStatus_BadParamErr);
+			}
+		// allocate registration entry, link into list
+		re = mallocL("registered_record_entry", sizeof(registered_record_entry));
+		if (!re) FatalError("ERROR: malloc");
+		re->key                   = request->hdr.reg_index;
+		re->rr                    = rr;
+		re->regrec_client_context = request->hdr.client_context;
+		re->request               = request;
+		re->external_advertise    = mDNSfalse;
+		rr->RecordContext         = re;
+		rr->RecordCallback        = regrecord_callback;
+
+		re->origInterfaceID = rr->resrec.InterfaceID;
+		if (rr->resrec.InterfaceID == mDNSInterface_P2P) rr->resrec.InterfaceID = mDNSInterface_Any;	
+#if 0
+		if (!AuthorizedDomain(request, rr->resrec.name, AutoRegistrationDomains))	return (mStatus_NoError);
+#endif
+		if (rr->resrec.rroriginalttl == 0)
+			rr->resrec.rroriginalttl = DefaultTTLforRRType(rr->resrec.rrtype);
+	
+		LogOperation("%3d: DNSServiceRegisterRecord(%u %s) START", request->sd, re->key, RRDisplayString(&mDNSStorage, &rr->resrec));
+		err = mDNS_Register(&mDNSStorage, rr);
+		if (err)
+			{
+			LogOperation("%3d: DNSServiceRegisterRecord(%u %s) ERROR (%d)", request->sd, re->key, RRDisplayString(&mDNSStorage, &rr->resrec), err);
+			freeL("registered_record_entry", re);
+			freeL("registered_record_entry/AuthRecord", rr);
+			}
+		else
+			{
+			re->next = request->u.reg_recs;
+			request->u.reg_recs = re;
+			}
+		}
+	return(err);
+	}
+
+mDNSlocal void UpdateDeviceInfoRecord(mDNS *const m);
+
+mDNSlocal void regservice_termination_callback(request_state *request)
+	{
+	if (!request) { LogMsg("regservice_termination_callback context is NULL"); return; }
+	while (request->u.servicereg.instances)
+		{
+		service_instance *p = request->u.servicereg.instances;
+		request->u.servicereg.instances = request->u.servicereg.instances->next;
+		// only safe to free memory if registration is not valid, i.e. deregister fails (which invalidates p)
+		LogOperation("%3d: DNSServiceRegister(%##s, %u) STOP",
+			request->sd, p->srs.RR_SRV.resrec.name->c, mDNSVal16(p->srs.RR_SRV.resrec.rdata->u.srv.port));
+		
+		external_stop_advertising_helper(p);
+
+		// Clear backpointer *before* calling mDNS_DeregisterService/unlink_and_free_service_instance
+		// We don't need unlink_and_free_service_instance to cut its element from the list, because we're already advancing
+		// request->u.servicereg.instances as we work our way through the list, implicitly cutting one element at a time
+		// We can't clear p->request *after* the calling mDNS_DeregisterService/unlink_and_free_service_instance
+		// because by then we might have already freed p
+		p->request = NULL;
+		if (mDNS_DeregisterService(&mDNSStorage, &p->srs)) unlink_and_free_service_instance(p);
+		// Don't touch service_instance *p after this -- it's likely to have been freed already
+		}
+	if (request->u.servicereg.txtdata)
+		{ freeL("service_info txtdata", request->u.servicereg.txtdata); request->u.servicereg.txtdata = NULL; }
+	if (request->u.servicereg.autoname)
+		{
+		// Clear autoname before calling UpdateDeviceInfoRecord() so it doesn't mistakenly include this in its count of active autoname registrations
+		request->u.servicereg.autoname = mDNSfalse;
+		UpdateDeviceInfoRecord(&mDNSStorage);
+		}
+	}
+
+mDNSlocal request_state *LocateSubordinateRequest(request_state *request)
+	{
+	request_state *req;
+	for (req = all_requests; req; req = req->next)
+		if (req->primary == request &&
+			req->hdr.client_context.u32[0] == request->hdr.client_context.u32[0] &&
+			req->hdr.client_context.u32[1] == request->hdr.client_context.u32[1]) return(req);
+	return(request);
+	}
+
+mDNSlocal mStatus add_record_to_service(request_state *request, service_instance *instance, mDNSu16 rrtype, mDNSu16 rdlen, const char *rdata, mDNSu32 ttl)
+	{
+	ServiceRecordSet *srs = &instance->srs;
+	mStatus result;
+	int size = rdlen > sizeof(RDataBody) ? rdlen : sizeof(RDataBody);
+	ExtraResourceRecord *extra = mallocL("ExtraResourceRecord", sizeof(*extra) - sizeof(RDataBody) + size);
+	if (!extra) { my_perror("ERROR: malloc"); return mStatus_NoMemoryErr; }
+
+	mDNSPlatformMemZero(extra, sizeof(ExtraResourceRecord));  // OK if oversized rdata not zero'd
+	extra->r.resrec.rrtype = rrtype;
+	extra->r.rdatastorage.MaxRDLength = (mDNSu16) size;
+	extra->r.resrec.rdlength = rdlen;
+	mDNSPlatformMemCopy(&extra->r.rdatastorage.u.data, rdata, rdlen);
+
+	result = mDNS_AddRecordToService(&mDNSStorage, srs, extra, &extra->r.rdatastorage, ttl,
+					(request->flags & kDNSServiceFlagsIncludeP2P) ? 1: 0);
+	if (result) { freeL("ExtraResourceRecord/add_record_to_service", extra); return result; }
+
+	extra->ClientID = request->hdr.reg_index;
+	if (instance->external_advertise && (instance->request->u.servicereg.InterfaceID == mDNSInterface_P2P || (!instance->request->u.servicereg.InterfaceID && SameDomainName(&instance->domain, &localdomain) && (instance->request->flags & kDNSServiceFlagsIncludeP2P))))
+		{
+		LogInfo("add_record_to_service: calling external_start_advertising_service");
+		external_start_advertising_service(&extra->r.resrec);
+		}
+	return result;
+	}
+
+mDNSlocal mStatus handle_add_request(request_state *request)
+	{
+	service_instance *i;
+	mStatus result = mStatus_UnknownErr;
+	DNSServiceFlags flags  = get_flags (&request->msgptr, request->msgend);
+	mDNSu16         rrtype = get_uint16(&request->msgptr, request->msgend);
+	mDNSu16         rdlen  = get_uint16(&request->msgptr, request->msgend);
+	const char     *rdata  = get_rdata (&request->msgptr, request->msgend, rdlen);
+	mDNSu32         ttl    = get_uint32(&request->msgptr, request->msgend);
+	if (!ttl) ttl = DefaultTTLforRRType(rrtype);
+	(void)flags; // Unused
+
+	if (!request->msgptr) { LogMsg("%3d: DNSServiceAddRecord(unreadable parameters)", request->sd); return(mStatus_BadParamErr); }
+
+	// If this is a shared connection, check if the operation actually applies to a subordinate request_state object
+	if (request->terminate == connection_termination) request = LocateSubordinateRequest(request);
+
+	if (request->terminate != regservice_termination_callback)
+		{ LogMsg("%3d: DNSServiceAddRecord(not a registered service ref)", request->sd); return(mStatus_BadParamErr); }
+
+	// For a service registered with zero port, don't allow adding records. This mostly happens due to a bug
+	// in the application. See radar://9165807.
+	if (mDNSIPPortIsZero(request->u.servicereg.port))
+		{ LogMsg("%3d: DNSServiceAddRecord: adding record to a service registered with zero port", request->sd); return(mStatus_BadParamErr); }
+
+	LogOperation("%3d: DNSServiceAddRecord(%X, %##s, %s, %d)", request->sd, flags,
+		(request->u.servicereg.instances) ? request->u.servicereg.instances->srs.RR_SRV.resrec.name->c : NULL, DNSTypeName(rrtype), rdlen);
+
+	for (i = request->u.servicereg.instances; i; i = i->next)
+		{
+		result = add_record_to_service(request, i, rrtype, rdlen, rdata, ttl);
+		if (result && i->default_local) break;
+		else result = mStatus_NoError;  // suppress non-local default errors
+		}
+
+	return(result);
+	}
+
+mDNSlocal void update_callback(mDNS *const m, AuthRecord *const rr, RData *oldrd, mDNSu16 oldrdlen)
+	{
+	mDNSBool external_advertise = (rr->UpdateContext) ? *((mDNSBool *)rr->UpdateContext) : mDNSfalse;
+	(void)m; // Unused
+	
+	// There are three cases.
+	//
+	// 1. We have updated the primary TXT record of the service
+	// 2. We have updated the TXT record that was added to the service using DNSServiceAddRecord
+	// 3. We have updated the TXT record that was registered using DNSServiceRegisterRecord
+	//
+	// external_advertise is set if we have advertised at least once during the initial addition
+	// of the record in all of the three cases above. We should have checked for InterfaceID/LocalDomain
+	// checks during the first time and hence we don't do any checks here
+	if (external_advertise)
+		{
+		ResourceRecord ext = rr->resrec;
+		if (ext.rdlength == oldrdlen && mDNSPlatformMemSame(&ext.rdata->u, &oldrd->u, oldrdlen)) goto exit;
+		SetNewRData(&ext, oldrd, oldrdlen);
+		external_stop_advertising_service(&ext);
+		LogInfo("update_callback: calling external_start_advertising_service");
+		external_start_advertising_service(&rr->resrec);
+		}
+exit:
+	if (oldrd != &rr->rdatastorage) freeL("RData/update_callback", oldrd);
+	}
+
+mDNSlocal mStatus update_record(AuthRecord *rr, mDNSu16 rdlen, const char *rdata, mDNSu32 ttl, const mDNSBool *const external_advertise)
+	{
+	mStatus result;
+	const int rdsize = rdlen > sizeof(RDataBody) ? rdlen : sizeof(RDataBody);
+	RData *newrd = mallocL("RData/update_record", sizeof(RData) - sizeof(RDataBody) + rdsize);
+	if (!newrd) FatalError("ERROR: malloc");
+	newrd->MaxRDLength = (mDNSu16) rdsize;
+	mDNSPlatformMemCopy(&newrd->u, rdata, rdlen);
+
+	// BIND named (name daemon) doesn't allow TXT records with zero-length rdata. This is strictly speaking correct,
+	// since RFC 1035 specifies a TXT record as "One or more <character-string>s", not "Zero or more <character-string>s".
+	// Since some legacy apps try to create zero-length TXT records, we'll silently correct it here.
+	if (rr->resrec.rrtype == kDNSType_TXT && rdlen == 0) { rdlen = 1; newrd->u.txt.c[0] = 0; }
+	
+	if (external_advertise) rr->UpdateContext = (void *)external_advertise;
+	
+	result = mDNS_Update(&mDNSStorage, rr, ttl, rdlen, newrd, update_callback);
+	if (result) { LogMsg("update_record: Error %d for %s", (int)result, ARDisplayString(&mDNSStorage, rr)); freeL("RData/update_record", newrd); }
+	return result;
+	}
+
+mDNSlocal mStatus handle_update_request(request_state *request)
+	{
+	const ipc_msg_hdr *const hdr = &request->hdr;
+	mStatus result = mStatus_BadReferenceErr;
+	service_instance *i;
+	AuthRecord *rr = NULL;
+
+	// get the message data
+	DNSServiceFlags flags = get_flags (&request->msgptr, request->msgend);	// flags unused
+	mDNSu16         rdlen = get_uint16(&request->msgptr, request->msgend);
+	const char     *rdata = get_rdata (&request->msgptr, request->msgend, rdlen);
+	mDNSu32         ttl   = get_uint32(&request->msgptr, request->msgend);
+	(void)flags; // Unused
+
+	if (!request->msgptr) { LogMsg("%3d: DNSServiceUpdateRecord(unreadable parameters)", request->sd); return(mStatus_BadParamErr); }
+
+	// If this is a shared connection, check if the operation actually applies to a subordinate request_state object
+	if (request->terminate == connection_termination) request = LocateSubordinateRequest(request);
+
+	if (request->terminate == connection_termination)
+		{
+		// update an individually registered record
+		registered_record_entry *reptr;
+		for (reptr = request->u.reg_recs; reptr; reptr = reptr->next)
+			{
+			if (reptr->key == hdr->reg_index)
+				{
+				result = update_record(reptr->rr, rdlen, rdata, ttl, &reptr->external_advertise);
+				LogOperation("%3d: DNSServiceUpdateRecord(%##s, %s)",
+					request->sd, reptr->rr->resrec.name->c, reptr->rr ? DNSTypeName(reptr->rr->resrec.rrtype) : "<NONE>");
+				goto end;
+				}
+			}
+		result = mStatus_BadReferenceErr;
+		goto end;
+		}
+
+	if (request->terminate != regservice_termination_callback)
+		{ LogMsg("%3d: DNSServiceUpdateRecord(not a registered service ref)", request->sd); return(mStatus_BadParamErr); }
+
+	// For a service registered with zero port, only SRV record is initialized. Don't allow any updates.
+	if (mDNSIPPortIsZero(request->u.servicereg.port))
+		{ LogMsg("%3d: DNSServiceUpdateRecord: updating the record of a service registered with zero port", request->sd); return(mStatus_BadParamErr); }
+
+	// update the saved off TXT data for the service
+	if (hdr->reg_index == TXT_RECORD_INDEX)
+		{
+		if (request->u.servicereg.txtdata)
+			{ freeL("service_info txtdata", request->u.servicereg.txtdata); request->u.servicereg.txtdata = NULL; }
+		if (rdlen > 0)
+			{
+			request->u.servicereg.txtdata = mallocL("service_info txtdata", rdlen);
+			if (!request->u.servicereg.txtdata) FatalError("ERROR: handle_update_request - malloc");
+			mDNSPlatformMemCopy(request->u.servicereg.txtdata, rdata, rdlen);
+			}
+		request->u.servicereg.txtlen = rdlen;
+		}
+
+	// update a record from a service record set
+	for (i = request->u.servicereg.instances; i; i = i->next)
+		{
+		if (hdr->reg_index == TXT_RECORD_INDEX) rr = &i->srs.RR_TXT;
+		else
+			{
+			ExtraResourceRecord *e;
+			for (e = i->srs.Extras; e; e = e->next)
+				if (e->ClientID == hdr->reg_index) { rr = &e->r; break; }
+			}
+
+		if (!rr) { result = mStatus_BadReferenceErr; goto end; }
+		result = update_record(rr, rdlen, rdata, ttl, &i->external_advertise);
+		if (result && i->default_local) goto end;
+		else result = mStatus_NoError;  // suppress non-local default errors
+		}
+
+end:
+	if (request->terminate == regservice_termination_callback)
+		LogOperation("%3d: DNSServiceUpdateRecord(%##s, %s)", request->sd,
+			(request->u.servicereg.instances) ? request->u.servicereg.instances->srs.RR_SRV.resrec.name->c : NULL,
+			rr ? DNSTypeName(rr->resrec.rrtype) : "<NONE>");
+
+	return(result);
+	}
+
+// remove a resource record registered via DNSServiceRegisterRecord()
+mDNSlocal mStatus remove_record(request_state *request)
+	{
+	mStatus err = mStatus_UnknownErr;
+	registered_record_entry *e, **ptr = &request->u.reg_recs;
+
+	while (*ptr && (*ptr)->key != request->hdr.reg_index) ptr = &(*ptr)->next;
+	if (!*ptr) { LogMsg("%3d: DNSServiceRemoveRecord(%u) not found", request->sd, request->hdr.reg_index); return mStatus_BadReferenceErr; }
+	e = *ptr;
+	*ptr = e->next; // unlink
+
+	LogOperation("%3d: DNSServiceRemoveRecord(%u %s)", request->sd, e->key, RRDisplayString(&mDNSStorage, &e->rr->resrec));
+	e->rr->RecordContext = NULL;
+	if (e->external_advertise)
+		{
+		external_stop_advertising_service(&e->rr->resrec);
+		e->external_advertise = mDNSfalse;
+		}
+	err = mDNS_Deregister(&mDNSStorage, e->rr);		// Will free e->rr for us; we're responsible for freeing e
+	if (err)
+		{
+		LogMsg("ERROR: remove_record, mDNS_Deregister: %d", err);
+		freeL("registered_record_entry AuthRecord remove_record", e->rr);
+		}
+		
+	freeL("registered_record_entry remove_record", e);
+	return err;
+	}
+
+mDNSlocal mStatus remove_extra(const request_state *const request, service_instance *const serv, mDNSu16 *const rrtype)
+	{
+	mStatus err = mStatus_BadReferenceErr;
+	ExtraResourceRecord *ptr;
+
+	for (ptr = serv->srs.Extras; ptr; ptr = ptr->next)		
+		{
+		if (ptr->ClientID == request->hdr.reg_index) // found match
+			{
+			*rrtype = ptr->r.resrec.rrtype;
+			if (serv->external_advertise) external_stop_advertising_service(&ptr->r.resrec);
+			err = mDNS_RemoveRecordFromService(&mDNSStorage, &serv->srs, ptr, FreeExtraRR, ptr);
+			break;
+			}
+		}
+	return err;
+	}
+
+mDNSlocal mStatus handle_removerecord_request(request_state *request)
+	{
+	mStatus err = mStatus_BadReferenceErr;
+	get_flags(&request->msgptr, request->msgend);	// flags unused
+
+	if (!request->msgptr) { LogMsg("%3d: DNSServiceRemoveRecord(unreadable parameters)", request->sd); return(mStatus_BadParamErr); }
+
+	// If this is a shared connection, check if the operation actually applies to a subordinate request_state object
+	if (request->terminate == connection_termination) request = LocateSubordinateRequest(request);
+
+	if (request->terminate == connection_termination)
+		err = remove_record(request);  // remove individually registered record
+	else if (request->terminate != regservice_termination_callback)
+		{ LogMsg("%3d: DNSServiceRemoveRecord(not a registered service ref)", request->sd); return(mStatus_BadParamErr); }
+	else
+		{
+		service_instance *i;
+		mDNSu16 rrtype = 0;
+		LogOperation("%3d: DNSServiceRemoveRecord(%##s, %s)", request->sd,
+			(request->u.servicereg.instances) ? request->u.servicereg.instances->srs.RR_SRV.resrec.name->c : NULL,
+			rrtype ? DNSTypeName(rrtype) : "<NONE>");
+		for (i = request->u.servicereg.instances; i; i = i->next)
+			{
+			err = remove_extra(request, i, &rrtype);
+			if (err && i->default_local) break;
+			else err = mStatus_NoError;  // suppress non-local default errors
+			}
+		}
+
+	return(err);
+	}
+
+// If there's a comma followed by another character,
+// FindFirstSubType overwrites the comma with a nul and returns the pointer to the next character.
+// Otherwise, it returns a pointer to the final nul at the end of the string
+mDNSlocal char *FindFirstSubType(char *p)
+	{
+	while (*p)
+		{
+		if (p[0] == '\\' && p[1]) p += 2;
+		else if (p[0] == ',' && p[1]) { *p++ = 0; return(p); }
+		else p++;
+		}
+	return(p);
+	}
+
+// If there's a comma followed by another character,
+// FindNextSubType overwrites the comma with a nul and returns the pointer to the next character.
+// If it finds an illegal unescaped dot in the subtype name, it returns mDNSNULL
+// Otherwise, it returns a pointer to the final nul at the end of the string
+mDNSlocal char *FindNextSubType(char *p)
+	{
+	while (*p)
+		{
+		if (p[0] == '\\' && p[1])		// If escape character
+			p += 2;						// ignore following character
+		else if (p[0] == ',')			// If we found a comma
+			{
+			if (p[1]) *p++ = 0;
+			return(p);
+			}
+		else if (p[0] == '.')
+			return(mDNSNULL);
+		else p++;
+		}
+	return(p);
+	}
+
+// Returns -1 if illegal subtype found
+mDNSexport mDNSs32 ChopSubTypes(char *regtype)
+	{
+	mDNSs32 NumSubTypes = 0;
+	char *stp = FindFirstSubType(regtype);
+	while (stp && *stp)					// If we found a comma...
+		{
+		if (*stp == ',') return(-1);
+		NumSubTypes++;
+		stp = FindNextSubType(stp);
+		}
+	if (!stp) return(-1);
+	return(NumSubTypes);
+	}
+
+mDNSexport AuthRecord *AllocateSubTypes(mDNSs32 NumSubTypes, char *p)
+	{
+	AuthRecord *st = mDNSNULL;
+	if (NumSubTypes)
+		{
+		mDNSs32 i;
+		st = mallocL("ServiceSubTypes", NumSubTypes * sizeof(AuthRecord));
+		if (!st) return(mDNSNULL);
+		for (i = 0; i < NumSubTypes; i++)
+			{
+			mDNS_SetupResourceRecord(&st[i], mDNSNULL, mDNSInterface_Any, kDNSQType_ANY, kStandardTTL, 0, AuthRecordAny, mDNSNULL, mDNSNULL);
+			while (*p) p++;
+			p++;
+			if (!MakeDomainNameFromDNSNameString(&st[i].namestorage, p))
+				{ freeL("ServiceSubTypes", st); return(mDNSNULL); }
+			}
+		}
+	return(st);
+	}
+
+mDNSlocal mStatus register_service_instance(request_state *request, const domainname *domain)
+	{
+	service_instance **ptr, *instance;
+	const int extra_size = (request->u.servicereg.txtlen > sizeof(RDataBody)) ? (request->u.servicereg.txtlen - sizeof(RDataBody)) : 0;
+	const mDNSBool DomainIsLocal = SameDomainName(domain, &localdomain);
+	mStatus result;
+	mDNSInterfaceID interfaceID = request->u.servicereg.InterfaceID;
+	mDNSu32 regFlags = 0;
+
+	if (interfaceID == mDNSInterface_P2P)
+		{
+		interfaceID = mDNSInterface_Any;
+		regFlags |= regFlagIncludeP2P;
+		}
+	else if (request->flags & kDNSServiceFlagsIncludeP2P)
+		regFlags |= regFlagIncludeP2P;
+
+	// client guarantees that record names are unique
+	if (request->flags & kDNSServiceFlagsForce)
+		regFlags |= regFlagKnownUnique;
+
+	// If the client specified an interface, but no domain, then we honor the specified interface for the "local" (mDNS)
+	// registration but for the wide-area registrations we don't (currently) have any concept of a wide-area unicast
+	// registrations scoped to a specific interface, so for the automatic domains we add we must *not* specify an interface.
+	// (Specifying an interface with an apparently wide-area domain (i.e. something other than "local")
+	// currently forces the registration to use mDNS multicast despite the apparently wide-area domain.)
+	if (request->u.servicereg.default_domain && !DomainIsLocal) interfaceID = mDNSInterface_Any;
+
+	for (ptr = &request->u.servicereg.instances; *ptr; ptr = &(*ptr)->next)
+		{
+		if (SameDomainName(&(*ptr)->domain, domain))
+			{
+			LogMsg("register_service_instance: domain %##s already registered for %#s.%##s",
+				domain->c, &request->u.servicereg.name, &request->u.servicereg.type);
+			return mStatus_AlreadyRegistered;
+			}
+		}
+
+	if (mDNSStorage.KnownBugs & mDNS_KnownBug_LimitedIPv6)
+		{
+		// Special-case hack: On Mac OS X 10.6.x and earlier we don't advertise SMB service in AutoTunnel domains,
+		// because AutoTunnel services have to support IPv6, and in Mac OS X 10.6.x the SMB server does not.
+		// <rdar://problem/5482322> BTMM: Don't advertise SMB with BTMM because it doesn't support IPv6
+		if (SameDomainName(&request->u.servicereg.type, (const domainname *) "\x4" "_smb" "\x4" "_tcp"))
+			{
+			DomainAuthInfo *AuthInfo = GetAuthInfoForName(&mDNSStorage, domain);
+			if (AuthInfo && AuthInfo->AutoTunnel) return(kDNSServiceErr_Unsupported);
+			}
+		}
+
+	instance = mallocL("service_instance", sizeof(*instance) + extra_size);
+	if (!instance) { my_perror("ERROR: malloc"); return mStatus_NoMemoryErr; }
+
+	instance->next							= mDNSNULL;
+	instance->request						= request;
+	instance->subtypes						= AllocateSubTypes(request->u.servicereg.num_subtypes, request->u.servicereg.type_as_string);
+	instance->renameonmemfree				= 0;
+	instance->clientnotified				= mDNSfalse;
+	instance->default_local					= (request->u.servicereg.default_domain && DomainIsLocal);
+	instance->external_advertise            = mDNSfalse;
+	AssignDomainName(&instance->domain, domain);
+
+	if (request->u.servicereg.num_subtypes && !instance->subtypes)
+		{ unlink_and_free_service_instance(instance); instance = NULL; FatalError("ERROR: malloc"); }
+
+	result = mDNS_RegisterService(&mDNSStorage, &instance->srs,
+		&request->u.servicereg.name, &request->u.servicereg.type, domain,
+		request->u.servicereg.host.c[0] ? &request->u.servicereg.host : NULL,
+		request->u.servicereg.port,
+		request->u.servicereg.txtdata, request->u.servicereg.txtlen,
+		instance->subtypes, request->u.servicereg.num_subtypes,
+		interfaceID, regservice_callback, instance, regFlags);
+
+	if (!result)
+		{
+		*ptr = instance;		// Append this to the end of our request->u.servicereg.instances list
+		LogOperation("%3d: DNSServiceRegister(%##s, %u) ADDED",
+			instance->request->sd, instance->srs.RR_SRV.resrec.name->c, mDNSVal16(request->u.servicereg.port));
+		}
+	else
+		{
+		LogMsg("register_service_instance %#s.%##s%##s error %d",
+			&request->u.servicereg.name, &request->u.servicereg.type, domain->c, result);
+		unlink_and_free_service_instance(instance);
+		}
+
+	return result;
+	}
+
+mDNSlocal void udsserver_default_reg_domain_changed(const DNameListElem *const d, const mDNSBool add)
+	{
+	request_state *request;
+
+#if APPLE_OSX_mDNSResponder
+	machserver_automatic_registration_domain_changed(&d->name, add);
+#endif // APPLE_OSX_mDNSResponder
+
+	LogMsg("%s registration domain %##s", add ? "Adding" : "Removing", d->name.c);
+	for (request = all_requests; request; request = request->next)
+		{
+		if (request->terminate != regservice_termination_callback) continue;
+		if (!request->u.servicereg.default_domain) continue;
+		if (!d->uid || SystemUID(request->uid) || request->uid == d->uid)
+			{
+			service_instance **ptr = &request->u.servicereg.instances;
+			while (*ptr && !SameDomainName(&(*ptr)->domain, &d->name)) ptr = &(*ptr)->next;
+			if (add)
+				{
+				// If we don't already have this domain in our list for this registration, add it now
+				if (!*ptr) register_service_instance(request, &d->name);
+				else debugf("udsserver_default_reg_domain_changed %##s already in list, not re-adding", &d->name);
+				}
+			else
+				{
+				// Normally we should not fail to find the specified instance
+				// One case where this can happen is if a uDNS update fails for some reason,
+				// and regservice_callback then calls unlink_and_free_service_instance and disposes of that instance.
+				if (!*ptr)
+					LogMsg("udsserver_default_reg_domain_changed domain %##s not found for service %#s type %s",
+						&d->name, request->u.servicereg.name.c, request->u.servicereg.type_as_string);
+				else
+					{
+					DNameListElem *p;
+					for (p = AutoRegistrationDomains; p; p=p->next)
+						if (!p->uid || SystemUID(request->uid) || request->uid == p->uid)
+							if (SameDomainName(&d->name, &p->name)) break;
+					if (p) debugf("udsserver_default_reg_domain_changed %##s still in list, not removing", &d->name);
+					else
+						{
+						mStatus err;
+						service_instance *si = *ptr;
+						*ptr = si->next;
+						if (si->clientnotified) SendServiceRemovalNotification(&si->srs); // Do this *before* clearing si->request backpointer
+						// Now that we've cut this service_instance from the list, we MUST clear the si->request backpointer.
+						// Otherwise what can happen is this: While our mDNS_DeregisterService is in the
+						// process of completing asynchronously, the client cancels the entire operation, so
+						// regservice_termination_callback then runs through the whole list deregistering each
+						// instance, clearing the backpointers, and then disposing the parent request_state object.
+						// However, because this service_instance isn't in the list any more, regservice_termination_callback
+						// has no way to find it and clear its backpointer, and then when our mDNS_DeregisterService finally
+						// completes later with a mStatus_MemFree message, it calls unlink_and_free_service_instance() with
+						// a service_instance with a stale si->request backpointer pointing to memory that's already been freed.
+						si->request = NULL;
+						err = mDNS_DeregisterService(&mDNSStorage, &si->srs);
+						if (err) { LogMsg("udsserver_default_reg_domain_changed err %d", err); unlink_and_free_service_instance(si); }
+						}
+					}
+				}
+			}
+		}
+	}
+
+mDNSlocal mStatus handle_regservice_request(request_state *request)
+	{
+	char name[256];	// Lots of spare space for extra-long names that we'll auto-truncate down to 63 bytes
+	char domain[MAX_ESCAPED_DOMAIN_NAME], host[MAX_ESCAPED_DOMAIN_NAME];
+	char type_as_string[MAX_ESCAPED_DOMAIN_NAME];
+	domainname d, srv;
+	mStatus err;
+	const char *msgTXTData;
+
+	DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend);
+	mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend);
+	mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex);
+	if (interfaceIndex && !InterfaceID)
+		{ LogMsg("ERROR: handle_regservice_request - Couldn't find interfaceIndex %d", interfaceIndex); return(mStatus_BadParamErr); }
+
+	if (get_string(&request->msgptr, request->msgend, name, sizeof(name)) < 0 ||
+		get_string(&request->msgptr, request->msgend, type_as_string, MAX_ESCAPED_DOMAIN_NAME) < 0 ||
+		get_string(&request->msgptr, request->msgend, domain, MAX_ESCAPED_DOMAIN_NAME) < 0 ||
+		get_string(&request->msgptr, request->msgend, host, MAX_ESCAPED_DOMAIN_NAME) < 0)
+		{ LogMsg("ERROR: handle_regservice_request - Couldn't read name/regtype/domain"); return(mStatus_BadParamErr); }
+
+	request->flags = flags;
+	request->u.servicereg.InterfaceID = InterfaceID;
+	request->u.servicereg.instances = NULL;
+	request->u.servicereg.txtlen  = 0;
+	request->u.servicereg.txtdata = NULL;
+	mDNSPlatformStrLCopy(request->u.servicereg.type_as_string, type_as_string, sizeof(request->u.servicereg.type_as_string));
+
+	if (request->msgptr + 2 > request->msgend) request->msgptr = NULL;
+	else
+		{
+		request->u.servicereg.port.b[0] = *request->msgptr++;
+		request->u.servicereg.port.b[1] = *request->msgptr++;
+		}
+
+	request->u.servicereg.txtlen = get_uint16(&request->msgptr, request->msgend);
+	msgTXTData = get_rdata(&request->msgptr, request->msgend, request->u.servicereg.txtlen);
+	if (!request->msgptr)
+		{
+		LogMsg("%3d: DNSServiceRegister(unreadable parameters)", request->sd);
+		return(mStatus_BadParamErr);
+		}
+
+	if (request->u.servicereg.txtlen)
+		{
+		request->u.servicereg.txtdata = mallocL("service_info txtdata", request->u.servicereg.txtlen);
+		if (!request->u.servicereg.txtdata) FatalError("ERROR: handle_regservice_request - malloc");
+		mDNSPlatformMemCopy(request->u.servicereg.txtdata, msgTXTData, request->u.servicereg.txtlen);
+		}
+
+	// Check for sub-types after the service type
+	request->u.servicereg.num_subtypes = ChopSubTypes(request->u.servicereg.type_as_string);	// Note: Modifies regtype string to remove trailing subtypes
+	if (request->u.servicereg.num_subtypes < 0)
+		{ LogMsg("ERROR: handle_regservice_request - ChopSubTypes failed %s", request->u.servicereg.type_as_string); return(mStatus_BadParamErr); }
+
+	// Don't try to construct "domainname t" until *after* ChopSubTypes has worked its magic
+	if (!*request->u.servicereg.type_as_string || !MakeDomainNameFromDNSNameString(&request->u.servicereg.type, request->u.servicereg.type_as_string))
+		{ LogMsg("ERROR: handle_regservice_request - type_as_string bad %s", request->u.servicereg.type_as_string); return(mStatus_BadParamErr); }
+
+	if (!name[0])
+		{
+		request->u.servicereg.name = mDNSStorage.nicelabel;
+		request->u.servicereg.autoname = mDNStrue;
+		}
+	else
+		{
+		// If the client is allowing AutoRename, then truncate name to legal length before converting it to a DomainLabel
+		if ((flags & kDNSServiceFlagsNoAutoRename) == 0)
+			{
+			int newlen = TruncateUTF8ToLength((mDNSu8*)name, mDNSPlatformStrLen(name), MAX_DOMAIN_LABEL);
+			name[newlen] = 0;
+			}
+		if (!MakeDomainLabelFromLiteralString(&request->u.servicereg.name, name))
+			{ LogMsg("ERROR: handle_regservice_request - name bad %s", name); return(mStatus_BadParamErr); }
+		request->u.servicereg.autoname = mDNSfalse;
+		}
+
+	if (*domain)
+		{
+		request->u.servicereg.default_domain = mDNSfalse;
+		if (!MakeDomainNameFromDNSNameString(&d, domain))
+			{ LogMsg("ERROR: handle_regservice_request - domain bad %s", domain); return(mStatus_BadParamErr); }
+		}
+	else
+		{
+		request->u.servicereg.default_domain = mDNStrue;
+		MakeDomainNameFromDNSNameString(&d, "local.");
+		}
+
+	if (!ConstructServiceName(&srv, &request->u.servicereg.name, &request->u.servicereg.type, &d))
+		{
+		LogMsg("ERROR: handle_regservice_request - Couldn't ConstructServiceName from, “%#s” “%##s” “%##s”",
+			request->u.servicereg.name.c, request->u.servicereg.type.c, d.c); return(mStatus_BadParamErr);
+		}
+
+	if (!MakeDomainNameFromDNSNameString(&request->u.servicereg.host, host))
+		{ LogMsg("ERROR: handle_regservice_request - host bad %s", host); return(mStatus_BadParamErr); }
+	request->u.servicereg.autorename       = (flags & kDNSServiceFlagsNoAutoRename    ) == 0;
+	request->u.servicereg.allowremotequery = (flags & kDNSServiceFlagsAllowRemoteQuery) != 0;
+
+	// Some clients use mDNS for lightweight copy protection, registering a pseudo-service with
+	// a port number of zero. When two instances of the protected client are allowed to run on one
+	// machine, we don't want to see misleading "Bogus client" messages in syslog and the console.
+	if (!mDNSIPPortIsZero(request->u.servicereg.port))
+		{
+		int count = CountExistingRegistrations(&srv, request->u.servicereg.port);
+		if (count)
+			LogMsg("Client application registered %d identical instances of service %##s port %u.",
+				count+1, srv.c, mDNSVal16(request->u.servicereg.port));
+		}
+
+	LogOperation("%3d: DNSServiceRegister(%X, %d, \"%s\", \"%s\", \"%s\", \"%s\", %u) START",
+		request->sd, flags, interfaceIndex, name, request->u.servicereg.type_as_string, domain, host, mDNSVal16(request->u.servicereg.port));
+
+	// We need to unconditionally set request->terminate, because even if we didn't successfully
+	// start any registrations right now, subsequent configuration changes may cause successful
+	// registrations to be added, and we'll need to cancel them before freeing this memory.
+	// We also need to set request->terminate first, before adding additional service instances,
+	// because the uds_validatelists uses the request->terminate function pointer to determine
+	// what kind of request this is, and therefore what kind of list validation is required.
+	request->terminate = regservice_termination_callback;
+
+	err = register_service_instance(request, &d);
+
+#if 0
+	err = AuthorizedDomain(request, &d, AutoRegistrationDomains) ? register_service_instance(request, &d) : mStatus_NoError;
+#endif
+	if (!err)
+		{
+		if (request->u.servicereg.autoname) UpdateDeviceInfoRecord(&mDNSStorage);
+
+		if (!*domain)
+			{
+			DNameListElem *ptr;
+			// Note that we don't report errors for non-local, non-explicit domains
+			for (ptr = AutoRegistrationDomains; ptr; ptr = ptr->next)
+				if (!ptr->uid || SystemUID(request->uid) || request->uid == ptr->uid)
+					register_service_instance(request, &ptr->name);
+			}
+		}
+
+	return(err);
+	}
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - DNSServiceBrowse
+#endif
+
+mDNSlocal void FoundInstance(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
+	{
+	const DNSServiceFlags flags = AddRecord ? kDNSServiceFlagsAdd : 0;
+	request_state *req = question->QuestionContext;
+	reply_state *rep;
+	(void)m; // Unused
+
+	if (answer->rrtype != kDNSType_PTR)
+		{ LogMsg("%3d: FoundInstance: Should not be called with rrtype %d (not a PTR record)", req->sd, answer->rrtype); return; }
+
+	if (GenerateNTDResponse(&answer->rdata->u.name, answer->InterfaceID, req, &rep, browse_reply_op, flags, mStatus_NoError) != mStatus_NoError)
+		{
+		if (SameDomainName(&req->u.browser.regtype, (const domainname*)"\x09_services\x07_dns-sd\x04_udp"))
+			{
+			// Special support to enable the DNSServiceBrowse call made by Bonjour Browser
+			// Remove after Bonjour Browser is updated to use DNSServiceQueryRecord instead of DNSServiceBrowse
+			GenerateBonjourBrowserResponse(&answer->rdata->u.name, answer->InterfaceID, req, &rep, browse_reply_op, flags, mStatus_NoError);
+			goto bonjourbrowserhack;
+			}
+
+		LogMsg("%3d: FoundInstance: %##s PTR %##s received from network is not valid DNS-SD service pointer",
+			req->sd, answer->name->c, answer->rdata->u.name.c);
+		return;
+		}
+
+bonjourbrowserhack:
+
+	LogOperation("%3d: DNSServiceBrowse(%##s, %s) RESULT %s %d: %s",
+		req->sd, question->qname.c, DNSTypeName(question->qtype), AddRecord ? "Add" : "Rmv",
+		mDNSPlatformInterfaceIndexfromInterfaceID(m, answer->InterfaceID, mDNSfalse), RRDisplayString(m, answer));
+
+	append_reply(req, rep);
+	}
+
+mDNSlocal mStatus add_domain_to_browser(request_state *info, const domainname *d)
+	{
+	browser_t *b, *p;
+	mStatus err;
+
+	for (p = info->u.browser.browsers; p; p = p->next)
+		{
+		if (SameDomainName(&p->domain, d))
+			{ debugf("add_domain_to_browser %##s already in list", d->c); return mStatus_AlreadyRegistered; }
+		}
+
+	b = mallocL("browser_t", sizeof(*b));
+	if (!b) return mStatus_NoMemoryErr;
+	AssignDomainName(&b->domain, d);
+	err = mDNS_StartBrowse(&mDNSStorage, &b->q,
+		&info->u.browser.regtype, d, info->u.browser.interface_id, info->u.browser.ForceMCast, FoundInstance, info);
+	if (err)
+		{
+		LogMsg("mDNS_StartBrowse returned %d for type %##s domain %##s", err, info->u.browser.regtype.c, d->c);
+		freeL("browser_t/add_domain_to_browser", b);
+		}
+	else
+		{
+		b->next = info->u.browser.browsers;
+		info->u.browser.browsers = b;
+		LogOperation("%3d: DNSServiceBrowse(%##s) START", info->sd, b->q.qname.c);
+		if (info->u.browser.interface_id == mDNSInterface_P2P || (!info->u.browser.interface_id && SameDomainName(&b->domain, &localdomain) && (info->flags & kDNSServiceFlagsIncludeP2P)))
+			{
+			domainname tmp;
+			ConstructServiceName(&tmp, NULL, &info->u.browser.regtype, &b->domain);
+			LogInfo("add_domain_to_browser: calling external_start_browsing_for_service()");
+			external_start_browsing_for_service(&mDNSStorage, &tmp, kDNSType_PTR);
+			}
+		}
+	return err;
+	}
+
+mDNSlocal void browse_termination_callback(request_state *info)
+	{
+	while (info->u.browser.browsers)
+		{
+		browser_t *ptr = info->u.browser.browsers;
+		
+		if (info->u.browser.interface_id == mDNSInterface_P2P || (!info->u.browser.interface_id && SameDomainName(&ptr->domain, &localdomain) && (info->flags & kDNSServiceFlagsIncludeP2P)))
+			{
+			domainname tmp;
+			ConstructServiceName(&tmp, NULL, &info->u.browser.regtype, &ptr->domain);
+			LogInfo("browse_termination_callback: calling external_stop_browsing_for_service()");
+			external_stop_browsing_for_service(&mDNSStorage, &tmp, kDNSType_PTR);
+			}
+		
+		info->u.browser.browsers = ptr->next;
+		LogOperation("%3d: DNSServiceBrowse(%##s) STOP", info->sd, ptr->q.qname.c);
+		mDNS_StopBrowse(&mDNSStorage, &ptr->q);  // no need to error-check result
+		freeL("browser_t/browse_termination_callback", ptr);
+		}
+	}
+
+mDNSlocal void udsserver_automatic_browse_domain_changed(const DNameListElem *const d, const mDNSBool add)
+	{
+	request_state *request;
+	debugf("udsserver_automatic_browse_domain_changed: %s default browse domain %##s", add ? "Adding" : "Removing", d->name.c);
+
+#if APPLE_OSX_mDNSResponder
+	machserver_automatic_browse_domain_changed(&d->name, add);
+#endif // APPLE_OSX_mDNSResponder
+
+	for (request = all_requests; request; request = request->next)
+		{
+		if (request->terminate != browse_termination_callback) continue;	// Not a browse operation
+		if (!request->u.browser.default_domain) continue;					// Not an auto-browse operation
+		if (!d->uid || SystemUID(request->uid) || request->uid == d->uid)
+			{
+			browser_t **ptr = &request->u.browser.browsers;
+			while (*ptr && !SameDomainName(&(*ptr)->domain, &d->name)) ptr = &(*ptr)->next;
+			if (add)
+				{
+				// If we don't already have this domain in our list for this browse operation, add it now
+				if (!*ptr) add_domain_to_browser(request, &d->name);
+				else debugf("udsserver_automatic_browse_domain_changed %##s already in list, not re-adding", &d->name);
+				}
+			else
+				{
+				if (!*ptr) LogMsg("udsserver_automatic_browse_domain_changed ERROR %##s not found", &d->name);
+				else
+					{
+					DNameListElem *p;
+					for (p = AutoBrowseDomains; p; p=p->next)
+						if (!p->uid || SystemUID(request->uid) || request->uid == p->uid)
+							if (SameDomainName(&d->name, &p->name)) break;
+					if (p) debugf("udsserver_automatic_browse_domain_changed %##s still in list, not removing", &d->name);
+					else
+						{
+						browser_t *rem = *ptr;
+						*ptr = (*ptr)->next;
+						mDNS_StopQueryWithRemoves(&mDNSStorage, &rem->q);
+						freeL("browser_t/udsserver_automatic_browse_domain_changed", rem);
+						}
+					}
+				}
+			}
+		}
+	}
+
+mDNSlocal void FreeARElemCallback(mDNS *const m, AuthRecord *const rr, mStatus result)
+	{
+	(void)m;  // unused
+	if (result == mStatus_MemFree)
+		{
+		// On shutdown, mDNS_Close automatically deregisters all records
+		// Since in this case no one has called DeregisterLocalOnlyDomainEnumPTR to cut the record
+		// from the LocalDomainEnumRecords list, we do this here before we free the memory.
+		// (This should actually no longer be necessary, now that we do the proper cleanup in
+		// udsserver_exit. To confirm this, we'll log an error message if we do find a record that
+		// hasn't been cut from the list yet. If these messages don't appear, we can delete this code.)
+		ARListElem **ptr = &LocalDomainEnumRecords;
+		while (*ptr && &(*ptr)->ar != rr) ptr = &(*ptr)->next;
+		if (*ptr) { *ptr = (*ptr)->next; LogMsg("FreeARElemCallback: Have to cut %s", ARDisplayString(m, rr)); }
+		mDNSPlatformMemFree(rr->RecordContext);
+		}
+	}
+
+// RegisterLocalOnlyDomainEnumPTR and DeregisterLocalOnlyDomainEnumPTR largely duplicate code in
+// "FoundDomain" in uDNS.c for creating and destroying these special mDNSInterface_LocalOnly records.
+// We may want to turn the common code into a subroutine.
+
+mDNSlocal void RegisterLocalOnlyDomainEnumPTR(mDNS *m, const domainname *d, int type)
+	{
+	// allocate/register legacy and non-legacy _browse PTR record
+	mStatus err;
+	ARListElem *ptr = mDNSPlatformMemAllocate(sizeof(*ptr));
+
+	debugf("Incrementing %s refcount for %##s",
+		(type == mDNS_DomainTypeBrowse         ) ? "browse domain   " :
+		(type == mDNS_DomainTypeRegistration   ) ? "registration dom" :
+		(type == mDNS_DomainTypeBrowseAutomatic) ? "automatic browse" : "?", d->c);
+
+	mDNS_SetupResourceRecord(&ptr->ar, mDNSNULL, mDNSInterface_LocalOnly, kDNSType_PTR, 7200, kDNSRecordTypeShared, AuthRecordLocalOnly, FreeARElemCallback, ptr);
+	MakeDomainNameFromDNSNameString(&ptr->ar.namestorage, mDNS_DomainTypeNames[type]);
+	AppendDNSNameString            (&ptr->ar.namestorage, "local");
+	AssignDomainName(&ptr->ar.resrec.rdata->u.name, d);
+	err = mDNS_Register(m, &ptr->ar);
+	if (err)
+		{
+		LogMsg("SetSCPrefsBrowseDomain: mDNS_Register returned error %d", err);
+		mDNSPlatformMemFree(ptr);
+		}
+	else
+		{
+		ptr->next = LocalDomainEnumRecords;
+		LocalDomainEnumRecords = ptr;
+		}
+	}
+
+mDNSlocal void DeregisterLocalOnlyDomainEnumPTR(mDNS *m, const domainname *d, int type)
+	{
+	ARListElem **ptr = &LocalDomainEnumRecords;
+	domainname lhs; // left-hand side of PTR, for comparison
+
+	debugf("Decrementing %s refcount for %##s",
+		(type == mDNS_DomainTypeBrowse         ) ? "browse domain   " :
+		(type == mDNS_DomainTypeRegistration   ) ? "registration dom" :
+		(type == mDNS_DomainTypeBrowseAutomatic) ? "automatic browse" : "?", d->c);
+
+	MakeDomainNameFromDNSNameString(&lhs, mDNS_DomainTypeNames[type]);
+	AppendDNSNameString            (&lhs, "local");
+
+	while (*ptr)
+		{
+		if (SameDomainName(&(*ptr)->ar.resrec.rdata->u.name, d) && SameDomainName((*ptr)->ar.resrec.name, &lhs))
+			{
+			ARListElem *rem = *ptr;
+			*ptr = (*ptr)->next;
+			mDNS_Deregister(m, &rem->ar);
+			return;
+			}
+		else ptr = &(*ptr)->next;
+		}
+	}
+
+mDNSlocal void AddAutoBrowseDomain(const mDNSu32 uid, const domainname *const name)
+	{
+	DNameListElem *new = mDNSPlatformMemAllocate(sizeof(DNameListElem));
+	if (!new) { LogMsg("ERROR: malloc"); return; }
+	AssignDomainName(&new->name, name);
+	new->uid = uid;
+	new->next = AutoBrowseDomains;
+	AutoBrowseDomains = new;
+	udsserver_automatic_browse_domain_changed(new, mDNStrue);
+	}
+
+mDNSlocal void RmvAutoBrowseDomain(const mDNSu32 uid, const domainname *const name)
+	{
+	DNameListElem **p = &AutoBrowseDomains;
+	while (*p && (!SameDomainName(&(*p)->name, name) || (*p)->uid != uid)) p = &(*p)->next;
+	if (!*p) LogMsg("RmvAutoBrowseDomain: Got remove event for domain %##s not in list", name->c);
+	else
+		{
+		DNameListElem *ptr = *p;
+		*p = ptr->next;
+		udsserver_automatic_browse_domain_changed(ptr, mDNSfalse);
+		mDNSPlatformMemFree(ptr);
+		}
+	}
+
+mDNSlocal void SetPrefsBrowseDomains(mDNS *m, DNameListElem *browseDomains, mDNSBool add)
+	{
+	DNameListElem *d;
+	for (d = browseDomains; d; d = d->next)
+		{
+		if (add)
+			{
+			RegisterLocalOnlyDomainEnumPTR(m, &d->name, mDNS_DomainTypeBrowse);
+			AddAutoBrowseDomain(d->uid, &d->name);
+			}
+		else
+			{
+			DeregisterLocalOnlyDomainEnumPTR(m, &d->name, mDNS_DomainTypeBrowse);
+			RmvAutoBrowseDomain(d->uid, &d->name);
+			}
+		}
+	}
+
+mDNSlocal void UpdateDeviceInfoRecord(mDNS *const m)
+	{
+	int num_autoname = 0;
+	request_state *req;
+	for (req = all_requests; req; req = req->next)
+		if (req->terminate == regservice_termination_callback && req->u.servicereg.autoname)
+			num_autoname++;
+
+	// If DeviceInfo record is currently registered, see if we need to deregister it
+	if (m->DeviceInfo.resrec.RecordType != kDNSRecordTypeUnregistered)
+		if (num_autoname == 0 || !SameDomainLabelCS(m->DeviceInfo.resrec.name->c, m->nicelabel.c))
+			{
+			LogOperation("UpdateDeviceInfoRecord Deregister %##s", m->DeviceInfo.resrec.name);
+			mDNS_Deregister(m, &m->DeviceInfo);
+			}
+
+	// If DeviceInfo record is not currently registered, see if we need to register it
+	if (m->DeviceInfo.resrec.RecordType == kDNSRecordTypeUnregistered)
+		if (num_autoname > 0)
+			{
+			mDNSu8 len = m->HIHardware.c[0] < 255 - 6 ? m->HIHardware.c[0] : 255 - 6;
+			mDNS_SetupResourceRecord(&m->DeviceInfo, mDNSNULL, mDNSNULL, kDNSType_TXT, kStandardTTL, kDNSRecordTypeAdvisory, AuthRecordAny, mDNSNULL, mDNSNULL);
+			ConstructServiceName(&m->DeviceInfo.namestorage, &m->nicelabel, &DeviceInfoName, &localdomain);
+			mDNSPlatformMemCopy(m->DeviceInfo.resrec.rdata->u.data + 1, "model=", 6);
+			mDNSPlatformMemCopy(m->DeviceInfo.resrec.rdata->u.data + 7, m->HIHardware.c + 1, len);
+			m->DeviceInfo.resrec.rdata->u.data[0] = 6 + len;	// "model=" plus the device string
+			m->DeviceInfo.resrec.rdlength         = 7 + len;	// One extra for the length byte at the start of the string
+			LogOperation("UpdateDeviceInfoRecord   Register %##s", m->DeviceInfo.resrec.name);
+			mDNS_Register(m, &m->DeviceInfo);
+			}
+	}
+
+mDNSexport void udsserver_handle_configchange(mDNS *const m)
+	{
+	request_state *req;
+	service_instance *ptr;
+	DNameListElem *RegDomains = NULL;
+	DNameListElem *BrowseDomains = NULL;
+	DNameListElem *p;
+
+	UpdateDeviceInfoRecord(m);
+
+	// For autoname services, see if the default service name has changed, necessitating an automatic update
+	for (req = all_requests; req; req = req->next)
+		if (req->terminate == regservice_termination_callback)
+			if (req->u.servicereg.autoname && !SameDomainLabelCS(req->u.servicereg.name.c, m->nicelabel.c))
+				{
+				req->u.servicereg.name = m->nicelabel;
+				for (ptr = req->u.servicereg.instances; ptr; ptr = ptr->next)
+					{
+					ptr->renameonmemfree = 1;
+					if (ptr->clientnotified) SendServiceRemovalNotification(&ptr->srs);
+					LogInfo("udsserver_handle_configchange: Calling deregister for Service %##s", ptr->srs.RR_PTR.resrec.name->c);
+					if (mDNS_DeregisterService_drt(m, &ptr->srs, mDNS_Dereg_rapid))
+						regservice_callback(m, &ptr->srs, mStatus_MemFree);	// If service deregistered already, we can re-register immediately
+					}
+				}
+
+	// Let the platform layer get the current DNS information
+	mDNS_Lock(m);
+	mDNSPlatformSetDNSConfig(m, mDNSfalse, mDNSfalse, mDNSNULL, &RegDomains, &BrowseDomains);
+	mDNS_Unlock(m);
+
+	// Any automatic registration domains are also implicitly automatic browsing domains
+	if (RegDomains) SetPrefsBrowseDomains(m, RegDomains, mDNStrue);								// Add the new list first
+	if (AutoRegistrationDomains) SetPrefsBrowseDomains(m, AutoRegistrationDomains, mDNSfalse);	// Then clear the old list
+
+	// Add any new domains not already in our AutoRegistrationDomains list
+	for (p=RegDomains; p; p=p->next)
+		{
+		DNameListElem **pp = &AutoRegistrationDomains;
+		while (*pp && ((*pp)->uid != p->uid || !SameDomainName(&(*pp)->name, &p->name))) pp = &(*pp)->next;
+		if (!*pp)		// If not found in our existing list, this is a new default registration domain
+			{
+			RegisterLocalOnlyDomainEnumPTR(m, &p->name, mDNS_DomainTypeRegistration);
+			udsserver_default_reg_domain_changed(p, mDNStrue);
+			}
+		else			// else found same domainname in both old and new lists, so no change, just delete old copy
+			{
+			DNameListElem *del = *pp;
+			*pp = (*pp)->next;
+			mDNSPlatformMemFree(del);
+			}
+		}
+
+	// Delete any domains in our old AutoRegistrationDomains list that are now gone
+	while (AutoRegistrationDomains)
+		{
+		DNameListElem *del = AutoRegistrationDomains;
+		AutoRegistrationDomains = AutoRegistrationDomains->next;		// Cut record from list FIRST,
+		DeregisterLocalOnlyDomainEnumPTR(m, &del->name, mDNS_DomainTypeRegistration);
+		udsserver_default_reg_domain_changed(del, mDNSfalse);			// before calling udsserver_default_reg_domain_changed()
+		mDNSPlatformMemFree(del);
+		}
+
+	// Now we have our new updated automatic registration domain list
+	AutoRegistrationDomains = RegDomains;
+
+	// Add new browse domains to internal list
+	if (BrowseDomains) SetPrefsBrowseDomains(m, BrowseDomains, mDNStrue);
+
+	// Remove old browse domains from internal list
+	if (SCPrefBrowseDomains)
+		{
+		SetPrefsBrowseDomains(m, SCPrefBrowseDomains, mDNSfalse);
+		while (SCPrefBrowseDomains)
+			{
+			DNameListElem *fptr = SCPrefBrowseDomains;
+			SCPrefBrowseDomains = SCPrefBrowseDomains->next;
+			mDNSPlatformMemFree(fptr);
+			}
+		}
+
+	// Replace the old browse domains array with the new array
+	SCPrefBrowseDomains = BrowseDomains;
+	}
+
+mDNSlocal void AutomaticBrowseDomainChange(mDNS *const m, DNSQuestion *q, const ResourceRecord *const answer, QC_result AddRecord)
+	{
+	(void)m; // unused;
+	(void)q; // unused
+
+	LogOperation("AutomaticBrowseDomainChange: %s automatic browse domain %##s",
+		AddRecord ? "Adding" : "Removing", answer->rdata->u.name.c);
+
+	if (AddRecord) AddAutoBrowseDomain(0, &answer->rdata->u.name);
+	else           RmvAutoBrowseDomain(0, &answer->rdata->u.name);
+	}
+
+mDNSlocal mStatus handle_sethost_request(request_state *request)
+{
+	get_flags(&request->msgptr, request->msgend);
+	char hostName[MAX_DOMAIN_LABEL];
+	int len = 0;
+	if (get_string(&request->msgptr, request->msgend, hostName,
+		MAX_DOMAIN_LABEL) < 0) return (mStatus_BadParamErr);
+	LogOperation("%3d: DNSSetHostname(%X, %d, nonstr ) START",
+		request->sd, request->flags);
+	// if we start using this as a callback for notification when the
+	// hostname changes we may need to cleanup from it
+	//  request->terminate = sethost_termination_callback;
+	if(hostName[0] == 0) return mStatus_BadParamErr;
+		while (len < MAX_DOMAIN_LABEL && hostName[len+1]
+			&& hostName[len+1] != '.') len++;
+	strncpy(&(mDNSStorage.nicelabel.c[1]), hostName, len);
+	mDNSStorage.nicelabel.c[0] = len;
+	strncpy(&(mDNSStorage.hostlabel.c[1]), hostName, len);
+	mDNSStorage.hostlabel.c[0] = len;
+	mDNS_SetFQDN(&mDNSStorage);
+	return mStatus_NoError;
+}
+
+mDNSlocal mStatus handle_browse_request(request_state *request)
+	{
+	char regtype[MAX_ESCAPED_DOMAIN_NAME], domain[MAX_ESCAPED_DOMAIN_NAME];
+	domainname typedn, d, temp;
+	mDNSs32 NumSubTypes;
+	mStatus err = mStatus_NoError;
+
+	DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend);
+	mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend);
+	mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex);
+	if (interfaceIndex && !InterfaceID) return(mStatus_BadParamErr);
+
+	if (get_string(&request->msgptr, request->msgend, regtype, MAX_ESCAPED_DOMAIN_NAME) < 0 ||
+		get_string(&request->msgptr, request->msgend, domain, MAX_ESCAPED_DOMAIN_NAME) < 0) return(mStatus_BadParamErr);
+
+	if (!request->msgptr) { LogMsg("%3d: DNSServiceBrowse(unreadable parameters)", request->sd); return(mStatus_BadParamErr); }
+
+	if (domain[0] == '\0') uDNS_SetupSearchDomains(&mDNSStorage, UDNS_START_WAB_QUERY);
+
+	request->flags = flags;
+	typedn.c[0] = 0;
+	NumSubTypes = ChopSubTypes(regtype);	// Note: Modifies regtype string to remove trailing subtypes
+	if (NumSubTypes < 0 || NumSubTypes > 1) return(mStatus_BadParamErr);
+	if (NumSubTypes == 1 && !AppendDNSNameString(&typedn, regtype + strlen(regtype) + 1)) return(mStatus_BadParamErr);
+
+	if (!regtype[0] || !AppendDNSNameString(&typedn, regtype)) return(mStatus_BadParamErr);
+
+	if (!MakeDomainNameFromDNSNameString(&temp, regtype)) return(mStatus_BadParamErr);
+	// For over-long service types, we only allow domain "local"
+	if (temp.c[0] > 15 && domain[0] == 0) mDNSPlatformStrLCopy(domain, "local.", sizeof(domain));
+
+	// Set up browser info
+	request->u.browser.ForceMCast = (flags & kDNSServiceFlagsForceMulticast) != 0;
+	request->u.browser.interface_id = InterfaceID;
+	AssignDomainName(&request->u.browser.regtype, &typedn);
+	request->u.browser.default_domain = !domain[0];
+	request->u.browser.browsers = NULL;
+
+	LogOperation("%3d: DNSServiceBrowse(%X, %d, \"%##s\", \"%s\") START", 
+			request->sd, request->flags, interfaceIndex, request->u.browser.regtype.c, domain);
+
+	// We need to unconditionally set request->terminate, because even if we didn't successfully
+	// start any browses right now, subsequent configuration changes may cause successful
+	// browses to be added, and we'll need to cancel them before freeing this memory.
+	request->terminate = browse_termination_callback;
+
+	if (domain[0])
+		{
+		if (!MakeDomainNameFromDNSNameString(&d, domain)) return(mStatus_BadParamErr);
+		err = add_domain_to_browser(request, &d);
+#if 0
+		err = AuthorizedDomain(request, &d, AutoBrowseDomains) ? add_domain_to_browser(request, &d) : mStatus_NoError;
+#endif
+		}
+	else
+		{
+		DNameListElem *sdom;
+		for (sdom = AutoBrowseDomains; sdom; sdom = sdom->next)
+			if (!sdom->uid || SystemUID(request->uid) || request->uid == sdom->uid)
+				{
+				err = add_domain_to_browser(request, &sdom->name);
+				if (err)
+					{
+					if (SameDomainName(&sdom->name, &localdomain)) break;
+					else err = mStatus_NoError;  // suppress errors for non-local "default" domains
+					}
+				}
+		}
+
+	return(err);
+	}
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - DNSServiceResolve
+#endif
+
+mDNSlocal void resolve_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
+	{
+	size_t len = 0;
+	char fullname[MAX_ESCAPED_DOMAIN_NAME], target[MAX_ESCAPED_DOMAIN_NAME];
+	char *data;
+	reply_state *rep;
+	request_state *req = question->QuestionContext;
+	(void)m; // Unused
+
+	LogOperation("%3d: DNSServiceResolve(%##s) %s %s", req->sd, question->qname.c, AddRecord ? "ADD" : "RMV", RRDisplayString(m, answer));
+
+	if (!AddRecord)
+		{
+		if (req->u.resolve.srv == answer) req->u.resolve.srv = mDNSNULL;
+		if (req->u.resolve.txt == answer) req->u.resolve.txt = mDNSNULL;
+		return;
+		}
+
+	if (answer->rrtype == kDNSType_SRV) req->u.resolve.srv = answer;
+	if (answer->rrtype == kDNSType_TXT) req->u.resolve.txt = answer;
+
+	if (!req->u.resolve.txt || !req->u.resolve.srv) return;		// only deliver result to client if we have both answers
+
+	ConvertDomainNameToCString(answer->name, fullname);
+	ConvertDomainNameToCString(&req->u.resolve.srv->rdata->u.srv.target, target);
+
+	// calculate reply length
+	len += sizeof(DNSServiceFlags);
+	len += sizeof(mDNSu32);  // interface index
+	len += sizeof(DNSServiceErrorType);
+	len += strlen(fullname) + 1;
+	len += strlen(target) + 1;
+	len += 2 * sizeof(mDNSu16);  // port, txtLen
+	len += req->u.resolve.txt->rdlength;
+
+	// allocate/init reply header
+	rep = create_reply(resolve_reply_op, len, req);
+	rep->rhdr->flags = dnssd_htonl(0);
+	rep->rhdr->ifi   = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(m, answer->InterfaceID, mDNSfalse));
+	rep->rhdr->error = dnssd_htonl(kDNSServiceErr_NoError);
+
+	data = (char *)&rep->rhdr[1];
+
+	// write reply data to message
+	put_string(fullname, &data);
+	put_string(target, &data);
+	*data++ =  req->u.resolve.srv->rdata->u.srv.port.b[0];
+	*data++ =  req->u.resolve.srv->rdata->u.srv.port.b[1];
+	put_uint16(req->u.resolve.txt->rdlength, &data);
+	put_rdata (req->u.resolve.txt->rdlength, req->u.resolve.txt->rdata->u.data, &data);
+
+	LogOperation("%3d: DNSServiceResolve(%s) RESULT   %s:%d", req->sd, fullname, target, mDNSVal16(req->u.resolve.srv->rdata->u.srv.port));
+	append_reply(req, rep);
+	}
+
+mDNSlocal void resolve_termination_callback(request_state *request)
+	{
+	LogOperation("%3d: DNSServiceResolve(%##s) STOP", request->sd, request->u.resolve.qtxt.qname.c);
+	mDNS_StopQuery(&mDNSStorage, &request->u.resolve.qtxt);
+	mDNS_StopQuery(&mDNSStorage, &request->u.resolve.qsrv);
+	if (request->u.resolve.external_advertise) external_stop_resolving_service(&request->u.resolve.qsrv.qname);
+	}
+
+mDNSlocal mStatus handle_resolve_request(request_state *request)
+	{
+	char name[256], regtype[MAX_ESCAPED_DOMAIN_NAME], domain[MAX_ESCAPED_DOMAIN_NAME];
+	domainname fqdn;
+	mStatus err;
+
+	// extract the data from the message
+	DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend);
+	mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend);
+	mDNSInterfaceID InterfaceID;
+	mDNSBool wasP2P = (interfaceIndex == kDNSServiceInterfaceIndexP2P);
+	
+	
+	request->flags = flags;
+	if (wasP2P) interfaceIndex = kDNSServiceInterfaceIndexAny;
+	
+	InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex);
+	if (interfaceIndex && !InterfaceID)
+		{ LogMsg("ERROR: handle_resolve_request bad interfaceIndex %d", interfaceIndex); return(mStatus_BadParamErr); }
+
+	if (get_string(&request->msgptr, request->msgend, name, 256) < 0 ||
+		get_string(&request->msgptr, request->msgend, regtype, MAX_ESCAPED_DOMAIN_NAME) < 0 ||
+		get_string(&request->msgptr, request->msgend, domain, MAX_ESCAPED_DOMAIN_NAME) < 0)
+		{ LogMsg("ERROR: handle_resolve_request - Couldn't read name/regtype/domain"); return(mStatus_BadParamErr); }
+
+	if (!request->msgptr) { LogMsg("%3d: DNSServiceResolve(unreadable parameters)", request->sd); return(mStatus_BadParamErr); }
+
+	if (build_domainname_from_strings(&fqdn, name, regtype, domain) < 0)
+		{ LogMsg("ERROR: handle_resolve_request bad “%s” “%s” “%s”", name, regtype, domain); return(mStatus_BadParamErr); }
+
+	mDNSPlatformMemZero(&request->u.resolve, sizeof(request->u.resolve));
+
+	// format questions
+	request->u.resolve.qsrv.InterfaceID      = InterfaceID;
+	request->u.resolve.qsrv.Target           = zeroAddr;
+	AssignDomainName(&request->u.resolve.qsrv.qname, &fqdn);
+	request->u.resolve.qsrv.qtype            = kDNSType_SRV;
+	request->u.resolve.qsrv.qclass           = kDNSClass_IN;
+	request->u.resolve.qsrv.LongLived        = (flags & kDNSServiceFlagsLongLivedQuery     ) != 0;
+	request->u.resolve.qsrv.ExpectUnique     = mDNStrue;
+	request->u.resolve.qsrv.ForceMCast       = (flags & kDNSServiceFlagsForceMulticast     ) != 0;
+	request->u.resolve.qsrv.ReturnIntermed   = (flags & kDNSServiceFlagsReturnIntermediates) != 0;
+	request->u.resolve.qsrv.SuppressUnusable = mDNSfalse;
+	request->u.resolve.qsrv.SearchListIndex  = 0;
+	request->u.resolve.qsrv.AppendSearchDomains = 0;
+	request->u.resolve.qsrv.RetryWithSearchDomains = mDNSfalse;
+	request->u.resolve.qsrv.TimeoutQuestion  = 0;
+	request->u.resolve.qsrv.WakeOnResolve    = (flags & kDNSServiceFlagsWakeOnResolve) != 0;
+	request->u.resolve.qsrv.qnameOrig        = mDNSNULL;
+	request->u.resolve.qsrv.QuestionCallback = resolve_result_callback;
+	request->u.resolve.qsrv.QuestionContext  = request;
+
+	request->u.resolve.qtxt.InterfaceID      = InterfaceID;
+	request->u.resolve.qtxt.Target           = zeroAddr;
+	AssignDomainName(&request->u.resolve.qtxt.qname, &fqdn);
+	request->u.resolve.qtxt.qtype            = kDNSType_TXT;
+	request->u.resolve.qtxt.qclass           = kDNSClass_IN;
+	request->u.resolve.qtxt.LongLived        = (flags & kDNSServiceFlagsLongLivedQuery     ) != 0;
+	request->u.resolve.qtxt.ExpectUnique     = mDNStrue;
+	request->u.resolve.qtxt.ForceMCast       = (flags & kDNSServiceFlagsForceMulticast     ) != 0;
+	request->u.resolve.qtxt.ReturnIntermed   = (flags & kDNSServiceFlagsReturnIntermediates) != 0;
+	request->u.resolve.qtxt.SuppressUnusable = mDNSfalse;
+	request->u.resolve.qtxt.SearchListIndex  = 0;
+	request->u.resolve.qtxt.AppendSearchDomains = 0;
+	request->u.resolve.qtxt.RetryWithSearchDomains = mDNSfalse;
+	request->u.resolve.qtxt.TimeoutQuestion  = 0;
+	request->u.resolve.qtxt.WakeOnResolve    = 0;
+	request->u.resolve.qtxt.qnameOrig        = mDNSNULL;
+	request->u.resolve.qtxt.QuestionCallback = resolve_result_callback;
+	request->u.resolve.qtxt.QuestionContext  = request;
+
+	request->u.resolve.ReportTime            = NonZeroTime(mDNS_TimeNow(&mDNSStorage) + 130 * mDNSPlatformOneSecond);
+
+	request->u.resolve.external_advertise    = mDNSfalse;
+
+#if 0
+	if (!AuthorizedDomain(request, &fqdn, AutoBrowseDomains))	return(mStatus_NoError);
+#endif
+
+	// ask the questions
+	LogOperation("%3d: DNSServiceResolve(%##s) START", request->sd, request->u.resolve.qsrv.qname.c);
+	err = mDNS_StartQuery(&mDNSStorage, &request->u.resolve.qsrv);
+	if (!err)
+		{
+		err = mDNS_StartQuery(&mDNSStorage, &request->u.resolve.qtxt);
+		if (err) mDNS_StopQuery(&mDNSStorage, &request->u.resolve.qsrv);
+		else
+			{
+			request->terminate = resolve_termination_callback;
+			// If the user explicitly passed in P2P, we don't restrict the domain in which we resolve.
+			if (wasP2P || (!InterfaceID && IsLocalDomain(&fqdn) && (request->flags & kDNSServiceFlagsIncludeP2P)))
+				{ 
+				request->u.resolve.external_advertise    = mDNStrue;
+				LogInfo("handle_resolve_request: calling external_start_resolving_service()");
+				external_start_resolving_service(&fqdn);
+				}
+			}
+		}
+
+	return(err);
+	}
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - DNSServiceQueryRecord
+#endif
+
+// mDNS operation functions. Each operation has 3 associated functions - a request handler that parses
+// the client's request and makes the appropriate mDNSCore call, a result handler (passed as a callback
+// to the mDNSCore routine) that sends results back to the client, and a termination routine that aborts
+// the mDNSCore operation if the client dies or closes its socket.
+
+// Returns -1 to tell the caller that it should not try to reissue the query anymore 
+// Returns 1 on successfully appending a search domain and the caller should reissue the new query
+// Returns 0 when there are no more search domains and the caller should reissue the query
+mDNSlocal int AppendNewSearchDomain(mDNS *const m, DNSQuestion *question)
+	{
+	domainname *sd;
+	mStatus err;
+
+	// Sanity check: The caller already checks this. We use -1 to indicate that we have searched all
+	// the domains and should try the single label query directly on the wire. 
+	if (question->SearchListIndex == -1)
+		{
+		LogMsg("AppendNewSearchDomain: question %##s (%s) SearchListIndex is -1", question->qname.c, DNSTypeName(question->qtype));
+		return -1;
+		}
+
+	if (!question->AppendSearchDomains)
+		{
+		LogMsg("AppendNewSearchDomain: question %##s (%s) AppendSearchDoamins is 0", question->qname.c, DNSTypeName(question->qtype));
+		return -1;
+		}
+
+	// Save the original name, before we modify them below.
+	if (!question->qnameOrig)
+		{
+		question->qnameOrig =  mallocL("AppendNewSearchDomain", sizeof(domainname));
+		if (!question->qnameOrig) { LogMsg("AppendNewSearchDomain: ERROR!!  malloc failure"); return -1; }
+		question->qnameOrig->c[0] = 0;
+		AssignDomainName(question->qnameOrig, &question->qname);
+		LogInfo("AppendSearchDomain: qnameOrig %##s", question->qnameOrig->c);
+		}
+
+	sd = uDNS_GetNextSearchDomain(m, question->InterfaceID, &question->SearchListIndex, !question->AppendLocalSearchDomains);
+	// We use -1 to indicate that we have searched all the domains and should try the single label
+	// query directly on the wire. uDNS_GetNextSearchDomain should never return a negative value
+	if (question->SearchListIndex == -1)
+		{
+		LogMsg("AppendNewSearchDomain: ERROR!! uDNS_GetNextSearchDomain returned -1");
+		return -1;
+		}
+
+	// Not a common case. Perhaps, we should try the next search domain if it exceeds ?
+	if (sd && (DomainNameLength(question->qnameOrig) + DomainNameLength(sd)) > MAX_DOMAIN_NAME)
+		{
+		LogMsg("AppendNewSearchDomain: ERROR!! exceeding max domain length for %##s (%s) SearchDomain %##s length %d, Question name length %d", question->qnameOrig->c, DNSTypeName(question->qtype), sd->c, DomainNameLength(question->qnameOrig), DomainNameLength(sd));
+		return -1;
+		}
+
+	// if there are no more search domains and we have already tried this question
+	// without appending search domains, then we are done.
+	if (!sd && !ApplySearchDomainsFirst(question))
+		{
+		LogInfo("AppnedNewSearchDomain: No more search domains for question with name %##s (%s), not trying anymore", question->qname.c, DNSTypeName(question->qtype));
+		return -1;
+		}
+
+	// Stop the question before changing the name as negative cache entries could be pointing at this question.
+	// Even if we don't change the question in the case of returning 0, the caller is going to restart the
+	// question.
+	err = mDNS_StopQuery(&mDNSStorage, question);
+	if (err) { LogMsg("AppendNewSearchDomain: ERROR!! %##s %s mDNS_StopQuery: %d, while retrying with search domains", question->qname.c, DNSTypeName(question->qtype), (int)err); }
+
+	AssignDomainName(&question->qname, question->qnameOrig);
+	if (sd)
+		{
+		AppendDomainName(&question->qname, sd);
+		LogInfo("AppnedNewSearchDomain: Returning question with name %##s, SearchListIndex %d", question->qname.c, question->SearchListIndex);
+		return 1;
+		}
+
+	// Try the question as single label
+	LogInfo("AppnedNewSearchDomain: No more search domains for question with name %##s (%s), trying one last time", question->qname.c, DNSTypeName(question->qtype));
+	return 0;
+	}
+
+#if APPLE_OSX_mDNSResponder
+
+mDNSlocal mDNSBool DomainInSearchList(domainname *domain)
+	{
+	const SearchListElem *s;
+ 	for (s=SearchList; s; s=s->next)
+		if (SameDomainName(&s->domain, domain)) return mDNStrue;
+	return mDNSfalse;
+	}
+
+// Workaround for networks using Microsoft Active Directory using "local" as a private internal
+// top-level domain
+mDNSlocal mStatus SendAdditionalQuery(DNSQuestion *q, request_state *request, mStatus err)
+	{
+	extern domainname ActiveDirectoryPrimaryDomain;
+	DNSQuestion **question2;
+	#define VALID_MSAD_SRV_TRANSPORT(T) (SameDomainLabel((T)->c, (const mDNSu8 *)"\x4_tcp") || SameDomainLabel((T)->c, (const mDNSu8 *)"\x4_udp"))
+	#define VALID_MSAD_SRV(Q) ((Q)->qtype == kDNSType_SRV && VALID_MSAD_SRV_TRANSPORT(SecondLabel(&(Q)->qname)))
+
+	question2 = mDNSNULL;
+	if (request->hdr.op == query_request)
+		question2 = &request->u.queryrecord.q2;
+	else if (request->hdr.op == addrinfo_request)
+		{
+		if (q->qtype == kDNSType_A)
+			question2 = &request->u.addrinfo.q42;
+		else if (q->qtype == kDNSType_AAAA)
+			question2 = &request->u.addrinfo.q62;
+		}
+	if (!question2)
+		{
+		LogMsg("SendAdditionalQuery: question2 NULL for %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+		return mStatus_BadParamErr;
+		}
+
+	// Sanity check: If we already sent an additonal query, we don't need to send one more.
+	//
+	// 1. When the application calls DNSServiceQueryRecord or DNSServiceGetAddrInfo with a .local name, this function
+	// is called to see whether a unicast query should be sent or not.
+	//
+	// 2. As a result of appending search domains, the question may be end up with a .local suffix even though it
+	// was not a .local name to start with. In that case, queryrecord_result_callback calls this function to
+	// send the additional query.
+	//
+	// Thus, it should not be called more than once.
+	if (*question2)
+		{
+		LogInfo("SendAdditionalQuery: question2 already sent for %##s (%s), no more q2", q->qname.c, DNSTypeName(q->qtype));
+		return err;
+		}
+
+	if (!q->ForceMCast && SameDomainLabel(LastLabel(&q->qname), (const mDNSu8 *)&localdomain))
+		if (q->qtype == kDNSType_A || q->qtype == kDNSType_AAAA || VALID_MSAD_SRV(q))
+			{
+			DNSQuestion *q2;
+			int labels = CountLabels(&q->qname);
+			q2 = mallocL("DNSQuestion", sizeof(DNSQuestion));
+			if (!q2) FatalError("ERROR: SendAdditionalQuery malloc");
+			*question2        = q2;
+			*q2               = *q;
+			q2->InterfaceID   = mDNSInterface_Unicast;
+			q2->ExpectUnique  = mDNStrue;
+			// If the query starts as a single label e.g., somehost, and we have search domains with .local,
+			// queryrecord_result_callback calls this function when .local is appended to "somehost".
+			// At that time, the name in "q" is pointing at somehost.local and its qnameOrig pointing at
+			// "somehost". We need to copy that information so that when we retry with a different search
+			// domain e.g., mycompany.local, we get "somehost.mycompany.local".
+			if (q->qnameOrig)
+				{
+				(*question2)->qnameOrig =  mallocL("SendAdditionalQuery", DomainNameLength(q->qnameOrig));
+				if (!(*question2)->qnameOrig) { LogMsg("SendAdditionalQuery: ERROR!!  malloc failure"); return mStatus_NoMemoryErr; }
+				(*question2)->qnameOrig->c[0] = 0;
+				AssignDomainName((*question2)->qnameOrig, q->qnameOrig);
+				LogInfo("SendAdditionalQuery: qnameOrig %##s", (*question2)->qnameOrig->c);
+				}
+			// For names of the form "<one-or-more-labels>.bar.local." we always do a second unicast query in parallel.
+			// For names of the form "<one-label>.local." it's less clear whether we should do a unicast query.
+			// If the name being queried is exactly the same as the name in the DHCP "domain" option (e.g. the DHCP
+			// "domain" is my-small-company.local, and the user types "my-small-company.local" into their web browser)
+			// then that's a hint that it's worth doing a unicast query. Otherwise, we first check to see if the
+			// site's DNS server claims there's an SOA record for "local", and if so, that's also a hint that queries
+			// for names in the "local" domain will be safely answered privately before they hit the root name servers.
+			// Note that in the "my-small-company.local" example above there will typically be an SOA record for
+			// "my-small-company.local" but *not* for "local", which is why the "local SOA" check would fail in that case.
+			// We need to check against both ActiveDirectoryPrimaryDomain and SearchList. If it matches against either
+			// of those, we don't want do the SOA check for the local
+			if (labels == 2 && !SameDomainName(&q->qname, &ActiveDirectoryPrimaryDomain) && !DomainInSearchList(&q->qname))
+				{
+				AssignDomainName(&q2->qname, &localdomain);
+				q2->qtype          = kDNSType_SOA;
+				q2->LongLived      = mDNSfalse;
+				q2->ForceMCast     = mDNSfalse;
+				q2->ReturnIntermed = mDNStrue;
+				// Don't append search domains for the .local SOA query
+				q2->AppendSearchDomains = 0;
+				q2->AppendLocalSearchDomains = 0;
+				q2->RetryWithSearchDomains = mDNSfalse;
+				q2->SearchListIndex = 0;
+				q2->TimeoutQuestion = 0;
+				}
+			LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) unicast", request->sd, q2->qname.c, DNSTypeName(q2->qtype));
+			err = mDNS_StartQuery(&mDNSStorage, q2);
+			if (err) LogMsg("%3d: ERROR: DNSServiceQueryRecord %##s %s mDNS_StartQuery: %d", request->sd, q2->qname.c, DNSTypeName(q2->qtype), (int)err);
+			}
+	return(err);
+	}
+#endif // APPLE_OSX_mDNSResponder
+
+// This function tries to append a search domain if valid and possible. If so, returns true.
+mDNSlocal mDNSBool RetryQuestionWithSearchDomains(mDNS *const m, DNSQuestion *question, request_state *req)
+	{
+	int result;
+	// RetryWithSearchDomains tells the core to call us back so that we can retry with search domains if there is no
+	// answer in the cache or /etc/hosts. In the first call back from the core, we clear RetryWithSearchDomains so
+	// that we don't get called back repeatedly. If we got an answer from the cache or /etc/hosts, we don't touch
+	// RetryWithSearchDomains which may or may not be set.
+	//
+	// If we get e.g., NXDOMAIN and the query is neither suppressed nor exhausted the domain search list and
+	// is a valid question for appending search domains, retry by appending domains
+
+	if (!question->SuppressQuery && question->SearchListIndex != -1 && question->AppendSearchDomains)
+		{
+		question->RetryWithSearchDomains = 0;
+		result = AppendNewSearchDomain(m, question);
+		// As long as the result is either zero or 1, we retry the question. If we exahaust the search
+		// domains (result is zero) we try the original query (as it was before appending the search
+		// domains) as such on the wire as a last resort if we have not tried them before. For queries
+		// with more than one label, we have already tried them before appending search domains and
+		// hence don't retry again
+		if (result != -1)
+			{
+			mStatus err;
+			err = mDNS_StartQuery(m, question);
+			if (!err)
+				{
+				LogOperation("%3d: RetryQuestionWithSearchDomains(%##s, %s), retrying after appending search domain", req->sd, question->qname.c, DNSTypeName(question->qtype));
+				// If the result was zero, it meant that there are no search domains and we just retried the question
+				// as a single label and we should not retry with search domains anymore.
+				if (!result) question->SearchListIndex = -1;
+				return mDNStrue;
+				}
+			else
+				{
+				LogMsg("%3d: ERROR: RetryQuestionWithSearchDomains %##s %s mDNS_StartQuery: %d, while retrying with search domains", req->sd, question->qname.c, DNSTypeName(question->qtype), (int)err);
+				// We have already stopped the query and could not restart. Reset the appropriate pointers
+				// so that we don't call stop again when the question terminates
+				question->QuestionContext = mDNSNULL;
+				}
+			}
+		}
+	else 
+		{
+		LogInfo("%3d: RetryQuestionWithSearchDomains: Not appending search domains - SuppressQuery %d, SearchListIndex %d, AppendSearchDomains %d", req->sd, question->SuppressQuery, question->SearchListIndex, question->AppendSearchDomains);
+		}
+	return mDNSfalse;
+	}
+
+mDNSlocal void queryrecord_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, QC_result AddRecord)
+	{
+	char name[MAX_ESCAPED_DOMAIN_NAME];
+	request_state *req = question->QuestionContext;
+	reply_state *rep;
+	char *data;
+	size_t len;
+	DNSServiceErrorType error = kDNSServiceErr_NoError;
+	DNSQuestion *q = mDNSNULL;
+
+#if APPLE_OSX_mDNSResponder
+	{
+	// Sanity check: QuestionContext is set to NULL after we stop the question and hence we should not
+	// get any callbacks from the core after this.
+	if (!req)
+		{
+		LogMsg("queryrecord_result_callback: ERROR!! QuestionContext NULL for %##s (%s)", question->qname.c, DNSTypeName(question->qtype));
+		return;
+		}
+	if (req->hdr.op == query_request && question == req->u.queryrecord.q2)
+		q = &req->u.queryrecord.q;
+	else if (req->hdr.op == addrinfo_request && question == req->u.addrinfo.q42)
+		q = &req->u.addrinfo.q4;
+	else if (req->hdr.op == addrinfo_request && question == req->u.addrinfo.q62)
+		q = &req->u.addrinfo.q6;
+	
+	if (q && question->qtype != q->qtype && !SameDomainName(&question->qname, &q->qname))
+		{
+		mStatus err;
+		domainname *orig = question->qnameOrig;
+
+		LogInfo("queryrecord_result_callback: Stopping q2 local %##s", question->qname.c);
+		mDNS_StopQuery(m, question);
+		question->QuestionContext = mDNSNULL;
+
+		// We got a negative response for the SOA record indicating that .local does not exist.
+		// But we might have other search domains (that does not end in .local) that can be
+		// appended to this question. In that case, we want to retry the question. Otherwise,
+		// we don't want to try this question as unicast.
+		if (answer->RecordType == kDNSRecordTypePacketNegative && !q->AppendSearchDomains)
+			{
+			LogInfo("queryrecord_result_callback: question %##s AppendSearchDomains zero", q->qname.c);
+			return;
+			}
+
+		// If we got a non-negative answer for our "local SOA" test query, start an additional parallel unicast query
+		//
+		// Note: When we copy the original question, we copy everything including the AppendSearchDomains,
+		// RetryWithSearchDomains except for qnameOrig which can be non-NULL if the original question is
+		// e.g., somehost and then we appended e.g., ".local" and retried that question. See comment in
+		// SendAdditionalQuery as to how qnameOrig gets initialized.
+		*question              = *q;
+		question->InterfaceID  = mDNSInterface_Unicast;
+		question->ExpectUnique = mDNStrue;
+		question->qnameOrig    = orig;
+
+		LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) unicast, context %p", req->sd, question->qname.c, DNSTypeName(question->qtype), question->QuestionContext);
+
+		// If the original question timed out, its QuestionContext would already be set to NULL and that's what we copied above.
+		// Hence, we need to set it explicitly here.
+		question->QuestionContext = req;
+		err = mDNS_StartQuery(m, question);
+		if (err) LogMsg("%3d: ERROR: queryrecord_result_callback %##s %s mDNS_StartQuery: %d", req->sd, question->qname.c, DNSTypeName(question->qtype), (int)err);
+
+		// If we got a positive response to local SOA, then try the .local question as unicast
+		if (answer->RecordType != kDNSRecordTypePacketNegative) return;
+
+		// Fall through and get the next search domain. The question is pointing at .local
+		// and we don't want to try that. Try the next search domain. Don't try with local
+		// search domains for the unicast question anymore.
+		//
+		// Note: we started the question above which will be stopped immediately (never sent on the wire)
+		// before we pick the next search domain below. RetryQuestionWithSearchDomains assumes that the
+		// question has already started.
+		question->AppendLocalSearchDomains = 0;
+		}
+
+	if (q && AddRecord && (question->InterfaceID == mDNSInterface_Unicast) && !answer->rdlength)
+		{
+		// If we get a negative response to the unicast query that we sent above, retry after appending search domains
+		// Note: We could have appended search domains below (where do it for regular unicast questions) instead of doing it here. 
+		// As we ignore negative unicast answers below, we would never reach the code where the search domains are appended.
+		// To keep things simple, we handle unicast ".local" separately here.
+		LogInfo("queryrecord_result_callback: Retrying .local question %##s (%s) as unicast after appending search domains", question->qname.c, DNSTypeName(question->qtype));
+		if (RetryQuestionWithSearchDomains(m, question, req))
+			return;
+		if (question->AppendSearchDomains && !question->AppendLocalSearchDomains && IsLocalDomain(&question->qname))
+			{
+			// If "local" is the last search domain, we need to stop the question so that we don't send the "local"
+			// question on the wire as we got a negative response for the local SOA. But, we can't stop the question
+			// yet as we may have to timeout the question (done by the "core") for which we need to leave the question
+			// in the list. We leave it disabled so that it does not hit the wire.
+			LogInfo("queryrecord_result_callback: Disabling .local question %##s (%s)", question->qname.c, DNSTypeName(question->qtype));
+			question->ThisQInterval = 0;
+			}
+		}
+	// If we are here it means that either "question" is not "q2" OR we got a positive response for "q2" OR we have no more search
+	// domains to append for "q2". In all cases, fall through and deliver the response
+	}
+#endif // APPLE_OSX_mDNSResponder
+
+	if (answer->RecordType == kDNSRecordTypePacketNegative)
+		{
+		// If this question needs to be timed out and we have reached the stop time, mark
+		// the error as timeout. It is possible that we might get a negative response from an
+		// external DNS server at the same time when this question reaches its stop time. We
+		// can't tell the difference as there is no indication in the callback. This should
+		// be okay as we will be timing out this query anyway.
+		mDNS_Lock(m);
+		if (question->TimeoutQuestion)
+			{
+			if ((m->timenow - question->StopTime) >= 0)
+				{
+				LogInfo("queryrecord_result_callback:Question %##s (%s) timing out, InterfaceID %p", question->qname.c, DNSTypeName(question->qtype), question->InterfaceID);
+				error = kDNSServiceErr_Timeout;
+				}
+			}
+		mDNS_Unlock(m);
+		// When we're doing parallel unicast and multicast queries for dot-local names (for supporting Microsoft
+		// Active Directory sites) we need to ignore negative unicast answers. Otherwise we'll generate negative
+		// answers for just about every single multicast name we ever look up, since the Microsoft Active Directory
+		// server is going to assert that pretty much every single multicast name doesn't exist.
+		//
+		// If we are timing out this query, we need to deliver the negative answer to the application
+		if (error != kDNSServiceErr_Timeout)
+			{
+			if (!answer->InterfaceID && IsLocalDomain(answer->name))
+				{
+				LogInfo("queryrecord_result_callback:Question %##s (%s) answering local with unicast", question->qname.c, DNSTypeName(question->qtype));
+				return;
+				}
+			error = kDNSServiceErr_NoSuchRecord;
+			}
+		AddRecord = mDNStrue;
+		}
+	// If we get a negative answer, try appending search domains. Don't append search domains
+	// - if we are timing out this question
+	// - if the negative response was received as a result of a multicast query
+	// - if this is an additional query (q2), we already appended search domains above (indicated by "!q" below)
+	if (error != kDNSServiceErr_Timeout)
+		{
+		if (!q && !answer->InterfaceID && !answer->rdlength && AddRecord)
+			{
+			// If the original question did not end in .local, we did not send an SOA query
+			// to figure out whether we should send an additional unicast query or not. If we just
+			// appended .local, we need to see if we need to send an additional query. This should
+			// normally happen just once because after we append .local, we ignore all negative
+			// responses for .local above.
+			LogInfo("queryrecord_result_callback: Retrying question %##s (%s) after appending search domains", question->qname.c, DNSTypeName(question->qtype));
+			if (RetryQuestionWithSearchDomains(m, question, req))
+				{
+				// Note: We need to call SendAdditionalQuery every time after appending a search domain as .local could
+				// be anywhere in the search domain list.
+#if APPLE_OSX_mDNSResponder
+				mStatus err = mStatus_NoError;
+				err = SendAdditionalQuery(question, req, err);
+				if (err) LogMsg("queryrecord_result_callback: Sending .local SOA query failed, after appending domains");
+#endif // APPLE_OSX_mDNSResponder
+				return;
+				}
+			}
+		}
+
+	ConvertDomainNameToCString(answer->name, name);
+
+	LogOperation("%3d: %s(%##s, %s) %s %s", req->sd,
+		req->hdr.op == query_request ? "DNSServiceQueryRecord" : "DNSServiceGetAddrInfo",
+		question->qname.c, DNSTypeName(question->qtype), AddRecord ? "ADD" : "RMV", RRDisplayString(m, answer));
+
+	len = sizeof(DNSServiceFlags);	// calculate reply data length
+	len += sizeof(mDNSu32);		// interface index
+	len += sizeof(DNSServiceErrorType);
+	len += strlen(name) + 1;
+	len += 3 * sizeof(mDNSu16);	// type, class, rdlen
+	len += answer->rdlength;
+	len += sizeof(mDNSu32);		// TTL
+
+	rep = create_reply(req->hdr.op == query_request ? query_reply_op : addrinfo_reply_op, len, req);
+
+	rep->rhdr->flags = dnssd_htonl(AddRecord ? kDNSServiceFlagsAdd : 0);
+	// Call mDNSPlatformInterfaceIndexfromInterfaceID, but suppressNetworkChange (last argument). Otherwise, if the
+	// InterfaceID is not valid, then it simulates a "NetworkChanged" which in turn makes questions
+	// to be stopped and started including  *this* one. Normally the InterfaceID is valid. But when we
+	// are using the /etc/hosts entries to answer a question, the InterfaceID may not be known to the
+	// mDNS core . Eventually, we should remove the calls to "NetworkChanged" in
+	// mDNSPlatformInterfaceIndexfromInterfaceID when it can't find InterfaceID as ResourceRecords
+	// should not have existed to answer this question if the corresponding interface is not valid.
+	rep->rhdr->ifi   = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(m, answer->InterfaceID, mDNStrue));
+	rep->rhdr->error = dnssd_htonl(error);
+
+	data = (char *)&rep->rhdr[1];
+
+	put_string(name,             &data);
+	put_uint16(answer->rrtype,   &data);
+	put_uint16(answer->rrclass,  &data);
+	put_uint16(answer->rdlength, &data);
+	// We need to use putRData here instead of the crude put_rdata function, because the crude put_rdata
+	// function just does a blind memory copy without regard to structures that may have holes in them.
+	if (answer->rdlength)
+		if (!putRData(mDNSNULL, (mDNSu8 *)data, (mDNSu8 *)rep->rhdr + len, answer))
+			LogMsg("queryrecord_result_callback putRData failed %d", (mDNSu8 *)rep->rhdr + len - (mDNSu8 *)data);
+	data += answer->rdlength;
+	put_uint32(AddRecord ? answer->rroriginalttl : 0, &data);
+
+	append_reply(req, rep);
+	// Stop the question, if we just timed out
+	if (error == kDNSServiceErr_Timeout)
+		{
+		mDNS_StopQuery(m, question);
+		// Reset the pointers so that we don't call stop on termination
+		question->QuestionContext = mDNSNULL;
+		}
+#if APPLE_OSX_mDNSResponder
+#if ! NO_WCF
+	CHECK_WCF_FUNCTION(WCFIsServerRunning)
+		{
+		struct xucred x;
+		socklen_t xucredlen = sizeof(x);
+	
+		if (WCFIsServerRunning((WCFConnection *)m->WCF) && answer->rdlength != 0)
+			{
+			if (getsockopt(req->sd, 0, LOCAL_PEERCRED, &x, &xucredlen) >= 0 &&
+				(x.cr_version == XUCRED_VERSION))
+				{
+				struct sockaddr_storage addr;
+				const RDataBody2 *const rdb = (RDataBody2 *)answer->rdata->u.data;
+				addr.ss_len = 0;
+				if (answer->rrtype == kDNSType_A || answer->rrtype == kDNSType_AAAA)
+					{
+					if (answer->rrtype == kDNSType_A)
+						{
+						struct sockaddr_in *sin = (struct sockaddr_in *)&addr;
+						sin->sin_port = 0;
+						if (!putRData(mDNSNULL, (mDNSu8 *)&sin->sin_addr, (mDNSu8 *)(&sin->sin_addr + sizeof(rdb->ipv4)), answer))
+							LogMsg("queryrecord_result_callback: WCF AF_INET putRData failed");
+						else
+							{
+							addr.ss_len = sizeof (struct sockaddr_in);
+							addr.ss_family = AF_INET;
+							}
+						}
+					else if (answer->rrtype == kDNSType_AAAA)
+						{
+						struct sockaddr_in6 *sin6 = (struct sockaddr_in6 *)&addr;
+						sin6->sin6_port = 0;
+						if (!putRData(mDNSNULL, (mDNSu8 *)&sin6->sin6_addr, (mDNSu8 *)(&sin6->sin6_addr + sizeof(rdb->ipv6)), answer))
+							LogMsg("queryrecord_result_callback: WCF AF_INET6 putRData failed");
+						else
+							{
+							addr.ss_len = sizeof (struct sockaddr_in6);
+							addr.ss_family = AF_INET6;
+							}
+						}
+					if (addr.ss_len)
+						{	
+						debugf("queryrecord_result_callback: Name %s, uid %u, addr length %d", name, x.cr_uid, addr.ss_len);
+						CHECK_WCF_FUNCTION((WCFConnection *)WCFNameResolvesToAddr)
+							{
+							WCFNameResolvesToAddr(m->WCF, name, (struct sockaddr *)&addr, x.cr_uid);
+							}
+						}
+					}
+				else if (answer->rrtype == kDNSType_CNAME)
+					{
+					domainname cname;
+					char cname_cstr[MAX_ESCAPED_DOMAIN_NAME];
+					if (!putRData(mDNSNULL, cname.c, (mDNSu8 *)(cname.c + MAX_DOMAIN_NAME), answer))
+							LogMsg("queryrecord_result_callback: WCF CNAME putRData failed");
+					else
+						{
+						ConvertDomainNameToCString(&cname, cname_cstr);
+						CHECK_WCF_FUNCTION((WCFConnection *)WCFNameResolvesToAddr)
+							{
+							WCFNameResolvesToName(m->WCF, name, cname_cstr, x.cr_uid);
+							}
+						}
+					}
+				}
+			else my_perror("queryrecord_result_callback: ERROR: getsockopt LOCAL_PEERCRED");
+			}
+		}
+#endif
+#endif
+	}
+
+mDNSlocal void queryrecord_termination_callback(request_state *request)
+	{
+	LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) STOP",
+		request->sd, request->u.queryrecord.q.qname.c, DNSTypeName(request->u.queryrecord.q.qtype));
+	if (request->u.queryrecord.q.QuestionContext)
+		{
+		mDNS_StopQuery(&mDNSStorage, &request->u.queryrecord.q);  // no need to error check
+		request->u.queryrecord.q.QuestionContext = mDNSNULL;
+		}
+	else
+		{
+		DNSQuestion *question = &request->u.queryrecord.q;
+		LogInfo("queryrecord_termination_callback: question %##s (%s) already stopped, InterfaceID %p", question->qname.c, DNSTypeName(question->qtype), question->InterfaceID);
+		}
+
+	if (request->u.queryrecord.q.qnameOrig)
+		{
+		freeL("QueryTermination", request->u.queryrecord.q.qnameOrig);
+		request->u.queryrecord.q.qnameOrig = mDNSNULL;
+		}
+	if (request->u.queryrecord.q.InterfaceID == mDNSInterface_P2P || (!request->u.queryrecord.q.InterfaceID && SameDomainName((const domainname *)LastLabel(&request->u.queryrecord.q.qname), &localdomain) && (request->flags & kDNSServiceFlagsIncludeP2P)))
+		{
+		LogInfo("queryrecord_termination_callback: calling external_stop_browsing_for_service()");
+		external_stop_browsing_for_service(&mDNSStorage, &request->u.queryrecord.q.qname, request->u.queryrecord.q.qtype);
+		}
+  	if (request->u.queryrecord.q2)
+  		{
+ 		if (request->u.queryrecord.q2->QuestionContext)
+ 			{
+ 			LogInfo("queryrecord_termination_callback: Stopping q2 %##s", request->u.queryrecord.q2->qname.c);
+ 			mDNS_StopQuery(&mDNSStorage, request->u.queryrecord.q2);
+ 			}
+		else 
+			{
+			DNSQuestion *question = request->u.queryrecord.q2;
+			LogInfo("queryrecord_termination_callback: q2 %##s (%s) already stopped, InterfaceID %p", question->qname.c, DNSTypeName(question->qtype), question->InterfaceID);
+			}
+ 		if (request->u.queryrecord.q2->qnameOrig)
+ 			{
+ 			LogInfo("queryrecord_termination_callback: freeing q2 qnameOrig %##s", request->u.queryrecord.q2->qnameOrig->c);
+ 			freeL("QueryTermination q2", request->u.queryrecord.q2->qnameOrig);
+ 			request->u.queryrecord.q2->qnameOrig = mDNSNULL;
+ 			}
+  		freeL("queryrecord Q2", request->u.queryrecord.q2);
+  		request->u.queryrecord.q2 = mDNSNULL;
+  		}
+	}
+
+mDNSlocal mStatus handle_queryrecord_request(request_state *request)
+	{
+	DNSQuestion *const q = &request->u.queryrecord.q;
+	char name[256];
+	mDNSu16 rrtype, rrclass;
+	mStatus err;
+
+	DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend);
+	mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend);
+	mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex);
+	if (interfaceIndex && !InterfaceID) return(mStatus_BadParamErr);
+
+	if (get_string(&request->msgptr, request->msgend, name, 256) < 0) return(mStatus_BadParamErr);
+	rrtype  = get_uint16(&request->msgptr, request->msgend);
+	rrclass = get_uint16(&request->msgptr, request->msgend);
+
+	if (!request->msgptr)
+		{ LogMsg("%3d: DNSServiceQueryRecord(unreadable parameters)", request->sd); return(mStatus_BadParamErr); }
+
+	request->flags = flags;
+	mDNSPlatformMemZero(&request->u.queryrecord, sizeof(request->u.queryrecord));
+
+	q->InterfaceID      = InterfaceID;
+	q->Target           = zeroAddr;
+	if (!MakeDomainNameFromDNSNameString(&q->qname, name)) 			return(mStatus_BadParamErr);
+#if 0
+	if (!AuthorizedDomain(request, &q->qname, AutoBrowseDomains))	return (mStatus_NoError);
+#endif
+	q->qtype            = rrtype;
+	q->qclass           = rrclass;
+	q->LongLived        = (flags & kDNSServiceFlagsLongLivedQuery     ) != 0;
+	q->ExpectUnique     = mDNSfalse;
+	q->ForceMCast       = (flags & kDNSServiceFlagsForceMulticast     ) != 0;
+	q->ReturnIntermed   = (flags & kDNSServiceFlagsReturnIntermediates) != 0;
+	q->SuppressUnusable = (flags & kDNSServiceFlagsSuppressUnusable   ) != 0;
+	q->TimeoutQuestion  = (flags & kDNSServiceFlagsTimeout            ) != 0;
+	q->WakeOnResolve    = 0;
+	q->QuestionCallback = queryrecord_result_callback;
+	q->QuestionContext  = request;
+	q->SearchListIndex  = 0;
+
+	// Don't append search domains for fully qualified domain names including queries
+	// such as e.g., "abc." that has only one label. We convert all names to FQDNs as internally
+	// we only deal with FQDNs. Hence, we cannot look at qname to figure out whether we should
+	// append search domains or not.  So, we record that information in AppendSearchDomains.
+	//
+	// We append search domains only for queries that are a single label. If overriden using
+	// command line argument "AlwaysAppendSearchDomains", then we do it for any query which
+	// is not fully qualified.
+
+	if ((rrtype == kDNSType_A || rrtype == kDNSType_AAAA) && name[strlen(name) - 1] != '.' &&
+		(AlwaysAppendSearchDomains || CountLabels(&q->qname) == 1))
+		{
+		q->AppendSearchDomains = 1;
+		q->AppendLocalSearchDomains = 1;
+		}
+	else
+		{
+		q->AppendSearchDomains = 0;
+		q->AppendLocalSearchDomains = 0;
+		}
+
+	// For single label queries that are not fully qualified, look at /etc/hosts, cache and try
+	// search domains before trying them on the wire as a single label query. RetryWithSearchDomains
+	// tell the core to call back into the UDS layer if there is no valid response in /etc/hosts or
+	// the cache
+	q->RetryWithSearchDomains = ApplySearchDomainsFirst(q) ? 1 : 0;
+	q->qnameOrig        = mDNSNULL;
+
+	LogOperation("%3d: DNSServiceQueryRecord(%X, %d, %##s, %s) START", request->sd, flags, interfaceIndex, q->qname.c, DNSTypeName(q->qtype));
+	err = mDNS_StartQuery(&mDNSStorage, q);
+	if (err) LogMsg("%3d: ERROR: DNSServiceQueryRecord %##s %s mDNS_StartQuery: %d", request->sd, q->qname.c, DNSTypeName(q->qtype), (int)err);
+	else 
+		{
+		request->terminate = queryrecord_termination_callback;
+		if (q->InterfaceID == mDNSInterface_P2P || (!q->InterfaceID && SameDomainName((const domainname *)LastLabel(&q->qname), &localdomain) && (flags & kDNSServiceFlagsIncludeP2P)))
+			{
+			LogInfo("handle_queryrecord_request: calling external_start_browsing_for_service()");
+			external_start_browsing_for_service(&mDNSStorage, &q->qname, q->qtype);
+			}
+		}
+
+#if APPLE_OSX_mDNSResponder
+	err = SendAdditionalQuery(q, request, err);
+#endif // APPLE_OSX_mDNSResponder
+
+	return(err);
+	}
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - DNSServiceEnumerateDomains
+#endif
+
+mDNSlocal reply_state *format_enumeration_reply(request_state *request,
+	const char *domain, DNSServiceFlags flags, mDNSu32 ifi, DNSServiceErrorType err)
+	{
+	size_t len;
+	reply_state *reply;
+	char *data;
+
+	len = sizeof(DNSServiceFlags);
+	len += sizeof(mDNSu32);
+	len += sizeof(DNSServiceErrorType);
+	len += strlen(domain) + 1;
+
+	reply = create_reply(enumeration_reply_op, len, request);
+	reply->rhdr->flags = dnssd_htonl(flags);
+	reply->rhdr->ifi   = dnssd_htonl(ifi);
+	reply->rhdr->error = dnssd_htonl(err);
+	data = (char *)&reply->rhdr[1];
+	put_string(domain, &data);
+	return reply;
+	}
+
+mDNSlocal void enum_termination_callback(request_state *request)
+	{
+	mDNS_StopGetDomains(&mDNSStorage, &request->u.enumeration.q_all);
+	mDNS_StopGetDomains(&mDNSStorage, &request->u.enumeration.q_default);
+	}
+
+mDNSlocal void enum_result_callback(mDNS *const m,
+	DNSQuestion *const question, const ResourceRecord *const answer, QC_result AddRecord)
+	{
+	char domain[MAX_ESCAPED_DOMAIN_NAME];
+	request_state *request = question->QuestionContext;
+	DNSServiceFlags flags = 0;
+	reply_state *reply;
+	(void)m; // Unused
+
+	if (answer->rrtype != kDNSType_PTR) return;
+
+#if 0
+	if (!AuthorizedDomain(request, &answer->rdata->u.name, request->u.enumeration.flags ? AutoRegistrationDomains : AutoBrowseDomains)) return;
+#endif
+
+	// We only return add/remove events for the browse and registration lists
+	// For the default browse and registration answers, we only give an "ADD" event
+	if (question == &request->u.enumeration.q_default && !AddRecord) return;
+
+	if (AddRecord)
+		{
+		flags |= kDNSServiceFlagsAdd;
+		if (question == &request->u.enumeration.q_default) flags |= kDNSServiceFlagsDefault;
+		}
+
+	ConvertDomainNameToCString(&answer->rdata->u.name, domain);
+	// Note that we do NOT propagate specific interface indexes to the client - for example, a domain we learn from
+	// a machine's system preferences may be discovered on the LocalOnly interface, but should be browsed on the
+	// network, so we just pass kDNSServiceInterfaceIndexAny
+	reply = format_enumeration_reply(request, domain, flags, kDNSServiceInterfaceIndexAny, kDNSServiceErr_NoError);
+	if (!reply) { LogMsg("ERROR: enum_result_callback, format_enumeration_reply"); return; }
+
+	LogOperation("%3d: DNSServiceEnumerateDomains(%#2s) RESULT %s: %s", request->sd, question->qname.c, AddRecord ? "Add" : "Rmv", domain);
+
+	append_reply(request, reply);
+	}
+
+mDNSlocal mStatus handle_enum_request(request_state *request)
+	{
+	mStatus err;
+	DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend);
+	DNSServiceFlags reg = flags & kDNSServiceFlagsRegistrationDomains;
+	mDNS_DomainType t_all     = reg ? mDNS_DomainTypeRegistration        : mDNS_DomainTypeBrowse;
+	mDNS_DomainType t_default = reg ? mDNS_DomainTypeRegistrationDefault : mDNS_DomainTypeBrowseDefault;
+	mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend);
+	mDNSInterfaceID InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex);
+	if (interfaceIndex && !InterfaceID) return(mStatus_BadParamErr);
+
+	if (!request->msgptr)
+		{ LogMsg("%3d: DNSServiceEnumerateDomains(unreadable parameters)", request->sd); return(mStatus_BadParamErr); }
+
+	// allocate context structures
+	uDNS_SetupSearchDomains(&mDNSStorage, UDNS_START_WAB_QUERY);
+
+#if 0
+	// mark which kind of enumeration we're doing so we can (de)authorize certain domains
+	request->u.enumeration.flags = reg;
+#endif
+
+	// enumeration requires multiple questions, so we must link all the context pointers so that
+	// necessary context can be reached from the callbacks
+	request->u.enumeration.q_all    .QuestionContext = request;
+	request->u.enumeration.q_default.QuestionContext = request;
+	
+	// if the caller hasn't specified an explicit interface, we use local-only to get the system-wide list.
+	if (!InterfaceID) InterfaceID = mDNSInterface_LocalOnly;
+
+	// make the calls
+	LogOperation("%3d: DNSServiceEnumerateDomains(%X=%s)", request->sd, flags,
+		(flags & kDNSServiceFlagsBrowseDomains      ) ? "kDNSServiceFlagsBrowseDomains" :
+		(flags & kDNSServiceFlagsRegistrationDomains) ? "kDNSServiceFlagsRegistrationDomains" : "<<Unknown>>");
+	err = mDNS_GetDomains(&mDNSStorage, &request->u.enumeration.q_all, t_all, NULL, InterfaceID, enum_result_callback, request);
+	if (!err)
+		{
+		err = mDNS_GetDomains(&mDNSStorage, &request->u.enumeration.q_default, t_default, NULL, InterfaceID, enum_result_callback, request);
+		if (err) mDNS_StopGetDomains(&mDNSStorage, &request->u.enumeration.q_all);
+		else request->terminate = enum_termination_callback;
+		}
+
+	return(err);
+	}
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - DNSServiceReconfirmRecord & Misc
+#endif
+
+mDNSlocal mStatus handle_reconfirm_request(request_state *request)
+	{
+	mStatus status = mStatus_BadParamErr;
+	AuthRecord *rr = read_rr_from_ipc_msg(request, 0, 0);
+	if (rr)
+		{
+		status = mDNS_ReconfirmByValue(&mDNSStorage, &rr->resrec);
+		LogOperation(
+			(status == mStatus_NoError) ?
+			"%3d: DNSServiceReconfirmRecord(%s) interface %d initiated" :
+			"%3d: DNSServiceReconfirmRecord(%s) interface %d failed: %d",
+			request->sd, RRDisplayString(&mDNSStorage, &rr->resrec),
+			mDNSPlatformInterfaceIndexfromInterfaceID(&mDNSStorage, rr->resrec.InterfaceID, mDNSfalse), status);
+		freeL("AuthRecord/handle_reconfirm_request", rr);
+		}
+	return(status);
+	}
+
+mDNSlocal mStatus handle_setdomain_request(request_state *request)
+	{
+	char domainstr[MAX_ESCAPED_DOMAIN_NAME];
+	domainname domain;
+	DNSServiceFlags flags = get_flags(&request->msgptr, request->msgend);
+	(void)flags; // Unused
+	if (get_string(&request->msgptr, request->msgend, domainstr, MAX_ESCAPED_DOMAIN_NAME) < 0 ||
+		!MakeDomainNameFromDNSNameString(&domain, domainstr))
+		{ LogMsg("%3d: DNSServiceSetDefaultDomainForUser(unreadable parameters)", request->sd); return(mStatus_BadParamErr); }
+
+	LogOperation("%3d: DNSServiceSetDefaultDomainForUser(%##s)", request->sd, domain.c);
+	return(mStatus_NoError);
+	}
+
+typedef packedstruct
+	{
+	mStatus err;
+	mDNSu32 len;
+	mDNSu32 vers;
+	} DaemonVersionReply;
+
+mDNSlocal void handle_getproperty_request(request_state *request)
+	{
+	const mStatus BadParamErr = dnssd_htonl((mDNSu32)mStatus_BadParamErr);
+	char prop[256];
+	if (get_string(&request->msgptr, request->msgend, prop, sizeof(prop)) >= 0)
+		{
+		LogOperation("%3d: DNSServiceGetProperty(%s)", request->sd, prop);
+		if (!strcmp(prop, kDNSServiceProperty_DaemonVersion))
+			{
+			DaemonVersionReply x = { 0, dnssd_htonl(4), dnssd_htonl(_DNS_SD_H) };
+			send_all(request->sd, (const char *)&x, sizeof(x));
+			return;
+			}
+		}
+
+	// If we didn't recogize the requested property name, return BadParamErr
+	send_all(request->sd, (const char *)&BadParamErr, sizeof(BadParamErr));
+	}
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - DNSServiceNATPortMappingCreate
+#endif
+
+#define DNSServiceProtocol(X) ((X) == NATOp_AddrRequest ? 0 : (X) == NATOp_MapUDP ? kDNSServiceProtocol_UDP : kDNSServiceProtocol_TCP)
+
+mDNSlocal void port_mapping_termination_callback(request_state *request)
+	{
+	LogOperation("%3d: DNSServiceNATPortMappingCreate(%X, %u, %u, %d) STOP", request->sd,
+		DNSServiceProtocol(request->u.pm.NATinfo.Protocol),
+		mDNSVal16(request->u.pm.NATinfo.IntPort), mDNSVal16(request->u.pm.ReqExt), request->u.pm.NATinfo.NATLease);
+	mDNS_StopNATOperation(&mDNSStorage, &request->u.pm.NATinfo);
+	}
+
+// Called via function pointer when we get a NAT-PMP address request or port mapping response
+mDNSlocal void port_mapping_create_request_callback(mDNS *m, NATTraversalInfo *n)
+	{
+	request_state *request = (request_state *)n->clientContext;
+	reply_state *rep;
+	int replyLen;
+	char *data;
+
+	if (!request) { LogMsg("port_mapping_create_request_callback called with unknown request_state object"); return; }
+
+	// calculate reply data length
+	replyLen = sizeof(DNSServiceFlags);
+	replyLen += 3 * sizeof(mDNSu32);  // if index + addr + ttl
+	replyLen += sizeof(DNSServiceErrorType);
+	replyLen += 2 * sizeof(mDNSu16);  // Internal Port + External Port
+	replyLen += sizeof(mDNSu8);       // protocol
+
+	rep = create_reply(port_mapping_reply_op, replyLen, request);
+
+	rep->rhdr->flags = dnssd_htonl(0);
+	rep->rhdr->ifi   = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(m, n->InterfaceID, mDNSfalse));
+	rep->rhdr->error = dnssd_htonl(n->Result);
+
+	data = (char *)&rep->rhdr[1];
+
+	*data++ = request->u.pm.NATinfo.ExternalAddress.b[0];
+	*data++ = request->u.pm.NATinfo.ExternalAddress.b[1];
+	*data++ = request->u.pm.NATinfo.ExternalAddress.b[2];
+	*data++ = request->u.pm.NATinfo.ExternalAddress.b[3];
+	*data++ = DNSServiceProtocol(request->u.pm.NATinfo.Protocol);
+	*data++ = request->u.pm.NATinfo.IntPort.b[0];
+	*data++ = request->u.pm.NATinfo.IntPort.b[1];
+	*data++ = request->u.pm.NATinfo.ExternalPort.b[0];
+	*data++ = request->u.pm.NATinfo.ExternalPort.b[1];
+	put_uint32(request->u.pm.NATinfo.Lifetime, &data);
+
+	LogOperation("%3d: DNSServiceNATPortMappingCreate(%X, %u, %u, %d) RESULT %.4a:%u TTL %u", request->sd,
+		DNSServiceProtocol(request->u.pm.NATinfo.Protocol),
+		mDNSVal16(request->u.pm.NATinfo.IntPort), mDNSVal16(request->u.pm.ReqExt), request->u.pm.NATinfo.NATLease,
+		&request->u.pm.NATinfo.ExternalAddress, mDNSVal16(request->u.pm.NATinfo.ExternalPort), request->u.pm.NATinfo.Lifetime);
+
+	append_reply(request, rep);
+	}
+
+mDNSlocal mStatus handle_port_mapping_request(request_state *request)
+	{
+	mDNSu32 ttl = 0;
+	mStatus err = mStatus_NoError;
+
+	DNSServiceFlags flags          = get_flags(&request->msgptr, request->msgend);
+	mDNSu32         interfaceIndex = get_uint32(&request->msgptr, request->msgend);
+	mDNSInterfaceID InterfaceID    = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex);
+	mDNSu8          protocol       = (mDNSu8)get_uint32(&request->msgptr, request->msgend);
+	(void)flags; // Unused
+	if (interfaceIndex && !InterfaceID) return(mStatus_BadParamErr);
+	if (request->msgptr + 8 > request->msgend) request->msgptr = NULL;
+	else
+		{
+		request->u.pm.NATinfo.IntPort.b[0] = *request->msgptr++;
+		request->u.pm.NATinfo.IntPort.b[1] = *request->msgptr++;
+		request->u.pm.ReqExt.b[0]          = *request->msgptr++;
+		request->u.pm.ReqExt.b[1]          = *request->msgptr++;
+		ttl = get_uint32(&request->msgptr, request->msgend);
+		}
+
+	if (!request->msgptr)
+		{ LogMsg("%3d: DNSServiceNATPortMappingCreate(unreadable parameters)", request->sd); return(mStatus_BadParamErr); }
+
+	if (protocol == 0)	// If protocol == 0 (i.e. just request public address) then IntPort, ExtPort, ttl must be zero too
+		{
+		if (!mDNSIPPortIsZero(request->u.pm.NATinfo.IntPort) || !mDNSIPPortIsZero(request->u.pm.ReqExt) || ttl) return(mStatus_BadParamErr);
+		}
+	else
+		{
+		if (mDNSIPPortIsZero(request->u.pm.NATinfo.IntPort)) return(mStatus_BadParamErr);
+		if (!(protocol & (kDNSServiceProtocol_UDP | kDNSServiceProtocol_TCP))) return(mStatus_BadParamErr);
+		}
+
+	request->u.pm.NATinfo.Protocol       = !protocol ? NATOp_AddrRequest : (protocol == kDNSServiceProtocol_UDP) ? NATOp_MapUDP : NATOp_MapTCP;
+	//       u.pm.NATinfo.IntPort        = already set above
+	request->u.pm.NATinfo.RequestedPort  = request->u.pm.ReqExt;
+	request->u.pm.NATinfo.NATLease       = ttl;
+	request->u.pm.NATinfo.clientCallback = port_mapping_create_request_callback;
+	request->u.pm.NATinfo.clientContext  = request;
+
+	LogOperation("%3d: DNSServiceNATPortMappingCreate(%X, %u, %u, %d) START", request->sd,
+		protocol, mDNSVal16(request->u.pm.NATinfo.IntPort), mDNSVal16(request->u.pm.ReqExt), request->u.pm.NATinfo.NATLease);
+	err = mDNS_StartNATOperation(&mDNSStorage, &request->u.pm.NATinfo);
+	if (err) LogMsg("ERROR: mDNS_StartNATOperation: %d", (int)err);
+	else request->terminate = port_mapping_termination_callback;
+
+	return(err);
+	}
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - DNSServiceGetAddrInfo
+#endif
+
+mDNSlocal void addrinfo_termination_callback(request_state *request)
+	{
+	LogOperation("%3d: DNSServiceGetAddrInfo(%##s) STOP", request->sd, request->u.addrinfo.q4.qname.c);
+
+	if (request->u.addrinfo.q4.QuestionContext)
+		{
+		mDNS_StopQuery(&mDNSStorage, &request->u.addrinfo.q4);
+		request->u.addrinfo.q4.QuestionContext = mDNSNULL;
+		}
+	if (request->u.addrinfo.q4.qnameOrig)
+		{
+		freeL("QueryTermination", request->u.addrinfo.q4.qnameOrig);
+		request->u.addrinfo.q4.qnameOrig = mDNSNULL;
+		}
+	if (request->u.addrinfo.q42)
+		{
+		if (request->u.addrinfo.q42->QuestionContext)
+			{
+			LogInfo("addrinfo_termination_callback: Stopping q42 %##s", request->u.addrinfo.q42->qname.c);
+			mDNS_StopQuery(&mDNSStorage, request->u.addrinfo.q42);
+			}
+		if (request->u.addrinfo.q42->qnameOrig)
+			{
+			LogInfo("addrinfo_termination_callback: freeing q42 qnameOrig %##s", request->u.addrinfo.q42->qnameOrig->c);
+			freeL("QueryTermination q42", request->u.addrinfo.q42->qnameOrig);
+			request->u.addrinfo.q42->qnameOrig = mDNSNULL;
+			}
+		freeL("addrinfo Q42", request->u.addrinfo.q42);
+		request->u.addrinfo.q42 = mDNSNULL;
+		}
+
+	if (request->u.addrinfo.q6.QuestionContext)
+		{
+		mDNS_StopQuery(&mDNSStorage, &request->u.addrinfo.q6);
+		request->u.addrinfo.q6.QuestionContext = mDNSNULL;
+		}
+	if (request->u.addrinfo.q6.qnameOrig)
+		{
+		freeL("QueryTermination", request->u.addrinfo.q6.qnameOrig);
+		request->u.addrinfo.q6.qnameOrig = mDNSNULL;
+		}
+	if (request->u.addrinfo.q62)
+		{
+		if (request->u.addrinfo.q62->QuestionContext)
+			{
+			LogInfo("addrinfo_termination_callback: Stopping q62 %##s", request->u.addrinfo.q62->qname.c);
+			mDNS_StopQuery(&mDNSStorage, request->u.addrinfo.q62);
+			}
+		if (request->u.addrinfo.q62->qnameOrig)
+			{
+			LogInfo("addrinfo_termination_callback: freeing q62 qnameOrig %##s", request->u.addrinfo.q62->qnameOrig->c);
+			freeL("QueryTermination q62", request->u.addrinfo.q62->qnameOrig);
+			request->u.addrinfo.q62->qnameOrig = mDNSNULL;
+			}
+		freeL("addrinfo Q62", request->u.addrinfo.q62);
+		request->u.addrinfo.q62 = mDNSNULL;
+		}
+	}
+
+mDNSlocal mStatus handle_addrinfo_request(request_state *request)
+	{
+	char hostname[256];
+	domainname d;
+	mStatus err = 0;
+
+	DNSServiceFlags flags  = get_flags(&request->msgptr, request->msgend);
+	mDNSu32 interfaceIndex = get_uint32(&request->msgptr, request->msgend);
+
+	mDNSPlatformMemZero(&request->u.addrinfo, sizeof(request->u.addrinfo));
+	request->u.addrinfo.interface_id = mDNSPlatformInterfaceIDfromInterfaceIndex(&mDNSStorage, interfaceIndex);
+	request->u.addrinfo.flags        = flags;
+	request->u.addrinfo.protocol     = get_uint32(&request->msgptr, request->msgend);
+
+	if (interfaceIndex && !request->u.addrinfo.interface_id) return(mStatus_BadParamErr);
+	if (request->u.addrinfo.protocol > (kDNSServiceProtocol_IPv4|kDNSServiceProtocol_IPv6)) return(mStatus_BadParamErr);
+
+	if (get_string(&request->msgptr, request->msgend, hostname, 256) < 0) return(mStatus_BadParamErr);
+
+	if (!request->msgptr) { LogMsg("%3d: DNSServiceGetAddrInfo(unreadable parameters)", request->sd); return(mStatus_BadParamErr); }
+
+	if (!MakeDomainNameFromDNSNameString(&d, hostname))
+		{ LogMsg("ERROR: handle_addrinfo_request: bad hostname: %s", hostname); return(mStatus_BadParamErr); }
+
+#if 0
+	if (!AuthorizedDomain(request, &d, AutoBrowseDomains))	return (mStatus_NoError);
+#endif
+
+	if (!request->u.addrinfo.protocol)
+		{
+		flags |= kDNSServiceFlagsSuppressUnusable;
+		request->u.addrinfo.protocol = (kDNSServiceProtocol_IPv4 | kDNSServiceProtocol_IPv6);
+		}
+
+	request->u.addrinfo.q4.InterfaceID      = request->u.addrinfo.q6.InterfaceID      = request->u.addrinfo.interface_id;
+	request->u.addrinfo.q4.Target           = request->u.addrinfo.q6.Target           = zeroAddr;
+	request->u.addrinfo.q4.qname            = request->u.addrinfo.q6.qname            = d;
+	request->u.addrinfo.q4.qclass           = request->u.addrinfo.q6.qclass           = kDNSServiceClass_IN;
+	request->u.addrinfo.q4.LongLived        = request->u.addrinfo.q6.LongLived        = (flags & kDNSServiceFlagsLongLivedQuery     ) != 0;
+	request->u.addrinfo.q4.ExpectUnique     = request->u.addrinfo.q6.ExpectUnique     = mDNSfalse;
+	request->u.addrinfo.q4.ForceMCast       = request->u.addrinfo.q6.ForceMCast       = (flags & kDNSServiceFlagsForceMulticast     ) != 0;
+	request->u.addrinfo.q4.ReturnIntermed   = request->u.addrinfo.q6.ReturnIntermed   = (flags & kDNSServiceFlagsReturnIntermediates) != 0;
+	request->u.addrinfo.q4.SuppressUnusable = request->u.addrinfo.q6.SuppressUnusable = (flags & kDNSServiceFlagsSuppressUnusable   ) != 0;
+	request->u.addrinfo.q4.TimeoutQuestion  = request->u.addrinfo.q6.TimeoutQuestion  = (flags & kDNSServiceFlagsTimeout            ) != 0;
+	request->u.addrinfo.q4.WakeOnResolve    = request->u.addrinfo.q6.WakeOnResolve    = 0;
+	request->u.addrinfo.q4.qnameOrig        = request->u.addrinfo.q6.qnameOrig        = mDNSNULL;
+
+	if (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv4)
+		{
+		request->u.addrinfo.q4.qtype            = kDNSServiceType_A;
+		request->u.addrinfo.q4.SearchListIndex  = 0;
+
+		// We append search domains only for queries that are a single label. If overriden using
+		// command line argument "AlwaysAppendSearchDomains", then we do it for any query which
+		// is not fully qualified.
+		if (hostname[strlen(hostname) - 1] != '.' && (AlwaysAppendSearchDomains || CountLabels(&d) == 1))
+			{
+			request->u.addrinfo.q4.AppendSearchDomains = 1;
+			request->u.addrinfo.q4.AppendLocalSearchDomains = 1;
+			}
+		else
+			{
+			request->u.addrinfo.q4.AppendSearchDomains = 0;
+			request->u.addrinfo.q4.AppendLocalSearchDomains = 0;
+			}
+		request->u.addrinfo.q4.RetryWithSearchDomains = (ApplySearchDomainsFirst(&request->u.addrinfo.q4) ? 1 : 0);
+		request->u.addrinfo.q4.QuestionCallback = queryrecord_result_callback;
+		request->u.addrinfo.q4.QuestionContext  = request;
+		err = mDNS_StartQuery(&mDNSStorage, &request->u.addrinfo.q4);
+		if (err != mStatus_NoError)
+			{
+			LogMsg("ERROR: mDNS_StartQuery: %d", (int)err);
+			request->u.addrinfo.q4.QuestionContext = mDNSNULL;
+			}
+		#if APPLE_OSX_mDNSResponder
+		err = SendAdditionalQuery(&request->u.addrinfo.q4, request, err);
+		#endif // APPLE_OSX_mDNSResponder
+		}
+
+	if (!err && (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv6))
+		{
+		request->u.addrinfo.q6.qtype            = kDNSServiceType_AAAA;
+		request->u.addrinfo.q6.SearchListIndex  = 0;
+		if (hostname[strlen(hostname) - 1] != '.' && (AlwaysAppendSearchDomains || CountLabels(&d) == 1))
+			{
+			request->u.addrinfo.q6.AppendSearchDomains = 1;
+			request->u.addrinfo.q6.AppendLocalSearchDomains = 1;
+			}
+		else
+			{
+			request->u.addrinfo.q6.AppendSearchDomains = 0;
+			request->u.addrinfo.q6.AppendLocalSearchDomains = 0;
+			}
+		request->u.addrinfo.q6.RetryWithSearchDomains = (ApplySearchDomainsFirst(&request->u.addrinfo.q6) ? 1 : 0);
+		request->u.addrinfo.q6.QuestionCallback = queryrecord_result_callback;
+		request->u.addrinfo.q6.QuestionContext  = request;
+		err = mDNS_StartQuery(&mDNSStorage, &request->u.addrinfo.q6);
+		if (err != mStatus_NoError)
+			{
+			LogMsg("ERROR: mDNS_StartQuery: %d", (int)err);
+			request->u.addrinfo.q6.QuestionContext = mDNSNULL;
+			if (request->u.addrinfo.protocol & kDNSServiceProtocol_IPv4)
+				{
+				// If we started a query for IPv4, we need to cancel it
+				mDNS_StopQuery(&mDNSStorage, &request->u.addrinfo.q4);
+				request->u.addrinfo.q4.QuestionContext = mDNSNULL;
+				}
+			}
+		#if APPLE_OSX_mDNSResponder
+		err = SendAdditionalQuery(&request->u.addrinfo.q6, request, err);
+		#endif // APPLE_OSX_mDNSResponder
+		}
+
+	LogOperation("%3d: DNSServiceGetAddrInfo(%X, %d, %d, %##s) START",
+		request->sd, flags, interfaceIndex, request->u.addrinfo.protocol, d.c);
+
+	if (!err) request->terminate = addrinfo_termination_callback;
+
+	return(err);
+	}
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - Main Request Handler etc.
+#endif
+
+mDNSlocal request_state *NewRequest(void)
+	{
+	request_state **p = &all_requests;
+	while (*p) p=&(*p)->next;
+	*p = mallocL("request_state", sizeof(request_state));
+	if (!*p) FatalError("ERROR: malloc");
+	mDNSPlatformMemZero(*p, sizeof(request_state));
+	return(*p);
+	}
+
+// read_msg may be called any time when the transfer state (req->ts) is t_morecoming.
+// if there is no data on the socket, the socket will be closed and t_terminated will be returned
+mDNSlocal void read_msg(request_state *req)
+	{
+	if (req->ts == t_terminated || req->ts == t_error)
+		{ LogMsg("%3d: ERROR: read_msg called with transfer state terminated or error", req->sd); req->ts = t_error; return; }
+
+	if (req->ts == t_complete)	// this must be death or something is wrong
+		{
+		char buf[4];	// dummy for death notification
+		int nread = udsSupportReadFD(req->sd, buf, 4, 0, req->platform_data);
+		if (!nread) { req->ts = t_terminated; return; }
+		if (nread < 0) goto rerror;
+		LogMsg("%3d: ERROR: read data from a completed request", req->sd);
+		req->ts = t_error;
+		return;
+		}
+
+	if (req->ts != t_morecoming)
+		{ LogMsg("%3d: ERROR: read_msg called with invalid transfer state (%d)", req->sd, req->ts); req->ts = t_error; return; }
+
+	if (req->hdr_bytes < sizeof(ipc_msg_hdr))
+		{
+		mDNSu32 nleft = sizeof(ipc_msg_hdr) - req->hdr_bytes;
+		int nread = udsSupportReadFD(req->sd, (char *)&req->hdr + req->hdr_bytes, nleft, 0, req->platform_data);
+		if (nread == 0) { req->ts = t_terminated; return; }
+		if (nread < 0) goto rerror;
+		req->hdr_bytes += nread;
+		if (req->hdr_bytes > sizeof(ipc_msg_hdr))
+			{ LogMsg("%3d: ERROR: read_msg - read too many header bytes", req->sd); req->ts = t_error; return; }
+
+		// only read data if header is complete
+		if (req->hdr_bytes == sizeof(ipc_msg_hdr))
+			{
+			ConvertHeaderBytes(&req->hdr);
+			if (req->hdr.version != VERSION)
+				{ LogMsg("%3d: ERROR: client version 0x%08X daemon version 0x%08X", req->sd, req->hdr.version, VERSION); req->ts = t_error; return; }
+
+			// Largest conceivable single request is a DNSServiceRegisterRecord() or DNSServiceAddRecord()
+			// with 64kB of rdata. Adding 1009 byte for a maximal domain name, plus a safety margin
+			// for other overhead, this means any message above 70kB is definitely bogus.
+			if (req->hdr.datalen > 70000)
+				{ LogMsg("%3d: ERROR: read_msg: hdr.datalen %u (0x%X) > 70000", req->sd, req->hdr.datalen, req->hdr.datalen); req->ts = t_error; return; }
+			req->msgbuf = mallocL("request_state msgbuf", req->hdr.datalen + MSG_PAD_BYTES);
+			if (!req->msgbuf) { my_perror("ERROR: malloc"); req->ts = t_error; return; }
+			req->msgptr = req->msgbuf;
+			req->msgend = req->msgbuf + req->hdr.datalen;
+			mDNSPlatformMemZero(req->msgbuf, req->hdr.datalen + MSG_PAD_BYTES);
+			}
+		}
+
+	// If our header is complete, but we're still needing more body data, then try to read it now
+	// Note: For cancel_request req->hdr.datalen == 0, but there's no error return socket for cancel_request
+	// Any time we need to get the error return socket we know we'll have at least one data byte
+	// (even if only the one-byte empty C string placeholder for the old ctrl_path parameter)
+	if (req->hdr_bytes == sizeof(ipc_msg_hdr) && req->data_bytes < req->hdr.datalen)
+		{
+		mDNSu32 nleft = req->hdr.datalen - req->data_bytes;
+		int nread;
+#if !defined(_WIN32)
+		struct iovec vec = { req->msgbuf + req->data_bytes, nleft };	// Tell recvmsg where we want the bytes put
+		struct msghdr msg;
+		struct cmsghdr *cmsg;
+		char cbuf[CMSG_SPACE(sizeof(dnssd_sock_t))];
+		msg.msg_name       = 0;
+		msg.msg_namelen    = 0;
+		msg.msg_iov        = &vec;
+		msg.msg_iovlen     = 1;
+		msg.msg_control    = cbuf;
+		msg.msg_controllen = sizeof(cbuf);
+		msg.msg_flags      = 0;
+		nread = recvmsg(req->sd, &msg, 0);
+#else
+		nread = udsSupportReadFD(req->sd, (char *)req->msgbuf + req->data_bytes, nleft, 0, req->platform_data);
+#endif
+		if (nread == 0) { req->ts = t_terminated; return; }
+		if (nread < 0) goto rerror;
+		req->data_bytes += nread;
+		if (req->data_bytes > req->hdr.datalen)
+			{ LogMsg("%3d: ERROR: read_msg - read too many data bytes", req->sd); req->ts = t_error; return; }
+#if !defined(_WIN32)
+		cmsg = CMSG_FIRSTHDR(&msg);
+#if DEBUG_64BIT_SCM_RIGHTS
+		LogMsg("%3d: Expecting %d %d %d %d", req->sd, sizeof(cbuf),       sizeof(cbuf),   SOL_SOCKET,       SCM_RIGHTS);
+		LogMsg("%3d: Got       %d %d %d %d", req->sd, msg.msg_controllen, cmsg->cmsg_len, cmsg->cmsg_level, cmsg->cmsg_type);
+#endif // DEBUG_64BIT_SCM_RIGHTS
+		if (msg.msg_controllen == sizeof(cbuf) &&
+			cmsg->cmsg_len     == CMSG_LEN(sizeof(dnssd_sock_t)) &&
+			cmsg->cmsg_level   == SOL_SOCKET   &&
+			cmsg->cmsg_type    == SCM_RIGHTS)
+			{
+#if APPLE_OSX_mDNSResponder
+			// Strictly speaking BPF_fd belongs solely in the platform support layer, but because
+			// of privilege separation on Mac OS X we need to get BPF_fd from mDNSResponderHelper,
+			// and it's convenient to repurpose the existing fd-passing code here for that task
+			if (req->hdr.op == send_bpf)
+				{
+				dnssd_sock_t x = *(dnssd_sock_t *)CMSG_DATA(cmsg);
+				LogOperation("%3d: Got BPF %d", req->sd, x);
+				mDNSPlatformReceiveBPF_fd(&mDNSStorage, x);
+				}
+			else
+#endif // APPLE_OSX_mDNSResponder
+				req->errsd = *(dnssd_sock_t *)CMSG_DATA(cmsg);
+#if DEBUG_64BIT_SCM_RIGHTS
+			LogMsg("%3d: read req->errsd %d", req->sd, req->errsd);
+#endif // DEBUG_64BIT_SCM_RIGHTS
+			if (req->data_bytes < req->hdr.datalen)
+				{
+				LogMsg("%3d: Client sent error socket %d via SCM_RIGHTS with req->data_bytes %d < req->hdr.datalen %d",
+					req->sd, req->errsd, req->data_bytes, req->hdr.datalen);
+				req->ts = t_error;
+				return;
+				}
+			}
+#endif
+		}
+
+	// If our header and data are both complete, see if we need to make our separate error return socket
+	if (req->hdr_bytes == sizeof(ipc_msg_hdr) && req->data_bytes == req->hdr.datalen)
+		{
+		if (req->terminate && req->hdr.op != cancel_request)
+			{
+			dnssd_sockaddr_t cliaddr;
+#if defined(USE_TCP_LOOPBACK)
+			mDNSOpaque16 port;
+			u_long opt = 1;
+			port.b[0] = req->msgptr[0];
+			port.b[1] = req->msgptr[1];
+			req->msgptr += 2;
+			cliaddr.sin_family      = AF_INET;
+			cliaddr.sin_port        = port.NotAnInteger;
+			cliaddr.sin_addr.s_addr = inet_addr(MDNS_TCP_SERVERADDR);
+#else
+			char ctrl_path[MAX_CTLPATH];
+			get_string(&req->msgptr, req->msgend, ctrl_path, MAX_CTLPATH);	// path is first element in message buffer
+			mDNSPlatformMemZero(&cliaddr, sizeof(cliaddr));
+			cliaddr.sun_family = AF_LOCAL;
+			mDNSPlatformStrCopy(cliaddr.sun_path, ctrl_path);
+			// If the error return path UDS name is empty string, that tells us
+			// that this is a new version of the library that's going to pass us
+			// the error return path socket via sendmsg/recvmsg
+			if (ctrl_path[0] == 0)
+				{
+				if (req->errsd == req->sd)
+					{ LogMsg("%3d: read_msg: ERROR failed to get errsd via SCM_RIGHTS", req->sd); req->ts = t_error; return; }
+				goto got_errfd;
+				}
+#endif
+	
+			req->errsd = socket(AF_DNSSD, SOCK_STREAM, 0);
+			if (!dnssd_SocketValid(req->errsd)) { my_perror("ERROR: socket"); req->ts = t_error; return; }
+
+			if (connect(req->errsd, (struct sockaddr *)&cliaddr, sizeof(cliaddr)) < 0)
+				{
+#if !defined(USE_TCP_LOOPBACK)
+				struct stat sb;
+				LogMsg("%3d: read_msg: Couldn't connect to error return path socket “%s” errno %d (%s)",
+					req->sd, cliaddr.sun_path, dnssd_errno, dnssd_strerror(dnssd_errno));
+				if (stat(cliaddr.sun_path, &sb) < 0)
+					LogMsg("%3d: read_msg: stat failed “%s” errno %d (%s)", req->sd, cliaddr.sun_path, dnssd_errno, dnssd_strerror(dnssd_errno));
+				else
+					LogMsg("%3d: read_msg: file “%s” mode %o (octal) uid %d gid %d", req->sd, cliaddr.sun_path, sb.st_mode, sb.st_uid, sb.st_gid);
+#endif
+				req->ts = t_error;
+				return;
+				}
+	
+#if !defined(USE_TCP_LOOPBACK)
+got_errfd:
+#endif
+			LogOperation("%3d: Error socket %d created %08X %08X", req->sd, req->errsd, req->hdr.client_context.u32[1], req->hdr.client_context.u32[0]);
+#if defined(_WIN32)
+			if (ioctlsocket(req->errsd, FIONBIO, &opt) != 0)
+#else
+			if (fcntl(req->errsd, F_SETFL, fcntl(req->errsd, F_GETFL, 0) | O_NONBLOCK) != 0)
+#endif
+				{
+				LogMsg("%3d: ERROR: could not set control socket to non-blocking mode errno %d (%s)",
+					req->sd, dnssd_errno, dnssd_strerror(dnssd_errno));
+				req->ts = t_error;
+				return;
+				}
+			}
+		
+		req->ts = t_complete;
+		}
+
+	return;
+
+rerror:
+	if (dnssd_errno == dnssd_EWOULDBLOCK || dnssd_errno == dnssd_EINTR) return;
+	LogMsg("%3d: ERROR: read_msg errno %d (%s)", req->sd, dnssd_errno, dnssd_strerror(dnssd_errno));
+	req->ts = t_error;
+	}
+
+#define RecordOrientedOp(X) \
+	((X) == reg_record_request || (X) == add_record_request || (X) == update_record_request || (X) == remove_record_request)
+
+// The lightweight operations are the ones that don't need a dedicated request_state structure allocated for them
+#define LightweightOp(X) (RecordOrientedOp(X) || (X) == cancel_request)
+
+mDNSlocal void request_callback(int fd, short filter, void *info)
+	{
+	mStatus err = 0;
+	request_state *req = info;
+	mDNSs32 min_size = sizeof(DNSServiceFlags);
+	(void)fd; // Unused
+	(void)filter; // Unused
+
+	for (;;)
+		{
+		read_msg(req);
+		if (req->ts == t_morecoming) return;
+		if (req->ts == t_terminated || req->ts == t_error) { AbortUnlinkAndFree(req); return; }
+		if (req->ts != t_complete) { LogMsg("req->ts %d != t_complete", req->ts); AbortUnlinkAndFree(req); return; }
+
+		if (req->hdr.version != VERSION)
+			{
+			LogMsg("ERROR: client version %d incompatible with daemon version %d", req->hdr.version, VERSION);
+			AbortUnlinkAndFree(req);
+			return;
+			}
+
+		switch(req->hdr.op)            //          Interface       + other data
+			{
+			case connection_request:       min_size = 0;                                                                           break;
+			case reg_service_request:      min_size += sizeof(mDNSu32) + 4 /* name, type, domain, host */ + 4 /* port, textlen */; break;
+			case add_record_request:       min_size +=                   4 /* type, rdlen */              + 4 /* ttl */;           break;
+			case update_record_request:    min_size +=                   2 /* rdlen */                    + 4 /* ttl */;           break;
+			case remove_record_request:                                                                                            break;
+			case browse_request:           min_size += sizeof(mDNSu32) + 2 /* type, domain */;                                     break;
+			case resolve_request:          min_size += sizeof(mDNSu32) + 3 /* type, type, domain */;                               break;
+			case query_request:            min_size += sizeof(mDNSu32) + 1 /* name */                     + 4 /* type, class*/;    break;
+			case enumeration_request:      min_size += sizeof(mDNSu32);                                                            break;
+			case reg_record_request:       min_size += sizeof(mDNSu32) + 1 /* name */ + 6 /* type, class, rdlen */ + 4 /* ttl */;  break;
+			case reconfirm_record_request: min_size += sizeof(mDNSu32) + 1 /* name */ + 6 /* type, class, rdlen */;                break;
+			case setdomain_request:        min_size +=                   1 /* domain */;                                           break;
+			case getproperty_request:      min_size = 2;                                                                           break;
+			case port_mapping_request:     min_size += sizeof(mDNSu32) + 4 /* udp/tcp */ + 4 /* int/ext port */    + 4 /* ttl */;  break;
+			case addrinfo_request:         min_size += sizeof(mDNSu32) + 4 /* v4/v6 */   + 1 /* hostname */;                       break;
+			case send_bpf:                 // Same as cancel_request below
+			case cancel_request:           min_size = 0;									       break;
+			case sethost_request:          min_size = sizeof(mDNSu32) + 1 /* hostname */;                                          break;
+			default: LogMsg("ERROR: validate_message - unsupported req type: %d", req->hdr.op); min_size = -1;                     break;
+			}
+
+		if ((mDNSs32)req->data_bytes < min_size)
+			{ LogMsg("Invalid message %d bytes; min for %d is %d", req->data_bytes, req->hdr.op, min_size); AbortUnlinkAndFree(req); return; }
+
+		if (LightweightOp(req->hdr.op) && !req->terminate)
+			{ LogMsg("Reg/Add/Update/Remove %d require existing connection", req->hdr.op);                  AbortUnlinkAndFree(req); return; }
+
+		// check if client wants silent operation
+		if (req->hdr.ipc_flags & IPC_FLAGS_NOREPLY) req->no_reply = 1;
+
+		// If req->terminate is already set, this means this operation is sharing an existing connection
+		if (req->terminate && !LightweightOp(req->hdr.op))
+			{
+			request_state *newreq = NewRequest();
+			newreq->primary = req;
+			newreq->sd      = req->sd;
+			newreq->errsd   = req->errsd;
+			newreq->uid     = req->uid;
+			newreq->hdr     = req->hdr;
+			newreq->msgbuf  = req->msgbuf;
+			newreq->msgptr  = req->msgptr;
+			newreq->msgend  = req->msgend;
+			req = newreq;
+			}
+
+		// If we're shutting down, don't allow new client requests
+		// We do allow "cancel" and "getproperty" during shutdown
+		if (mDNSStorage.ShutdownTime && req->hdr.op != cancel_request && req->hdr.op != getproperty_request)
+			{
+			err = mStatus_ServiceNotRunning;
+			}
+		else switch(req->hdr.op)
+			{
+			// These are all operations that have their own first-class request_state object
+			case connection_request:           LogOperation("%3d: DNSServiceCreateConnection START", req->sd);
+											   req->terminate = connection_termination; break;
+			case resolve_request:              err = handle_resolve_request     (req);  break;
+			case query_request:                err = handle_queryrecord_request (req);  break;
+			case browse_request:               err = handle_browse_request      (req);  break;
+			case reg_service_request:          err = handle_regservice_request  (req);  break;
+			case enumeration_request:          err = handle_enum_request        (req);  break;
+			case reconfirm_record_request:     err = handle_reconfirm_request   (req);  break;
+			case setdomain_request:            err = handle_setdomain_request   (req);  break;
+			case getproperty_request:                handle_getproperty_request (req);  break;
+			case port_mapping_request:         err = handle_port_mapping_request(req);  break;
+			case addrinfo_request:             err = handle_addrinfo_request    (req);  break;
+			case send_bpf:                     /* Do nothing for send_bpf */            break;
+
+			// These are all operations that work with an existing request_state object
+			case reg_record_request:           err = handle_regrecord_request   (req);  break;
+			case add_record_request:           err = handle_add_request         (req);  break;
+			case update_record_request:        err = handle_update_request      (req);  break;
+			case remove_record_request:        err = handle_removerecord_request(req);  break;
+			case cancel_request:                     handle_cancel_request      (req);  break;
+			case sethost_request:              err = handle_sethost_request     (req);  break;
+			default: LogMsg("%3d: ERROR: Unsupported UDS req: %d", req->sd, req->hdr.op);
+			}
+
+		// req->msgbuf may be NULL, e.g. for connection_request or remove_record_request
+		if (req->msgbuf) freeL("request_state msgbuf", req->msgbuf);
+
+		// There's no return data for a cancel request (DNSServiceRefDeallocate returns no result)
+		// For a DNSServiceGetProperty call, the handler already generated the response, so no need to do it again here
+		if (req->hdr.op != cancel_request && req->hdr.op != getproperty_request && req->hdr.op != send_bpf)
+			{
+			const mStatus err_netorder = dnssd_htonl(err);
+			send_all(req->errsd, (const char *)&err_netorder, sizeof(err_netorder));
+			if (req->errsd != req->sd)
+				{
+				LogOperation("%3d: Error socket %d closed  %08X %08X (%d)",
+					req->sd, req->errsd, req->hdr.client_context.u32[1], req->hdr.client_context.u32[0], err);
+				dnssd_close(req->errsd);
+				req->errsd = req->sd;
+				// Also need to reset the parent's errsd, if this is a subordinate operation
+				if (req->primary) req->primary->errsd = req->primary->sd;
+				}
+			}
+
+		// Reset ready to accept the next req on this pipe
+		if (req->primary) req = req->primary;
+		req->ts         = t_morecoming;
+		req->hdr_bytes  = 0;
+		req->data_bytes = 0;
+		req->msgbuf     = mDNSNULL;
+		req->msgptr     = mDNSNULL;
+		req->msgend     = 0;
+		}
+	}
+
+mDNSlocal void connect_callback(int fd, short filter, void *info)
+	{
+	dnssd_sockaddr_t cliaddr;
+	dnssd_socklen_t len = (dnssd_socklen_t) sizeof(cliaddr);
+	dnssd_sock_t sd = accept(fd, (struct sockaddr*) &cliaddr, &len);
+#if defined(SO_NOSIGPIPE) || defined(_WIN32)
+	unsigned long optval = 1;
+#endif
+
+	(void)filter; // Unused
+	(void)info; // Unused
+
+	if (!dnssd_SocketValid(sd))
+		{
+		if (dnssd_errno != dnssd_EWOULDBLOCK) my_perror("ERROR: accept");
+		return;
+		}
+
+#ifdef SO_NOSIGPIPE
+	// Some environments (e.g. OS X) support turning off SIGPIPE for a socket
+	if (setsockopt(sd, SOL_SOCKET, SO_NOSIGPIPE, &optval, sizeof(optval)) < 0)
+		LogMsg("%3d: WARNING: setsockopt - SO_NOSIGPIPE %d (%s)", sd, dnssd_errno, dnssd_strerror(dnssd_errno));
+#endif
+
+#if defined(_WIN32)
+	if (ioctlsocket(sd, FIONBIO, &optval) != 0)
+#else
+	if (fcntl(sd, F_SETFL, fcntl(sd, F_GETFL, 0) | O_NONBLOCK) != 0)
+#endif
+		{
+		my_perror("ERROR: fcntl(sd, F_SETFL, O_NONBLOCK) - aborting client");
+		dnssd_close(sd);
+		return;
+		}
+	else
+		{
+		request_state *request = NewRequest();
+		request->ts    = t_morecoming;
+		request->sd    = sd;
+		request->errsd = sd;
+#if APPLE_OSX_mDNSResponder
+		struct xucred x;
+		socklen_t xucredlen = sizeof(x);
+		if (getsockopt(sd, 0, LOCAL_PEERCRED, &x, &xucredlen) >= 0 && x.cr_version == XUCRED_VERSION) request->uid = x.cr_uid;
+		else my_perror("ERROR: getsockopt, LOCAL_PEERCRED");
+		debugf("LOCAL_PEERCRED %d %u %u %d", xucredlen, x.cr_version, x.cr_uid, x.cr_ngroups);
+#endif // APPLE_OSX_mDNSResponder
+		LogOperation("%3d: Adding FD for uid %u", request->sd, request->uid);
+		udsSupportAddFDToEventLoop(sd, request_callback, request, &request->platform_data);
+		}
+	}
+
+mDNSlocal mDNSBool uds_socket_setup(dnssd_sock_t skt)
+	{
+#if defined(SO_NP_EXTENSIONS)
+	struct		so_np_extensions sonpx;
+	socklen_t 	optlen = sizeof(struct so_np_extensions);
+	sonpx.npx_flags = SONPX_SETOPTSHUT;
+	sonpx.npx_mask  = SONPX_SETOPTSHUT;
+	if (setsockopt(skt, SOL_SOCKET, SO_NP_EXTENSIONS, &sonpx, optlen) < 0)
+		my_perror("WARNING: could not set sockopt - SO_NP_EXTENSIONS");
+#endif
+#if defined(_WIN32)
+	// SEH: do we even need to do this on windows?
+	// This socket will be given to WSAEventSelect which will automatically set it to non-blocking
+	u_long opt = 1;
+	if (ioctlsocket(skt, FIONBIO, &opt) != 0)
+#else
+	if (fcntl(skt, F_SETFL, fcntl(skt, F_GETFL, 0) | O_NONBLOCK) != 0)
+#endif
+		{
+		my_perror("ERROR: could not set listen socket to non-blocking mode");
+		return mDNSfalse;
+		}
+
+	if (listen(skt, LISTENQ) != 0)
+		{
+		my_perror("ERROR: could not listen on listen socket");
+		return mDNSfalse;
+		}
+
+	if (mStatus_NoError != udsSupportAddFDToEventLoop(skt, connect_callback, (void *) NULL, (void **) NULL))
+		{
+		my_perror("ERROR: could not add listen socket to event loop");
+		return mDNSfalse;
+		}
+	else LogOperation("%3d: Listening for incoming Unix Domain Socket client requests", skt);
+	
+	return mDNStrue;
+	}
+
+mDNSexport int udsserver_init(dnssd_sock_t skts[], mDNSu32 count)
+	{
+	dnssd_sockaddr_t laddr;
+	int ret;
+	mDNSu32 i = 0;
+
+	LogInfo("udsserver_init");
+
+	// If a particular platform wants to opt out of having a PID file, define PID_FILE to be ""
+	if (PID_FILE[0])
+		{
+		FILE *fp = fopen(PID_FILE, "w");
+		if (fp != NULL)
+			{
+			fprintf(fp, "%d\n", getpid());
+			fclose(fp);
+			}
+		}
+
+	if (skts)
+		{
+		for (i = 0; i < count; i++)
+			if (dnssd_SocketValid(skts[i]) && !uds_socket_setup(skts[i]))
+				goto error;
+		}
+	else
+		{
+		listenfd = socket(AF_DNSSD, SOCK_STREAM, 0);
+		if (!dnssd_SocketValid(listenfd))
+			{
+			my_perror("ERROR: socket(AF_DNSSD, SOCK_STREAM, 0); failed");
+			goto error;
+			}
+
+		mDNSPlatformMemZero(&laddr, sizeof(laddr));
+
+		#if defined(USE_TCP_LOOPBACK)
+			{
+			laddr.sin_family = AF_INET;
+			laddr.sin_port = htons(MDNS_TCP_SERVERPORT);
+			laddr.sin_addr.s_addr = inet_addr(MDNS_TCP_SERVERADDR);
+			ret = bind(listenfd, (struct sockaddr *) &laddr, sizeof(laddr));
+			if (ret < 0)
+				{
+				my_perror("ERROR: bind(listenfd, (struct sockaddr *) &laddr, sizeof(laddr)); failed");
+				goto error;
+				}
+			}
+		#else
+			{
+			mode_t mask = umask(0);
+			unlink(MDNS_UDS_SERVERPATH);  // OK if this fails
+			laddr.sun_family = AF_LOCAL;
+			#ifndef NOT_HAVE_SA_LEN
+			// According to Stevens (section 3.2), there is no portable way to
+			// determine whether sa_len is defined on a particular platform.
+			laddr.sun_len = sizeof(struct sockaddr_un);
+			#endif
+			if (strlen(MDNS_UDS_SERVERPATH) >= sizeof(laddr.sun_path))
+				{
+					LogMsg("ERROR: MDNS_UDS_SERVERPATH must be < %d characters", (int)sizeof(laddr.sun_path));
+					goto error;
+				}
+			mDNSPlatformStrCopy(laddr.sun_path, MDNS_UDS_SERVERPATH);
+			ret = bind(listenfd, (struct sockaddr *) &laddr, sizeof(laddr));
+			umask(mask);
+			if (ret < 0)
+				{
+				my_perror("ERROR: bind(listenfd, (struct sockaddr *) &laddr, sizeof(laddr)); failed");
+				goto error;
+				}
+			}
+		#endif
+		
+		if (!uds_socket_setup(listenfd)) goto error;
+		}
+
+#if !defined(PLATFORM_NO_RLIMIT)
+	{
+	// Set maximum number of open file descriptors
+	#define MIN_OPENFILES 10240
+	struct rlimit maxfds, newfds;
+
+	// Due to bugs in OS X (<rdar://problem/2941095>, <rdar://problem/3342704>, <rdar://problem/3839173>)
+	// you have to get and set rlimits once before getrlimit will return sensible values
+	if (getrlimit(RLIMIT_NOFILE, &maxfds) < 0) { my_perror("ERROR: Unable to get file descriptor limit"); return 0; }
+	if (setrlimit(RLIMIT_NOFILE, &maxfds) < 0) my_perror("ERROR: Unable to set maximum file descriptor limit");
+
+	if (getrlimit(RLIMIT_NOFILE, &maxfds) < 0) { my_perror("ERROR: Unable to get file descriptor limit"); return 0; }
+	newfds.rlim_max = (maxfds.rlim_max > MIN_OPENFILES) ? maxfds.rlim_max : MIN_OPENFILES;
+	newfds.rlim_cur = (maxfds.rlim_cur > MIN_OPENFILES) ? maxfds.rlim_cur : MIN_OPENFILES;
+	if (newfds.rlim_max != maxfds.rlim_max || newfds.rlim_cur != maxfds.rlim_cur)
+		if (setrlimit(RLIMIT_NOFILE, &newfds) < 0) my_perror("ERROR: Unable to set maximum file descriptor limit");
+
+	if (getrlimit(RLIMIT_NOFILE, &maxfds) < 0) { my_perror("ERROR: Unable to get file descriptor limit"); return 0; }
+	debugf("maxfds.rlim_max %d", (long)maxfds.rlim_max);
+	debugf("maxfds.rlim_cur %d", (long)maxfds.rlim_cur);
+	}
+#endif
+
+	// We start a "LocalOnly" query looking for Automatic Browse Domain records.
+	// When Domain Enumeration in uDNS.c finds an "lb" record from the network, its "FoundDomain" routine
+	// creates a "LocalOnly" record, which results in our AutomaticBrowseDomainChange callback being invoked
+	mDNS_GetDomains(&mDNSStorage, &mDNSStorage.AutomaticBrowseDomainQ, mDNS_DomainTypeBrowseAutomatic,
+		mDNSNULL, mDNSInterface_LocalOnly, AutomaticBrowseDomainChange, mDNSNULL);
+
+	// Add "local" as recommended registration domain ("dns-sd -E"), recommended browsing domain ("dns-sd -F"), and automatic browsing domain
+	RegisterLocalOnlyDomainEnumPTR(&mDNSStorage, &localdomain, mDNS_DomainTypeRegistration);
+	RegisterLocalOnlyDomainEnumPTR(&mDNSStorage, &localdomain, mDNS_DomainTypeBrowse);
+	AddAutoBrowseDomain(0, &localdomain);
+
+	udsserver_handle_configchange(&mDNSStorage);
+	return 0;
+
+error:
+
+	my_perror("ERROR: udsserver_init");
+	return -1;
+	}
+
+mDNSexport int udsserver_exit(void)
+	{
+	// Cancel all outstanding client requests
+	while (all_requests) AbortUnlinkAndFree(all_requests);
+
+	// Clean up any special mDNSInterface_LocalOnly records we created, both the entries for "local" we
+	// created in udsserver_init, and others we created as a result of reading local configuration data
+	while (LocalDomainEnumRecords)
+		{
+		ARListElem *rem = LocalDomainEnumRecords;
+		LocalDomainEnumRecords = LocalDomainEnumRecords->next;
+		mDNS_Deregister(&mDNSStorage, &rem->ar);
+		}
+
+	// If the launching environment created no listening socket,
+	// that means we created it ourselves, so we should clean it up on exit
+	if (dnssd_SocketValid(listenfd))
+		{
+		dnssd_close(listenfd);
+#if !defined(USE_TCP_LOOPBACK)
+		// Currently, we're unable to remove /var/run/mdnsd because we've changed to userid "nobody"
+		// to give up unnecessary privilege, but we need to be root to remove this Unix Domain Socket.
+		// It would be nice if we could find a solution to this problem
+		if (unlink(MDNS_UDS_SERVERPATH))
+			debugf("Unable to remove %s", MDNS_UDS_SERVERPATH);
+#endif
+		}
+
+	if (PID_FILE[0]) unlink(PID_FILE);
+
+	return 0;
+	}
+
+mDNSlocal void LogClientInfo(mDNS *const m, const request_state *req)
+	{
+	char prefix[16];
+	if (req->primary) mDNS_snprintf(prefix, sizeof(prefix), " -> ");
+	else mDNS_snprintf(prefix, sizeof(prefix), "%3d:", req->sd);
+
+	usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000);
+
+	if (!req->terminate)
+		LogMsgNoIdent("%s No operation yet on this socket", prefix);
+	else if (req->terminate == connection_termination)
+		{
+		int num_records = 0, num_ops = 0;
+		const registered_record_entry *p;
+		const request_state *r;
+		for (p = req->u.reg_recs; p; p=p->next) num_records++;
+		for (r = req->next; r; r=r->next) if (r->primary == req) num_ops++;
+		LogMsgNoIdent("%s DNSServiceCreateConnection: %d registered record%s, %d kDNSServiceFlagsShareConnection operation%s", prefix,
+			num_records, num_records != 1 ? "s" : "",
+			num_ops,     num_ops     != 1 ? "s" : "");
+		for (p = req->u.reg_recs; p; p=p->next)
+			LogMsgNoIdent(" ->  DNSServiceRegisterRecord %3d %s", p->key, ARDisplayString(m, p->rr));
+		for (r = req->next; r; r=r->next) if (r->primary == req) LogClientInfo(m, r);
+		}
+	else if (req->terminate == regservice_termination_callback)
+		{
+		service_instance *ptr;
+		for (ptr = req->u.servicereg.instances; ptr; ptr = ptr->next)
+			LogMsgNoIdent("%s DNSServiceRegister         %##s %u/%u",
+				(ptr == req->u.servicereg.instances) ? prefix : "    ",
+				ptr->srs.RR_SRV.resrec.name->c, mDNSVal16(req->u.servicereg.port), SRS_PORT(&ptr->srs));
+		}
+	else if (req->terminate == browse_termination_callback)
+		{
+		browser_t *blist;
+		for (blist = req->u.browser.browsers; blist; blist = blist->next)
+			LogMsgNoIdent("%s DNSServiceBrowse           %##s", (blist == req->u.browser.browsers) ? prefix : "    ", blist->q.qname.c);
+		}
+	else if (req->terminate == resolve_termination_callback)
+		LogMsgNoIdent("%s DNSServiceResolve          %##s", prefix, req->u.resolve.qsrv.qname.c);
+	else if (req->terminate == queryrecord_termination_callback)
+		LogMsgNoIdent("%s DNSServiceQueryRecord      %##s (%s)", prefix, req->u.queryrecord.q.qname.c, DNSTypeName(req->u.queryrecord.q.qtype));
+	else if (req->terminate == enum_termination_callback)
+		LogMsgNoIdent("%s DNSServiceEnumerateDomains %##s", prefix, req->u.enumeration.q_all.qname.c);
+	else if (req->terminate == port_mapping_termination_callback)
+		LogMsgNoIdent("%s DNSServiceNATPortMapping   %.4a %s%s Int %d Req %d Ext %d Req TTL %d Granted TTL %d",
+			prefix,
+			&req->u.pm.NATinfo.ExternalAddress,
+			req->u.pm.NATinfo.Protocol & NATOp_MapTCP ? "TCP" : "   ",
+			req->u.pm.NATinfo.Protocol & NATOp_MapUDP ? "UDP" : "   ",
+			mDNSVal16(req->u.pm.NATinfo.IntPort),
+			mDNSVal16(req->u.pm.ReqExt),
+			mDNSVal16(req->u.pm.NATinfo.ExternalPort),
+			req->u.pm.NATinfo.NATLease,
+			req->u.pm.NATinfo.Lifetime);
+	else if (req->terminate == addrinfo_termination_callback)
+		LogMsgNoIdent("%s DNSServiceGetAddrInfo      %s%s %##s", prefix,
+			req->u.addrinfo.protocol & kDNSServiceProtocol_IPv4 ? "v4" : "  ",
+			req->u.addrinfo.protocol & kDNSServiceProtocol_IPv6 ? "v6" : "  ",
+			req->u.addrinfo.q4.qname.c);
+	else
+		LogMsgNoIdent("%s Unrecognized operation %p", prefix, req->terminate);
+	}
+
+mDNSlocal char *RecordTypeName(mDNSu8 rtype)
+	{
+	switch (rtype)
+		{
+		case kDNSRecordTypeUnregistered:  return ("Unregistered ");
+		case kDNSRecordTypeDeregistering: return ("Deregistering");
+		case kDNSRecordTypeUnique:        return ("Unique       ");
+		case kDNSRecordTypeAdvisory:      return ("Advisory     ");
+		case kDNSRecordTypeShared:        return ("Shared       ");
+		case kDNSRecordTypeVerified:      return ("Verified     ");
+		case kDNSRecordTypeKnownUnique:   return ("KnownUnique  ");
+		default: return("Unknown");
+		}
+	}
+
+mDNSlocal void LogEtcHosts(mDNS *const m)
+	{
+	mDNSBool showheader = mDNStrue;
+	const AuthRecord *ar;
+	mDNSu32 slot;
+	AuthGroup *ag;
+	int count = 0;
+	int authslot = 0;
+	mDNSBool truncated = 0;
+
+	for (slot = 0; slot < AUTH_HASH_SLOTS; slot++)
+		{
+		if (m->rrauth.rrauth_hash[slot]) authslot++;
+		for (ag = m->rrauth.rrauth_hash[slot]; ag; ag = ag->next)
+			for (ar = ag->members; ar; ar = ar->next)
+				{
+				if (ar->RecordCallback != FreeEtcHosts) continue;
+				if (showheader) { showheader = mDNSfalse; LogMsgNoIdent("  State       Interface"); }
+		
+				// Print a maximum of 50 records
+				if (count++ >= 50) { truncated = mDNStrue; continue; }
+				if (ar->ARType == AuthRecordLocalOnly)
+					{
+					if (ar->resrec.InterfaceID == mDNSInterface_LocalOnly)
+						LogMsgNoIdent(" %s   LO %s", RecordTypeName(ar->resrec.RecordType), ARDisplayString(m, ar));
+					else
+						{
+						mDNSu32 scopeid  = (mDNSu32)(uintptr_t)ar->resrec.InterfaceID;
+						LogMsgNoIdent(" %s   %u  %s", RecordTypeName(ar->resrec.RecordType), scopeid, ARDisplayString(m, ar));
+						}
+					}
+				usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000);
+				}
+		}
+
+	if (showheader) LogMsgNoIdent("<None>");
+	else if (truncated) LogMsgNoIdent("<Truncated: to 50 records, Total records %d, Total Auth Groups %d, Auth Slots %d>", count, m->rrauth.rrauth_totalused, authslot);
+	}
+
+mDNSlocal void LogLocalOnlyAuthRecords(mDNS *const m)
+	{
+	mDNSBool showheader = mDNStrue;
+	const AuthRecord *ar;
+	mDNSu32 slot;
+	AuthGroup *ag;
+
+	for (slot = 0; slot < AUTH_HASH_SLOTS; slot++)
+		{
+		for (ag = m->rrauth.rrauth_hash[slot]; ag; ag = ag->next)
+			for (ar = ag->members; ar; ar = ar->next)
+				{
+				if (ar->RecordCallback == FreeEtcHosts) continue;
+				if (showheader) { showheader = mDNSfalse; LogMsgNoIdent("  State       Interface"); }
+		
+				// Print a maximum of 400 records
+				if (ar->ARType == AuthRecordLocalOnly)
+					LogMsgNoIdent(" %s   LO %s", RecordTypeName(ar->resrec.RecordType), ARDisplayString(m, ar));
+				else if (ar->ARType == AuthRecordP2P)
+					LogMsgNoIdent(" %s   PP %s", RecordTypeName(ar->resrec.RecordType), ARDisplayString(m, ar));
+				usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000);
+				}
+		}
+
+	if (showheader) LogMsgNoIdent("<None>");
+	}
+
+mDNSlocal void LogAuthRecords(mDNS *const m, const mDNSs32 now, AuthRecord *ResourceRecords, int *proxy)
+	{
+	mDNSBool showheader = mDNStrue;
+	const AuthRecord *ar;
+	OwnerOptData owner = zeroOwner;
+	for (ar = ResourceRecords; ar; ar=ar->next)
+		{
+		const char *const ifname = InterfaceNameForID(m, ar->resrec.InterfaceID);
+		if ((ar->WakeUp.HMAC.l[0] != 0) == (proxy != mDNSNULL))
+			{
+			if (showheader) { showheader = mDNSfalse; LogMsgNoIdent("    Int    Next  Expire   State"); }
+			if (proxy) (*proxy)++;
+			if (!mDNSPlatformMemSame(&owner, &ar->WakeUp, sizeof(owner)))
+				{
+				owner = ar->WakeUp;
+				if (owner.password.l[0])
+					LogMsgNoIdent("Proxying for H-MAC %.6a I-MAC %.6a Password %.6a seq %d", &owner.HMAC, &owner.IMAC, &owner.password, owner.seq);
+				else if (!mDNSSameEthAddress(&owner.HMAC, &owner.IMAC))
+					LogMsgNoIdent("Proxying for H-MAC %.6a I-MAC %.6a seq %d",               &owner.HMAC, &owner.IMAC,                  owner.seq);
+				else
+					LogMsgNoIdent("Proxying for %.6a seq %d",                                &owner.HMAC,                               owner.seq);
+				}
+			if (AuthRecord_uDNS(ar))
+				LogMsgNoIdent("%7d %7d %7d %7d %s",
+					ar->ThisAPInterval / mDNSPlatformOneSecond,
+					(ar->LastAPTime + ar->ThisAPInterval - now) / mDNSPlatformOneSecond,
+					ar->expire ? (ar->expire - now) / mDNSPlatformOneSecond : 0,
+					ar->state, ARDisplayString(m, ar));
+			else if (ar->ARType == AuthRecordLocalOnly)
+				LogMsgNoIdent("                             LO %s", ARDisplayString(m, ar));
+			else if (ar->ARType == AuthRecordP2P)
+				LogMsgNoIdent("                             PP %s", ARDisplayString(m, ar));
+			else
+				LogMsgNoIdent("%7d %7d %7d %7s %s",
+					ar->ThisAPInterval / mDNSPlatformOneSecond,
+					ar->AnnounceCount ? (ar->LastAPTime + ar->ThisAPInterval - now) / mDNSPlatformOneSecond : 0,
+					ar->TimeExpire    ? (ar->TimeExpire                      - now) / mDNSPlatformOneSecond : 0,
+					ifname ? ifname : "ALL",
+					ARDisplayString(m, ar));
+			usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000);
+			}
+		}
+	if (showheader) LogMsgNoIdent("<None>");
+	}
+
+mDNSexport void udsserver_info(mDNS *const m)
+	{
+	const mDNSs32 now = mDNS_TimeNow(m);
+	mDNSu32 CacheUsed = 0, CacheActive = 0, slot;
+	int ProxyA = 0, ProxyD = 0;
+	const CacheGroup *cg;
+	const CacheRecord *cr;
+	const DNSQuestion *q;
+	const DNameListElem *d;
+	const SearchListElem *s;
+
+	LogMsgNoIdent("Timenow 0x%08lX (%d)", (mDNSu32)now, now);
+
+	LogMsgNoIdent("------------ Cache -------------");
+	LogMsgNoIdent("Slt Q     TTL if     U Type rdlen");
+	for (slot = 0; slot < CACHE_HASH_SLOTS; slot++)
+		for (cg = m->rrcache_hash[slot]; cg; cg=cg->next)
+			{
+			CacheUsed++;	// Count one cache entity for the CacheGroup object
+			for (cr = cg->members; cr; cr=cr->next)
+				{
+				const mDNSs32 remain = cr->resrec.rroriginalttl - (now - cr->TimeRcvd) / mDNSPlatformOneSecond;
+				const char *ifname;
+				mDNSInterfaceID InterfaceID = cr->resrec.InterfaceID;
+				if (!InterfaceID && cr->resrec.rDNSServer)
+					InterfaceID = cr->resrec.rDNSServer->interface;
+				ifname = InterfaceNameForID(m, InterfaceID);
+				CacheUsed++;
+				if (cr->CRActiveQuestion) CacheActive++;
+				LogMsgNoIdent("%3d %s%8ld %-7s%s %-6s%s",
+					slot,
+					cr->CRActiveQuestion ? "*" : " ",
+					remain,
+					ifname ? ifname : "-U-",
+					(cr->resrec.RecordType == kDNSRecordTypePacketNegative)  ? "-" :
+					(cr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) ? " " : "+",
+					DNSTypeName(cr->resrec.rrtype),
+					CRDisplayString(m, cr));
+				usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000);
+				}
+			}
+
+	if (m->rrcache_totalused != CacheUsed)
+		LogMsgNoIdent("Cache use mismatch: rrcache_totalused is %lu, true count %lu", m->rrcache_totalused, CacheUsed);
+	if (m->rrcache_active != CacheActive)
+		LogMsgNoIdent("Cache use mismatch: rrcache_active is %lu, true count %lu", m->rrcache_active, CacheActive);
+	LogMsgNoIdent("Cache currently contains %lu entities; %lu referenced by active questions", CacheUsed, CacheActive);
+
+	LogMsgNoIdent("--------- Auth Records ---------");
+	LogAuthRecords(m, now, m->ResourceRecords, mDNSNULL);
+
+	LogMsgNoIdent("--------- LocalOnly, P2P Auth Records ---------");
+	LogLocalOnlyAuthRecords(m);
+
+	LogMsgNoIdent("--------- /etc/hosts ---------");
+	LogEtcHosts(m);
+
+	LogMsgNoIdent("------ Duplicate Records -------");
+	LogAuthRecords(m, now, m->DuplicateRecords, mDNSNULL);
+
+	LogMsgNoIdent("----- Auth Records Proxied -----");
+	LogAuthRecords(m, now, m->ResourceRecords, &ProxyA);
+
+	LogMsgNoIdent("-- Duplicate Records Proxied ---");
+	LogAuthRecords(m, now, m->DuplicateRecords, &ProxyD);
+
+	LogMsgNoIdent("---------- Questions -----------");
+	if (!m->Questions) LogMsgNoIdent("<None>");
+	else
+		{
+		CacheUsed = 0;
+		CacheActive = 0;
+		LogMsgNoIdent("   Int  Next if     T  NumAns VDNS    Qptr     DupOf    SU SQ Type Name");
+		for (q = m->Questions; q; q=q->next)
+			{
+			mDNSs32 i = q->ThisQInterval / mDNSPlatformOneSecond;
+			mDNSs32 n = (NextQSendTime(q) - now) / mDNSPlatformOneSecond;
+			char *ifname = InterfaceNameForID(m, q->InterfaceID);
+			CacheUsed++;
+			if (q->ThisQInterval) CacheActive++;
+			LogMsgNoIdent("%6d%6d %-7s%s%s %5d 0x%x%x 0x%p 0x%p %1d %2d %-5s%##s%s",
+				i, n,
+				ifname ? ifname : mDNSOpaque16IsZero(q->TargetQID) ? "" : "-U-",
+				mDNSOpaque16IsZero(q->TargetQID) ? (q->LongLived ? "l" : " ") : (q->LongLived ? "L" : "O"),
+				PrivateQuery(q)    ? "P" : " ",
+				q->CurrentAnswers, q->validDNSServers.l[1], q->validDNSServers.l[0], q, q->DuplicateOf,
+				q->SuppressUnusable, q->SuppressQuery, DNSTypeName(q->qtype), q->qname.c, q->DuplicateOf ? " (dup)" : "");
+			usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000);
+			}
+		LogMsgNoIdent("%lu question%s; %lu active", CacheUsed, CacheUsed > 1 ? "s" : "", CacheActive);
+		}
+
+	LogMsgNoIdent("----- Local-Only Questions -----");
+	if (!m->LocalOnlyQuestions) LogMsgNoIdent("<None>");
+	else for (q = m->LocalOnlyQuestions; q; q=q->next)
+		LogMsgNoIdent("                       %5d  %-6s%##s%s",
+			q->CurrentAnswers, DNSTypeName(q->qtype), q->qname.c, q->DuplicateOf ? " (dup)" : "");
+
+	LogMsgNoIdent("---- Active Client Requests ----");
+	if (!all_requests) LogMsgNoIdent("<None>");
+	else
+		{
+		const request_state *req, *r;
+		for (req = all_requests; req; req=req->next)
+			{
+			if (req->primary)	// If this is a subbordinate operation, check that the parent is in the list
+				{
+				for (r = all_requests; r && r != req; r=r->next) if (r == req->primary) goto foundparent;
+				LogMsgNoIdent("%3d: Orhpan operation %p; parent %p not found in request list", req->sd);
+				}
+			// For non-subbordinate operations, and subbordinate operations that have lost their parent, write out their info
+			LogClientInfo(m, req);
+			foundparent:;
+			}
+		}
+
+	LogMsgNoIdent("-------- NAT Traversals --------");
+	if (!m->NATTraversals) LogMsgNoIdent("<None>");
+	else
+		{
+		const NATTraversalInfo *nat;
+		for (nat = m->NATTraversals; nat; nat=nat->next)
+			{
+			if (nat->Protocol)
+				LogMsgNoIdent("%p %s Int %5d Ext %5d Err %d Retry %5d Interval %5d Expire %5d",
+					nat, nat->Protocol == NATOp_MapTCP ? "TCP" : "UDP",
+					mDNSVal16(nat->IntPort), mDNSVal16(nat->ExternalPort), nat->Result,
+					nat->retryPortMap ? (nat->retryPortMap - now) / mDNSPlatformOneSecond : 0,
+					nat->retryInterval / mDNSPlatformOneSecond,
+					nat->ExpiryTime ? (nat->ExpiryTime - now) / mDNSPlatformOneSecond : 0);
+			else
+				LogMsgNoIdent("%p Address Request               Retry %5d Interval %5d", nat,
+					(m->retryGetAddr - now) / mDNSPlatformOneSecond,
+					m->retryIntervalGetAddr / mDNSPlatformOneSecond);
+			usleep((m->KnownBugs & mDNS_KnownBug_LossySyslog) ? 3333 : 1000);
+			}
+		}
+
+	LogMsgNoIdent("--------- AuthInfoList ---------");
+	if (!m->AuthInfoList) LogMsgNoIdent("<None>");
+	else
+		{
+		const DomainAuthInfo *a;
+		for (a = m->AuthInfoList; a; a = a->next)
+			LogMsgNoIdent("%##s %##s %##s %d %s", a->domain.c, a->keyname.c, a->hostname.c, (a->port.b[0] << 8 | a->port.b[1]), a->AutoTunnel ? a->AutoTunnel : "");
+		}
+
+	#if APPLE_OSX_mDNSResponder
+	LogMsgNoIdent("--------- TunnelClients --------");
+	if (!m->TunnelClients) LogMsgNoIdent("<None>");
+	else
+		{
+		const ClientTunnel *c;
+		for (c = m->TunnelClients; c; c = c->next)
+			LogMsgNoIdent("%s %##s local %.16a %.4a %.16a remote %.16a %.4a %5d %.16a interval %d",
+				c->prefix, c->dstname.c, &c->loc_inner, &c->loc_outer, &c->loc_outer6, &c->rmt_inner, &c->rmt_outer, mDNSVal16(c->rmt_outer_port), &c->rmt_outer6, c->q.ThisQInterval);
+		}
+	#endif // APPLE_OSX_mDNSResponder
+
+	LogMsgNoIdent("---------- Misc State ----------");
+
+	LogMsgNoIdent("PrimaryMAC:   %.6a", &m->PrimaryMAC);
+
+	LogMsgNoIdent("m->SleepState %d (%s) seq %d",
+		m->SleepState,
+		m->SleepState == SleepState_Awake        ? "Awake"        :
+		m->SleepState == SleepState_Transferring ? "Transferring" : 
+		m->SleepState == SleepState_Sleeping     ? "Sleeping"     : "?",
+		m->SleepSeqNum);
+
+	if (!m->SPSSocket) LogMsgNoIdent("Not offering Sleep Proxy Service");
+	else LogMsgNoIdent("Offering Sleep Proxy Service: %#s", m->SPSRecords.RR_SRV.resrec.name->c);
+
+	if (m->ProxyRecords == ProxyA + ProxyD) LogMsgNoIdent("ProxyRecords: %d + %d = %d", ProxyA, ProxyD, ProxyA + ProxyD);
+	else LogMsgNoIdent("ProxyRecords: MISMATCH %d + %d = %d ≠ %d", ProxyA, ProxyD, ProxyA + ProxyD, m->ProxyRecords);
+
+	LogMsgNoIdent("------ Auto Browse Domains -----");
+	if (!AutoBrowseDomains) LogMsgNoIdent("<None>");
+	else for (d=AutoBrowseDomains; d; d=d->next) LogMsgNoIdent("%##s", d->name.c);
+
+	LogMsgNoIdent("--- Auto Registration Domains --");
+	if (!AutoRegistrationDomains) LogMsgNoIdent("<None>");
+	else for (d=AutoRegistrationDomains; d; d=d->next) LogMsgNoIdent("%##s", d->name.c);
+
+ 	LogMsgNoIdent("--- Search Domains --");
+ 	if (!SearchList) LogMsgNoIdent("<None>");
+ 	else
+ 		{
+ 		for (s=SearchList; s; s=s->next)
+ 			{
+ 			char *ifname = InterfaceNameForID(m, s->InterfaceID);
+ 			LogMsgNoIdent("%##s %s", s->domain.c, ifname ? ifname : "");
+ 			}
+ 		}
+
+	LogMsgNoIdent("---- Task Scheduling Timers ----");
+
+	if (!m->NewQuestions)
+		LogMsgNoIdent("NewQuestion <NONE>");
+	else
+		LogMsgNoIdent("NewQuestion DelayAnswering %d %d %##s (%s)",
+			m->NewQuestions->DelayAnswering, m->NewQuestions->DelayAnswering-now,
+			m->NewQuestions->qname.c, DNSTypeName(m->NewQuestions->qtype));
+
+	if (!m->NewLocalOnlyQuestions)
+		LogMsgNoIdent("NewLocalOnlyQuestions <NONE>");
+	else
+		LogMsgNoIdent("NewLocalOnlyQuestions %##s (%s)",
+			m->NewLocalOnlyQuestions->qname.c, DNSTypeName(m->NewLocalOnlyQuestions->qtype));
+
+	if (!m->NewLocalRecords)
+		LogMsgNoIdent("NewLocalRecords <NONE>");
+	else
+		LogMsgNoIdent("NewLocalRecords %02X %s", m->NewLocalRecords->resrec.RecordType, ARDisplayString(m, m->NewLocalRecords));
+
+	LogMsgNoIdent("SPSProxyListChanged%s", m->SPSProxyListChanged ? "" : " <NONE>");
+	LogMsgNoIdent("LocalRemoveEvents%s",   m->LocalRemoveEvents   ? "" : " <NONE>");
+	LogMsgNoIdent("m->RegisterAutoTunnel6  %08X", m->RegisterAutoTunnel6);
+	LogMsgNoIdent("m->AutoTunnelRelayAddrIn  %.16a", &m->AutoTunnelRelayAddrIn);
+	LogMsgNoIdent("m->AutoTunnelRelayAddrOut  %.16a", &m->AutoTunnelRelayAddrOut);
+
+#define LogTimer(MSG,T) LogMsgNoIdent( MSG " %08X %11d  %08X %11d", (T), (T), (T)-now, (T)-now)
+
+	LogMsgNoIdent("                         ABS (hex)  ABS (dec)  REL (hex)  REL (dec)");
+	LogMsgNoIdent("m->timenow               %08X %11d", now, now);
+	LogMsgNoIdent("m->timenow_adjust        %08X %11d", m->timenow_adjust, m->timenow_adjust);
+	LogTimer("m->NextScheduledEvent   ", m->NextScheduledEvent);
+
+#ifndef UNICAST_DISABLED
+	LogTimer("m->NextuDNSEvent        ", m->NextuDNSEvent);
+	LogTimer("m->NextSRVUpdate        ", m->NextSRVUpdate);
+	LogTimer("m->NextScheduledNATOp   ", m->NextScheduledNATOp);
+	LogTimer("m->retryGetAddr         ", m->retryGetAddr);
+#endif
+
+	LogTimer("m->NextCacheCheck       ", m->NextCacheCheck);
+	LogTimer("m->NextScheduledSPS     ", m->NextScheduledSPS);
+	LogTimer("m->NextScheduledSPRetry ", m->NextScheduledSPRetry);
+	LogTimer("m->DelaySleep           ", m->DelaySleep);
+
+	LogTimer("m->NextScheduledQuery   ", m->NextScheduledQuery);
+	LogTimer("m->NextScheduledProbe   ", m->NextScheduledProbe);
+	LogTimer("m->NextScheduledResponse", m->NextScheduledResponse);
+
+	LogTimer("m->SuppressSending      ", m->SuppressSending);
+	LogTimer("m->SuppressProbes       ", m->SuppressProbes);
+	LogTimer("m->ProbeFailTime        ", m->ProbeFailTime);
+	LogTimer("m->DelaySleep           ", m->DelaySleep);
+	LogTimer("m->SleepLimit           ", m->SleepLimit);
+	LogTimer("m->NextScheduledStopTime ", m->NextScheduledStopTime);
+	}
+
+#if APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING
+mDNSexport void uds_validatelists(void)
+	{
+	const request_state *req, *p;
+	for (req = all_requests; req; req=req->next)
+		{
+		if (req->next == (request_state *)~0 || (req->sd < 0 && req->sd != -2))
+			LogMemCorruption("UDS request list: %p is garbage (%d)", req, req->sd);
+
+		if (req->primary == req)
+			LogMemCorruption("UDS request list: req->primary should not point to self %p/%d", req, req->sd);
+
+		if (req->primary && req->replies)
+			LogMemCorruption("UDS request list: Subordinate request %p/%d/%p should not have replies (%p)",
+				req, req->sd, req->primary && req->replies);
+
+		p = req->primary;
+		if ((long)p & 3)
+			LogMemCorruption("UDS request list: req %p primary %p is misaligned (%d)", req, p, req->sd);
+		else if (p && (p->next == (request_state *)~0 || (p->sd < 0 && p->sd != -2)))
+			LogMemCorruption("UDS request list: req %p primary %p is garbage (%d)", req, p, p->sd);
+
+		reply_state *rep;
+		for (rep = req->replies; rep; rep=rep->next)
+		  if (rep->next == (reply_state *)~0)
+			LogMemCorruption("UDS req->replies: %p is garbage", rep);
+
+		if (req->terminate == connection_termination)
+			{
+			registered_record_entry *r;
+			for (r = req->u.reg_recs; r; r=r->next)
+				if (r->next == (registered_record_entry *)~0)
+					LogMemCorruption("UDS req->u.reg_recs: %p is garbage", r);
+			}
+		else if (req->terminate == regservice_termination_callback)
+			{
+			service_instance *s;
+			for (s = req->u.servicereg.instances; s; s=s->next)
+				if (s->next == (service_instance *)~0)
+					LogMemCorruption("UDS req->u.servicereg.instances: %p is garbage", s);
+			}
+		else if (req->terminate == browse_termination_callback)
+			{
+			browser_t *b;
+			for (b = req->u.browser.browsers; b; b=b->next)
+				if (b->next == (browser_t *)~0)
+					LogMemCorruption("UDS req->u.browser.browsers: %p is garbage", b);
+			}
+		}
+
+	DNameListElem *d;
+	for (d = SCPrefBrowseDomains; d; d=d->next)
+		if (d->next == (DNameListElem *)~0 || d->name.c[0] > 63)
+			LogMemCorruption("SCPrefBrowseDomains: %p is garbage (%d)", d, d->name.c[0]);
+
+	ARListElem *b;
+	for (b = LocalDomainEnumRecords; b; b=b->next)
+		if (b->next == (ARListElem *)~0 || b->ar.resrec.name->c[0] > 63)
+			LogMemCorruption("LocalDomainEnumRecords: %p is garbage (%d)", b, b->ar.resrec.name->c[0]);
+
+	for (d = AutoBrowseDomains; d; d=d->next)
+		if (d->next == (DNameListElem *)~0 || d->name.c[0] > 63)
+			LogMemCorruption("AutoBrowseDomains: %p is garbage (%d)", d, d->name.c[0]);
+
+	for (d = AutoRegistrationDomains; d; d=d->next)
+		if (d->next == (DNameListElem *)~0 || d->name.c[0] > 63)
+			LogMemCorruption("AutoRegistrationDomains: %p is garbage (%d)", d, d->name.c[0]);
+	}
+#endif // APPLE_OSX_mDNSResponder && MACOSX_MDNS_MALLOC_DEBUGGING
+
+mDNSlocal int send_msg(request_state *const req)
+	{
+	reply_state *const rep = req->replies;		// Send the first waiting reply
+	ssize_t nwriten;
+	if (req->no_reply) return(t_complete);
+
+	ConvertHeaderBytes(rep->mhdr);
+	nwriten = send(req->sd, (char *)&rep->mhdr + rep->nwriten, rep->totallen - rep->nwriten, 0);
+	ConvertHeaderBytes(rep->mhdr);
+
+	if (nwriten < 0)
+		{
+		if (dnssd_errno == dnssd_EINTR || dnssd_errno == dnssd_EWOULDBLOCK) nwriten = 0;
+		else
+			{
+#if !defined(PLATFORM_NO_EPIPE)
+			if (dnssd_errno == EPIPE)
+				return(req->ts = t_terminated);
+			else
+#endif
+				{
+				LogMsg("send_msg ERROR: failed to write %d of %d bytes to fd %d errno %d (%s)",
+					rep->totallen - rep->nwriten, rep->totallen, req->sd, dnssd_errno, dnssd_strerror(dnssd_errno));
+				return(t_error);
+				}
+			}
+		}
+	rep->nwriten += nwriten;
+	return (rep->nwriten == rep->totallen) ? t_complete : t_morecoming;
+	}
+
+mDNSexport mDNSs32 udsserver_idle(mDNSs32 nextevent)
+	{
+	mDNSs32 now = mDNS_TimeNow(&mDNSStorage);
+	request_state **req = &all_requests;
+
+	while (*req)
+		{
+		request_state *const r = *req;
+
+		if (r->terminate == resolve_termination_callback)
+			if (r->u.resolve.ReportTime && now - r->u.resolve.ReportTime >= 0)
+				{
+				r->u.resolve.ReportTime = 0;
+				LogMsgNoIdent("Client application bug: DNSServiceResolve(%##s) active for over two minutes. "
+					"This places considerable burden on the network.", r->u.resolve.qsrv.qname.c);
+				}
+
+		// Note: Only primary req's have reply lists, not subordinate req's.
+		while (r->replies)		// Send queued replies
+			{
+			transfer_state result;
+			if (r->replies->next) r->replies->rhdr->flags |= dnssd_htonl(kDNSServiceFlagsMoreComing);
+			result = send_msg(r);	// Returns t_morecoming if buffer full because client is not reading
+			if (result == t_complete)
+				{
+				reply_state *fptr = r->replies;
+				r->replies = r->replies->next;
+				freeL("reply_state/udsserver_idle", fptr);
+				r->time_blocked = 0; // reset failure counter after successful send
+				r->unresponsiveness_reports = 0;
+				continue;
+				}
+			else if (result == t_terminated || result == t_error)
+				{
+				LogMsg("%3d: Could not write data to client because of error - aborting connection", r->sd);
+				LogClientInfo(&mDNSStorage, r);
+				abort_request(r);
+				}
+			break;
+			}
+
+		if (r->replies)		// If we failed to send everything, check our time_blocked timer
+			{
+			if (nextevent - now > mDNSPlatformOneSecond) nextevent = now + mDNSPlatformOneSecond;
+
+			if (mDNSStorage.SleepState != SleepState_Awake) r->time_blocked = 0;
+			else if (!r->time_blocked) r->time_blocked = NonZeroTime(now);
+			else if (now - r->time_blocked >= 10 * mDNSPlatformOneSecond * (r->unresponsiveness_reports+1))
+				{
+				int num = 0;
+				struct reply_state *x = r->replies;
+				while (x) { num++; x=x->next; }
+				LogMsg("%3d: Could not write data to client after %ld seconds, %d repl%s waiting",
+					r->sd, (now - r->time_blocked) / mDNSPlatformOneSecond, num, num == 1 ? "y" : "ies");
+				if (++r->unresponsiveness_reports >= 60)
+					{
+					LogMsg("%3d: Client unresponsive; aborting connection", r->sd);
+					LogClientInfo(&mDNSStorage, r);
+					abort_request(r);
+					}
+				}
+			}
+
+		if (!dnssd_SocketValid(r->sd)) // If this request is finished, unlink it from the list and free the memory
+			{
+			// Since we're already doing a list traversal, we unlink the request directly instead of using AbortUnlinkAndFree()
+			*req = r->next;
+			freeL("request_state/udsserver_idle", r);
+			}
+		else
+			req = &r->next;
+		}
+	return nextevent;
+	}
+
+struct CompileTimeAssertionChecks_uds_daemon
+	{
+	// Check our structures are reasonable sizes. Including overly-large buffers, or embedding
+	// other overly-large structures instead of having a pointer to them, can inadvertently
+	// cause structure sizes (and therefore memory usage) to balloon unreasonably.
+	char sizecheck_request_state          [(sizeof(request_state)           <= 1784) ? 1 : -1];
+	char sizecheck_registered_record_entry[(sizeof(registered_record_entry) <=   60) ? 1 : -1];
+	char sizecheck_service_instance       [(sizeof(service_instance)        <= 6552) ? 1 : -1];
+	char sizecheck_browser_t              [(sizeof(browser_t)               <= 1050) ? 1 : -1];
+	char sizecheck_reply_hdr              [(sizeof(reply_hdr)               <=   12) ? 1 : -1];
+	char sizecheck_reply_state            [(sizeof(reply_state)             <=   64) ? 1 : -1];
+	};
diff --git a/mdnsresponder/mDNSShared/uds_daemon.h b/mdnsresponder/mDNSShared/uds_daemon.h
new file mode 100644
index 0000000..1e17efa
--- /dev/null
+++ b/mdnsresponder/mDNSShared/uds_daemon.h
@@ -0,0 +1,80 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2003 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+
+ 	File:		uds_daemon.h
+
+ 	Contains:	Interfaces necessary to talk to uds_daemon.c.
+
+ 	Version:	1.0
+
+ */
+
+#include "mDNSEmbeddedAPI.h"
+#include "dnssd_ipc.h"
+
+/* Client interface: */
+
+#define SRS_PORT(S) mDNSVal16((S)->RR_SRV.resrec.rdata->u.srv.port)
+
+extern int udsserver_init(dnssd_sock_t skts[], mDNSu32 count);
+extern mDNSs32 udsserver_idle(mDNSs32 nextevent);
+extern void udsserver_info(mDNS *const m);	// print out info about current state
+extern void udsserver_handle_configchange(mDNS *const m);
+extern int udsserver_exit(void);	// should be called prior to app exit
+
+/* Routines that uds_daemon expects to link against: */
+
+typedef	void (*udsEventCallback)(int fd, short filter, void *context);
+extern mStatus udsSupportAddFDToEventLoop(dnssd_sock_t fd, udsEventCallback callback, void *context, void **platform_data);
+extern int     udsSupportReadFD(dnssd_sock_t fd, char* buf, int len, int flags, void *platform_data);
+extern mStatus udsSupportRemoveFDFromEventLoop(dnssd_sock_t fd, void *platform_data); // Note: This also CLOSES the file descriptor as well
+
+extern void RecordUpdatedNiceLabel(mDNS *const m, mDNSs32 delay);
+
+// Globals and functions defined in uds_daemon.c and also shared with the old "daemon.c" on OS X
+
+extern mDNS mDNSStorage;
+extern DNameListElem *AutoRegistrationDomains;
+extern DNameListElem *AutoBrowseDomains;
+
+extern mDNSs32 ChopSubTypes(char *regtype);
+extern AuthRecord *AllocateSubTypes(mDNSs32 NumSubTypes, char *p);
+extern int CountExistingRegistrations(domainname *srv, mDNSIPPort port);
+extern void FreeExtraRR(mDNS *const m, AuthRecord *const rr, mStatus result);
+extern int CountPeerRegistrations(mDNS *const m, ServiceRecordSet *const srs);
+
+#if APPLE_OSX_mDNSResponder
+extern void machserver_automatic_browse_domain_changed(const domainname *d, mDNSBool add);
+extern void machserver_automatic_registration_domain_changed(const domainname *d, mDNSBool add);
+// External support
+extern void mDNSInitPacketFilter(void);
+extern void external_start_browsing_for_service(mDNS *const m, const domainname *const type, DNS_TypeValues qtype);
+extern void external_stop_browsing_for_service(mDNS *const m, const domainname *const type, DNS_TypeValues qtype);
+extern void external_start_advertising_service(const ResourceRecord *const resourceRecord);
+extern void external_stop_advertising_service(const ResourceRecord *const resourceRecord);
+extern void external_start_resolving_service(const domainname *const fqdn);
+extern void external_stop_resolving_service(const domainname *const fqdn);
+#else
+#define external_start_browsing_for_service(A,B,C) (void)(A)
+#define external_stop_browsing_for_service(A,B,C)  (void)(A)
+#define external_start_advertising_service(A)      (void)(A)
+#define external_stop_advertising_service(A)       (void)(A)
+#define external_start_resolving_service(A)        (void)(A)
+#define external_stop_resolving_service(A)         (void)(A)
+#endif // APPLE_OSX_mDNSResponder
+
+extern const char mDNSResponderVersionString_SCCS[];
+#define mDNSResponderVersionString (mDNSResponderVersionString_SCCS+5)
diff --git a/mdnsresponder/mDNSWindows/ControlPanel/BrowsingPage.cpp b/mdnsresponder/mDNSWindows/ControlPanel/BrowsingPage.cpp
new file mode 100755
index 0000000..20b5c6d
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/ControlPanel/BrowsingPage.cpp
@@ -0,0 +1,441 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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 "BrowsingPage.h"
+#include "resource.h"
+
+#include "ConfigPropertySheet.h"
+
+#include <WinServices.h>
+    
+#define MAX_KEY_LENGTH 255
+
+
+IMPLEMENT_DYNCREATE(CBrowsingPage, CPropertyPage)
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CBrowsingPage::CBrowsingPage
+//---------------------------------------------------------------------------------------------------------------------------
+
+CBrowsingPage::CBrowsingPage()
+:
+	CPropertyPage(CBrowsingPage::IDD)
+{
+	//{{AFX_DATA_INIT(CBrowsingPage)
+	//}}AFX_DATA_INIT
+
+	m_firstTime = true;
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CBrowsingPage::~CBrowsingPage
+//---------------------------------------------------------------------------------------------------------------------------
+
+CBrowsingPage::~CBrowsingPage()
+{
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CBrowsingPage::DoDataExchange
+//---------------------------------------------------------------------------------------------------------------------------
+
+void CBrowsingPage::DoDataExchange(CDataExchange* pDX)
+{
+	CPropertyPage::DoDataExchange(pDX);
+	//{{AFX_DATA_MAP(CBrowsingPage)
+	//}}AFX_DATA_MAP
+	DDX_Control(pDX, IDC_BROWSE_LIST, m_browseListCtrl);
+	DDX_Control(pDX, IDC_REMOVE_BROWSE_DOMAIN, m_removeButton);
+}
+
+BEGIN_MESSAGE_MAP(CBrowsingPage, CPropertyPage)
+	//{{AFX_MSG_MAP(CBrowsingPage)
+	//}}AFX_MSG_MAP
+	ON_BN_CLICKED(IDC_ADD_BROWSE_DOMAIN, OnBnClickedAddBrowseDomain)
+	ON_BN_CLICKED(IDC_REMOVE_BROWSE_DOMAIN, OnBnClickedRemoveBrowseDomain)
+	ON_NOTIFY(LVN_ITEMCHANGED, IDC_BROWSE_LIST, OnLvnItemchangedBrowseList)
+END_MESSAGE_MAP()
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CBrowsingPage::SetModified
+//---------------------------------------------------------------------------------------------------------------------------
+
+void CBrowsingPage::SetModified( BOOL bChanged )
+{
+	m_modified = bChanged;
+
+	CPropertyPage::SetModified( bChanged );
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CBrowsingPage::OnSetActive
+//---------------------------------------------------------------------------------------------------------------------------
+
+BOOL
+CBrowsingPage::OnSetActive()
+{
+	CConfigPropertySheet	*	psheet;
+	HKEY						key = NULL;
+	HKEY						subKey = NULL;
+	DWORD						dwSize;
+	DWORD						enabled;
+	DWORD						err;
+	TCHAR						subKeyName[MAX_KEY_LENGTH];
+	DWORD						cSubKeys = 0;
+	DWORD						cbMaxSubKey;
+	DWORD						cchMaxClass;
+	int							nIndex;
+    DWORD						i; 
+	BOOL						b = CPropertyPage::OnSetActive();
+
+	psheet = reinterpret_cast<CConfigPropertySheet*>(GetParent());
+	require_quiet( psheet, exit );
+	
+	m_modified = FALSE;
+
+	if ( m_firstTime )
+	{
+		m_browseListCtrl.SetExtendedStyle((m_browseListCtrl.GetStyle() & (~LVS_EX_GRIDLINES))|LVS_EX_CHECKBOXES | LVS_EX_FULLROWSELECT);
+
+		m_browseListCtrl.InsertColumn(0, L"", LVCFMT_LEFT, 20 );
+		m_browseListCtrl.InsertColumn(1, L"", LVCFMT_LEFT, 345);
+
+		m_firstTime = false;
+	}
+
+	m_initialized = false;
+
+	// Clear out what's there
+
+	m_browseListCtrl.DeleteAllItems();
+
+	// Now populate the browse domain box
+
+	err = RegCreateKeyEx( HKEY_LOCAL_MACHINE, kServiceParametersNode L"\\DynDNS\\Setup\\" kServiceDynDNSBrowseDomains, 0,
+		                  NULL, REG_OPTION_NON_VOLATILE, KEY_READ|KEY_WRITE|KEY_WOW64_32KEY, NULL, &key, NULL );
+	require_noerr( err, exit );
+
+	// Get information about this node
+
+    err = RegQueryInfoKey( key, NULL, NULL, NULL, &cSubKeys, &cbMaxSubKey, &cchMaxClass, NULL, NULL, NULL, NULL, NULL );       
+	require_noerr( err, exit );
+
+	for ( i = 0; i < cSubKeys; i++)
+	{	
+		dwSize = MAX_KEY_LENGTH;
+            
+		err = RegEnumKeyEx( key, i, subKeyName, &dwSize, NULL, NULL, NULL, NULL );
+		require_noerr( err, exit );
+
+		err = RegOpenKey( key, subKeyName, &subKey );
+		require_noerr( err, exit );
+
+		dwSize = sizeof( DWORD );
+		err = RegQueryValueEx( subKey, L"Enabled", NULL, NULL, (LPBYTE) &enabled, &dwSize );
+		require_noerr( err, exit );
+
+		nIndex = m_browseListCtrl.InsertItem( m_browseListCtrl.GetItemCount(), L"");
+		m_browseListCtrl.SetItemText( nIndex, 1, subKeyName );
+		m_browseListCtrl.SetCheck( nIndex, enabled );
+
+		RegCloseKey( subKey );
+		subKey = NULL;
+    }
+
+	m_browseListCtrl.SortItems( SortFunc, (DWORD_PTR) this );
+
+	m_removeButton.EnableWindow( FALSE );
+ 
+exit:
+
+	if ( subKey )
+	{
+		RegCloseKey( subKey );
+	}
+
+	if ( key )
+	{
+		RegCloseKey( key );
+	}
+
+	m_initialized = true;
+
+	return b;
+}
+
+ 
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CBrowsingPage::OnOK
+//---------------------------------------------------------------------------------------------------------------------------
+
+void
+CBrowsingPage::OnOK()
+{
+	if ( m_modified )
+	{
+		Commit();
+	}
+}
+
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CBrowsingPage::Commit
+//---------------------------------------------------------------------------------------------------------------------------
+
+void
+CBrowsingPage::Commit()
+{
+	HKEY		key		= NULL;
+	HKEY		subKey	= NULL;
+	TCHAR		subKeyName[MAX_KEY_LENGTH];
+	DWORD		cSubKeys = 0;
+	DWORD		cbMaxSubKey;
+	DWORD		cchMaxClass;
+	DWORD		dwSize;
+	int			i;
+	DWORD		err;
+
+	err = RegCreateKeyEx( HKEY_LOCAL_MACHINE, kServiceParametersNode L"\\DynDNS\\Setup\\" kServiceDynDNSBrowseDomains, 0,
+	                      NULL, REG_OPTION_NON_VOLATILE, KEY_READ|KEY_WRITE|KEY_WOW64_32KEY, NULL, &key, NULL );
+	require_noerr( err, exit );
+
+	// First, remove all the entries that are there
+
+    err = RegQueryInfoKey( key, NULL, NULL, NULL, &cSubKeys, &cbMaxSubKey, &cchMaxClass, NULL, NULL, NULL, NULL, NULL );       
+	require_noerr( err, exit );
+
+	for ( i = 0; i < (int) cSubKeys; i++ )
+	{	
+		dwSize = MAX_KEY_LENGTH;
+            
+		err = RegEnumKeyEx( key, 0, subKeyName, &dwSize, NULL, NULL, NULL, NULL );
+		require_noerr( err, exit );
+			
+		err = RegDeleteKey( key, subKeyName );
+		require_noerr( err, exit );
+	}
+
+	// Now re-populate
+
+	for ( i = 0; i < m_browseListCtrl.GetItemCount(); i++ )
+	{
+		DWORD enabled = (DWORD) m_browseListCtrl.GetCheck( i );
+
+		err = RegCreateKeyEx( key, m_browseListCtrl.GetItemText( i, 1 ), 0,
+		                      NULL, REG_OPTION_NON_VOLATILE, KEY_READ|KEY_WRITE|KEY_WOW64_32KEY, NULL, &subKey, NULL );
+		require_noerr( err, exit );
+
+		err = RegSetValueEx( subKey, L"Enabled", NULL, REG_DWORD, (LPBYTE) &enabled, sizeof( enabled ) );
+		require_noerr( err, exit );
+
+		RegCloseKey( subKey );
+		subKey = NULL;
+	}
+	
+exit:
+
+	if ( subKey )
+	{
+		RegCloseKey( subKey );
+	}
+
+	if ( key )
+	{
+		RegCloseKey( key );
+	}
+}
+
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CBrowsingPage::OnBnClickedAddBrowseDomain
+//---------------------------------------------------------------------------------------------------------------------------
+
+void
+CBrowsingPage::OnBnClickedAddBrowseDomain()
+{
+	CAddBrowseDomain dlg( GetParent() );
+
+	if ( ( dlg.DoModal() == IDOK ) && ( dlg.m_text.GetLength() > 0 ) )
+	{
+		int nIndex;
+
+		nIndex = m_browseListCtrl.InsertItem( m_browseListCtrl.GetItemCount(), L"");
+		m_browseListCtrl.SetItemText( nIndex, 1, dlg.m_text );
+		m_browseListCtrl.SetCheck( nIndex, 1 );
+
+		m_browseListCtrl.SortItems( SortFunc, (DWORD_PTR) this );
+
+		m_browseListCtrl.Invalidate();
+
+		SetModified( TRUE );
+	}
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CBrowsingPage::OnBnClickedRemoveBrowseDomain
+//---------------------------------------------------------------------------------------------------------------------------
+
+void
+CBrowsingPage::OnBnClickedRemoveBrowseDomain()
+{
+	UINT	selectedCount = m_browseListCtrl.GetSelectedCount();
+	int		nItem = -1;
+	UINT	i;
+
+	// Update all of the selected items.
+
+	for ( i = 0; i < selectedCount; i++ )
+	{
+		nItem = m_browseListCtrl.GetNextItem( -1, LVNI_SELECTED );
+		check( nItem != -1 );
+
+		m_browseListCtrl.DeleteItem( nItem );
+
+		SetModified( TRUE );
+	}
+
+	m_removeButton.EnableWindow( FALSE );
+}
+
+
+void
+CBrowsingPage::OnLvnItemchangedBrowseList(NMHDR *pNMHDR, LRESULT *pResult)
+{
+	if ( m_browseListCtrl.GetSelectedCount() )
+	{
+		m_removeButton.EnableWindow( TRUE );
+	}
+
+	if ( m_initialized )
+	{
+		NM_LISTVIEW * pNMListView = (NM_LISTVIEW*)pNMHDR; 
+	 
+		BOOL bPrevState = (BOOL) ( ( ( pNMListView->uOldState & LVIS_STATEIMAGEMASK ) >> 12 ) - 1 ); 
+	 
+		if ( bPrevState < 0 )
+		{
+			bPrevState = 0;
+		}
+
+
+		BOOL bChecked = ( BOOL ) ( ( ( pNMListView->uNewState & LVIS_STATEIMAGEMASK ) >> 12) - 1 ); 
+	 
+		if ( bChecked < 0 )
+		{
+			bChecked = 0;
+		}
+
+		if ( bPrevState != bChecked )
+		{
+			SetModified( TRUE );
+		}
+	}
+
+	*pResult = 0;
+}
+
+
+
+int CALLBACK 
+CBrowsingPage::SortFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort)
+{
+	CString str1;
+	CString	str2;
+	int		ret = 0;
+
+	CBrowsingPage * self = reinterpret_cast<CBrowsingPage*>( lParamSort );
+	require_quiet( self, exit );
+
+	str1 = self->m_browseListCtrl.GetItemText( (int) lParam1, 1 );
+	str2 = self->m_browseListCtrl.GetItemText( (int) lParam2, 1 );
+
+	ret = str1.Compare( str2 );
+
+exit:
+
+	return ret;
+}
+
+
+// CAddBrowseDomain dialog
+
+IMPLEMENT_DYNAMIC(CAddBrowseDomain, CDialog)
+CAddBrowseDomain::CAddBrowseDomain(CWnd* pParent /*=NULL*/)
+	: CDialog(CAddBrowseDomain::IDD, pParent)
+{
+}
+
+CAddBrowseDomain::~CAddBrowseDomain()
+{
+}
+
+void CAddBrowseDomain::DoDataExchange(CDataExchange* pDX)
+{
+	CDialog::DoDataExchange(pDX);
+	DDX_Control(pDX, IDC_COMBO1, m_comboBox);
+}
+
+
+BOOL
+CAddBrowseDomain::OnInitDialog()
+{
+	CConfigPropertySheet	*	psheet;
+	CConfigPropertySheet::StringList::iterator		it;
+	
+	BOOL b = CDialog::OnInitDialog();
+
+	psheet = reinterpret_cast<CConfigPropertySheet*>(GetParent());
+	require_quiet( psheet, exit );
+
+	for ( it = psheet->m_browseDomains.begin(); it != psheet->m_browseDomains.end(); it++ )
+	{
+		CString text = *it;
+
+		if ( m_comboBox.FindStringExact( -1, *it ) == CB_ERR )
+		{
+			m_comboBox.AddString( *it );
+		}
+	}
+
+exit:
+
+	return b;
+}
+
+
+void
+CAddBrowseDomain::OnOK()
+{
+	m_comboBox.GetWindowText( m_text );
+
+	CDialog::OnOK();
+}
+
+
+
+BEGIN_MESSAGE_MAP(CAddBrowseDomain, CDialog)
+END_MESSAGE_MAP()
+
diff --git a/mdnsresponder/mDNSWindows/ControlPanel/BrowsingPage.h b/mdnsresponder/mDNSWindows/ControlPanel/BrowsingPage.h
new file mode 100755
index 0000000..4711b36
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/ControlPanel/BrowsingPage.h
@@ -0,0 +1,156 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+#pragma once
+
+#include "stdafx.h"
+#include "resource.h"
+
+#include <DebugServices.h>
+#include <list>
+#include "afxcmn.h"
+
+#include "afxwin.h"
+
+
+
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CBrowsingPage
+//---------------------------------------------------------------------------------------------------------------------------
+
+class CBrowsingPage : public CPropertyPage
+{
+public:
+	CBrowsingPage();
+	~CBrowsingPage();
+
+protected:
+
+	//{{AFX_DATA(CBrowsingPage)
+	enum { IDD = IDR_APPLET_PAGE3 };
+	//}}AFX_DATA
+
+	//{{AFX_VIRTUAL(CBrowsingPage)
+	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
+	//}}AFX_VIRTUAL
+
+	DECLARE_DYNCREATE(CBrowsingPage)
+
+	//{{AFX_MSG(CBrowsingPage)
+	//}}AFX_MSG
+	DECLARE_MESSAGE_MAP()
+	
+private:
+	
+	typedef std::list<CString> StringList;
+
+	afx_msg BOOL
+	OnSetActive();
+	
+	afx_msg void
+	OnOK();
+	
+	void
+	SetModified( BOOL bChanged = TRUE );
+	
+	void
+	Commit();
+
+	BOOL			m_modified;
+
+public:
+private:
+
+	static int CALLBACK 
+
+	SortFunc(LPARAM lParam1, LPARAM lParam2, LPARAM lParamSort);
+
+
+
+	CListCtrl	m_browseListCtrl;
+
+	bool		m_initialized;
+
+	bool		m_firstTime;
+
+
+
+public:
+
+
+
+	afx_msg void OnBnClickedAddBrowseDomain();
+
+	afx_msg void OnBnClickedRemoveBrowseDomain();
+
+	afx_msg void OnLvnItemchangedBrowseList(NMHDR *pNMHDR, LRESULT *pResult);
+
+	CButton m_removeButton;
+
+};
+
+
+
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CAddBrowseDomain
+//---------------------------------------------------------------------------------------------------------------------------
+
+
+class CAddBrowseDomain : public CDialog
+
+{
+
+	DECLARE_DYNAMIC(CAddBrowseDomain)
+
+
+
+public:
+
+	CAddBrowseDomain(CWnd* pParent = NULL);   // standard constructor
+
+	virtual ~CAddBrowseDomain();
+
+
+
+// Dialog Data
+
+	enum { IDD = IDR_ADD_BROWSE_DOMAIN };
+
+
+
+protected:
+
+	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
+
+	virtual BOOL OnInitDialog();
+
+	virtual void OnOK();
+
+	DECLARE_MESSAGE_MAP()
+
+public:
+
+	CComboBox	m_comboBox;
+
+	CString		m_text;
+
+};
+
diff --git a/mdnsresponder/mDNSWindows/ControlPanel/ConfigDialog.cpp b/mdnsresponder/mDNSWindows/ControlPanel/ConfigDialog.cpp
new file mode 100755
index 0000000..ad59066
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/ControlPanel/ConfigDialog.cpp
@@ -0,0 +1,59 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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 "ConfigDialog.h"
+#include "ControlPanel.h"
+
+
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+IMPLEMENT_DYNCREATE(CConfigDialog, CDialog)
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CConfigDialog::CConfigDialog
+//---------------------------------------------------------------------------------------------------------------------------
+
+CConfigDialog::CConfigDialog()
+	: CDialog(CConfigDialog::IDD, NULL)
+{
+	//{{AFX_DATA_INIT(CConfigDialog)
+	//}}AFX_DATA_INIT
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CConfigDialog::DoDataExchange
+//---------------------------------------------------------------------------------------------------------------------------
+
+void CConfigDialog::DoDataExchange(CDataExchange* pDX)
+{
+	CDialog::DoDataExchange(pDX);
+	//{{AFX_DATA_MAP(CConfigDialog)
+	//}}AFX_DATA_MAP
+}
+
+
+BEGIN_MESSAGE_MAP(CConfigDialog, CDialog)
+	//{{AFX_MSG_MAP(CConfigDialog)
+	//}}AFX_MSG_MAP
+END_MESSAGE_MAP()
diff --git a/mdnsresponder/mDNSWindows/ControlPanel/ConfigDialog.h b/mdnsresponder/mDNSWindows/ControlPanel/ConfigDialog.h
new file mode 100644
index 0000000..fa8df5f
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/ControlPanel/ConfigDialog.h
@@ -0,0 +1,48 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+#pragma once
+
+#include "stdafx.h"
+#include "resource.h"
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CConfigDialog
+//---------------------------------------------------------------------------------------------------------------------------
+
+class CConfigDialog : public CDialog
+{
+public:
+
+	CConfigDialog();
+
+protected:
+
+	//{{AFX_DATA(CConfigDialog)
+	enum { IDD = IDR_APPLET };
+	//}}AFX_DATA
+
+	//{{AFX_VIRTUAL(CConfigDialog)
+	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
+	//}}AFX_VIRTUAL
+
+	//{{AFX_MSG(CConfigDialog)
+	//}}AFX_MSG
+	DECLARE_MESSAGE_MAP()
+
+	DECLARE_DYNCREATE(CConfigDialog)
+};
diff --git a/mdnsresponder/mDNSWindows/ControlPanel/ConfigPropertySheet.cpp b/mdnsresponder/mDNSWindows/ControlPanel/ConfigPropertySheet.cpp
new file mode 100755
index 0000000..5fae955
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/ControlPanel/ConfigPropertySheet.cpp
@@ -0,0 +1,301 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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 "ConfigPropertySheet.h"
+#include <WinServices.h>
+extern "C"
+{
+#include <ClientCommon.h>
+}
+#include <process.h>
+
+// Custom events
+
+#define WM_DATAREADY		( WM_USER + 0x100 )
+
+
+IMPLEMENT_DYNCREATE(CConfigPropertySheet, CPropertySheet)
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CConfigPropertySheet::CConfigPropertySheet
+//---------------------------------------------------------------------------------------------------------------------------
+
+CConfigPropertySheet::CConfigPropertySheet()
+:
+	CPropertySheet(),
+	m_browseDomainsRef( NULL ),
+	m_thread( NULL ),
+	m_threadExited( NULL )
+{
+	AddPage(&m_firstPage );
+	AddPage(&m_secondPage);
+	AddPage(&m_thirdPage);
+
+	InitializeCriticalSection( &m_lock );
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CConfigPropertySheet::~CConfigPropertySheet
+//---------------------------------------------------------------------------------------------------------------------------
+
+CConfigPropertySheet::~CConfigPropertySheet()
+{
+	DeleteCriticalSection( &m_lock );
+}
+
+
+BEGIN_MESSAGE_MAP(CConfigPropertySheet, CPropertySheet)
+	//{{AFX_MSG_MAP(CConfigPropertySheet)
+	//}}AFX_MSG_MAP
+	ON_MESSAGE( WM_DATAREADY, OnDataReady )
+END_MESSAGE_MAP()
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CConfigPropertySheet::OnInitDialog
+//---------------------------------------------------------------------------------------------------------------------------
+
+BOOL
+CConfigPropertySheet::OnInitDialog()
+{
+	OSStatus err;
+
+	BOOL b = CPropertySheet::OnInitDialog();
+
+	err = SetupBrowsing();
+	require_noerr( err, exit );
+
+exit:
+
+	return b;
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CConfigPropertySheet::OnCommand
+//---------------------------------------------------------------------------------------------------------------------------
+
+BOOL
+CConfigPropertySheet::OnCommand(WPARAM wParam, LPARAM lParam)
+{
+   // Check if OK or Cancel was hit
+
+   if ( ( wParam == ID_WIZFINISH ) || ( wParam == IDOK ) || ( wParam == IDCANCEL ) ) 
+   {
+      OnEndDialog();
+   }
+
+   return CPropertySheet::OnCommand(wParam, lParam);
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CConfigPropertySheet::OnDataReady
+//---------------------------------------------------------------------------------------------------------------------------
+
+LRESULT
+CConfigPropertySheet::OnDataReady(WPARAM inWParam, LPARAM inLParam)
+{
+	if (WSAGETSELECTERROR(inLParam) && !(HIWORD(inLParam)))
+	{
+		dlog( kDebugLevelError, "OnSocket: window error\n" );
+	}
+	else
+	{
+		SOCKET sock = (SOCKET) inWParam;
+
+		if ( m_browseDomainsRef && DNSServiceRefSockFD( m_browseDomainsRef ) == (int) sock )
+		{
+			DNSServiceProcessResult( m_browseDomainsRef );
+		}
+	}
+
+	return 0;
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CConfigPropertySheet::OnEndDialog
+//---------------------------------------------------------------------------------------------------------------------------
+
+void
+CConfigPropertySheet::OnEndDialog()
+{
+	OSStatus err;
+
+	err = TearDownBrowsing();
+	check_noerr( err );
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CConfigPropertySheet::SetupBrowsing
+//---------------------------------------------------------------------------------------------------------------------------
+
+OSStatus
+CConfigPropertySheet::SetupBrowsing()
+{
+	OSStatus err;
+
+	// Start browsing for browse domains
+
+	err = DNSServiceEnumerateDomains( &m_browseDomainsRef, kDNSServiceFlagsBrowseDomains, 0, BrowseDomainsReply, this );
+	require_noerr( err, exit );
+
+	err = WSAAsyncSelect( DNSServiceRefSockFD( m_browseDomainsRef ), m_hWnd, WM_DATAREADY, FD_READ|FD_CLOSE );
+	require_noerr( err, exit );
+
+exit:
+
+	if ( err )
+	{
+		TearDownBrowsing();
+	}
+
+	return err;
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CConfigPropertySheet::TearDownBrowsing
+//---------------------------------------------------------------------------------------------------------------------------
+
+OSStatus
+CConfigPropertySheet::TearDownBrowsing()
+{
+	OSStatus err = kNoErr;
+
+	if ( m_browseDomainsRef )
+	{
+		err = WSAAsyncSelect( DNSServiceRefSockFD( m_browseDomainsRef ), m_hWnd, 0, 0 );
+		check_noerr( err );
+
+		DNSServiceRefDeallocate( m_browseDomainsRef );
+	
+		m_browseDomainsRef = NULL;
+	}
+
+	return err;
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CConfigPropertySheet::DecodeDomainName
+//---------------------------------------------------------------------------------------------------------------------------
+
+OSStatus
+CConfigPropertySheet::DecodeDomainName( const char * raw, CString & decoded )
+{
+	char nextLabel[128] = "\0";
+	char decodedDomainString[kDNSServiceMaxDomainName];
+    char * buffer = (char *) raw;
+    int labels = 0, i;
+    char text[64];
+	const char *label[128];
+	OSStatus	err;
+    
+	// Initialize
+
+	decodedDomainString[0] = '\0';
+
+    // Count the labels
+
+	while ( *buffer )
+	{
+		label[labels++] = buffer;
+		buffer = (char *) GetNextLabel(buffer, text);
+    }
+        
+    buffer = (char*) raw;
+
+    for (i = 0; i < labels; i++)
+	{
+		buffer = (char *)GetNextLabel(buffer, nextLabel);
+        strcat(decodedDomainString, nextLabel);
+        strcat(decodedDomainString, ".");
+    }
+    
+    // Remove trailing dot from domain name.
+    
+	decodedDomainString[ strlen( decodedDomainString ) - 1 ] = '\0';
+
+	// Convert to Unicode
+
+	err = UTF8StringToStringObject( decodedDomainString, decoded );
+
+	return err;
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CConfigPropertySheet::BrowseDomainsReply
+//---------------------------------------------------------------------------------------------------------------------------
+
+void DNSSD_API
+CConfigPropertySheet::BrowseDomainsReply
+							(
+							DNSServiceRef			sdRef,
+							DNSServiceFlags			flags,
+							uint32_t				interfaceIndex,
+							DNSServiceErrorType		errorCode,
+							const char			*	replyDomain,
+							void				*	context
+							)
+{
+	CConfigPropertySheet	*	self = reinterpret_cast<CConfigPropertySheet*>(context);
+	CString						decoded;
+	OSStatus					err;
+
+	DEBUG_UNUSED( sdRef );
+	DEBUG_UNUSED( interfaceIndex );
+
+	if ( errorCode )
+	{
+		goto exit;
+	}
+
+	check( replyDomain );
+	
+	// Ignore local domains
+
+	if ( strcmp( replyDomain, "local." ) == 0 )
+	{
+		goto exit;
+	}
+
+	err = self->DecodeDomainName( replyDomain, decoded );
+	require_noerr( err, exit );
+
+	// Remove trailing '.'
+
+	decoded.TrimRight( '.' );
+
+	if ( flags & kDNSServiceFlagsAdd )
+	{
+		self->m_browseDomains.push_back( decoded );
+	}
+	else
+	{
+		self->m_browseDomains.remove( decoded );
+	}
+
+exit:
+
+	return;
+}
diff --git a/mdnsresponder/mDNSWindows/ControlPanel/ConfigPropertySheet.h b/mdnsresponder/mDNSWindows/ControlPanel/ConfigPropertySheet.h
new file mode 100755
index 0000000..9e4fda8
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/ControlPanel/ConfigPropertySheet.h
@@ -0,0 +1,105 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+#ifndef _ConfigPropertySheet_h
+#define _ConfigPropertySheet_h
+
+#include "stdafx.h"
+#include "ServicesPage.h"
+#include "RegistrationPage.h"
+#include "BrowsingPage.h"
+
+#include <RegNames.h>
+#include <dns_sd.h>
+#include <list>
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CConfigPropertySheet
+//---------------------------------------------------------------------------------------------------------------------------
+
+class CConfigPropertySheet : public CPropertySheet
+{
+public:
+
+	CConfigPropertySheet();
+	virtual ~CConfigPropertySheet();
+
+	typedef std::list<CString> StringList;
+
+	StringList	m_browseDomains;
+
+protected:
+
+	CServicesPage		m_firstPage;
+	CRegistrationPage	m_secondPage;
+	CBrowsingPage		m_thirdPage;
+
+	//{{AFX_VIRTUAL(CConfigPropertySheet)
+	//}}AFX_VIRTUAL
+
+	DECLARE_DYNCREATE(CConfigPropertySheet)
+
+	//{{AFX_MSG(CConfigPropertySheet)
+	//}}AFX_MSG
+	DECLARE_MESSAGE_MAP()
+
+	afx_msg BOOL	OnInitDialog();
+	afx_msg BOOL	OnCommand( WPARAM wParam, LPARAM lParam );
+	afx_msg LRESULT	OnDataReady( WPARAM inWParam, LPARAM inLParam );
+	afx_msg LRESULT	OnRegistryChanged( WPARAM inWParam, LPARAM inLParam );
+	void			OnEndDialog();
+
+private:
+
+	OSStatus
+	SetupBrowsing();
+
+	OSStatus
+	TearDownBrowsing();
+
+	OSStatus
+	DecodeDomainName( const char * raw, CString & decoded );
+
+	static void DNSSD_API
+	BrowseDomainsReply
+				(
+				DNSServiceRef			sdRef,
+				DNSServiceFlags			flags,
+				uint32_t				interfaceIndex,
+				DNSServiceErrorType		errorCode,
+				const char			*	replyDomain,
+				void				*	context
+				);
+
+	// This thread will watch for registry changes
+
+	static unsigned WINAPI
+	WatchRegistry
+				(
+				LPVOID inParam
+				);
+
+	HKEY				m_statusKey;
+	HANDLE				m_thread;
+	HANDLE				m_threadExited;
+	DNSServiceRef		m_browseDomainsRef;
+	CRITICAL_SECTION	m_lock;
+};
+
+
+#endif
diff --git a/mdnsresponder/mDNSWindows/ControlPanel/ControlPanel.cpp b/mdnsresponder/mDNSWindows/ControlPanel/ControlPanel.cpp
new file mode 100755
index 0000000..4bf5df3
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/ControlPanel/ControlPanel.cpp
@@ -0,0 +1,380 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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 "ControlPanel.h"
+#include "ConfigDialog.h"
+#include "ConfigPropertySheet.h"
+#include "resource.h"
+
+#include <DebugServices.h>
+
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+
+static CCPApp theApp;
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	GetControlPanelApp
+//---------------------------------------------------------------------------------------------------------------------------
+
+CCPApp*
+GetControlPanelApp()
+{
+	CCPApp * pApp = (CCPApp*) AfxGetApp();
+
+	check( pApp );
+	check( pApp->IsKindOf( RUNTIME_CLASS( CCPApp ) ) );
+
+	return pApp;
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CPlApplet
+//---------------------------------------------------------------------------------------------------------------------------
+
+LONG APIENTRY
+CPlApplet(HWND hWndCPl, UINT uMsg, LONG lParam1, LONG lParam2)
+{
+	AFX_MANAGE_STATE(AfxGetStaticModuleState());
+
+	CCPApp * pApp = GetControlPanelApp();
+
+	return ( LONG ) pApp->OnCplMsg(hWndCPl, uMsg, lParam1, lParam2);
+}
+
+
+IMPLEMENT_DYNAMIC(CCPApplet, CCmdTarget);
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CCPApplet::CCPApplet
+//---------------------------------------------------------------------------------------------------------------------------
+
+CCPApplet::CCPApplet(UINT resourceId, UINT descId, CRuntimeClass * uiClass)
+:
+	m_resourceId(resourceId),
+	m_descId(descId),
+	m_uiClass(uiClass),
+	m_pageNumber(0)
+{
+	check( uiClass );
+	check( uiClass->IsDerivedFrom( RUNTIME_CLASS( CDialog ) ) || 
+	       uiClass->IsDerivedFrom( RUNTIME_CLASS( CPropertySheet ) ) );
+
+	m_name.LoadString(resourceId);
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CCPApplet::~CCPApplet
+//---------------------------------------------------------------------------------------------------------------------------
+
+CCPApplet::~CCPApplet()
+{
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CCPApplet::OnStartParms
+//---------------------------------------------------------------------------------------------------------------------------
+
+LRESULT
+CCPApplet::OnStartParms(CWnd * pParentWnd, LPCTSTR extra)
+{
+	DEBUG_UNUSED( pParentWnd );
+
+	if ( extra )
+	{
+		m_pageNumber = ::_ttoi( extra ) - 1;
+	}
+
+	return 0;
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CCPApplet::OnRun
+//---------------------------------------------------------------------------------------------------------------------------
+
+LRESULT
+CCPApplet::OnRun(CWnd* pParentWnd)
+{
+	LRESULT		lResult = 1;
+	CWnd	*	pWnd;
+
+	InitCommonControls();
+
+	pWnd = (CWnd*) m_uiClass->CreateObject(); 
+
+	if ( pWnd )
+	{
+		lResult = ERROR_SUCCESS;
+
+		if ( pWnd->IsKindOf( RUNTIME_CLASS( CPropertySheet ) ) )
+		{
+			CPropertySheet * pSheet = (CPropertySheet*) pWnd;
+
+			pSheet->Construct(m_name, pParentWnd, m_pageNumber);
+
+			pSheet->DoModal();
+		}
+		else
+		{
+			check( pWnd->IsKindOf( RUNTIME_CLASS( CDialog ) ) );
+
+			CDialog * pDialog = (CDialog*) pWnd;
+
+      		pDialog->DoModal();
+    	}
+
+		delete pWnd;
+  	}
+
+	return lResult;
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CCPApplet::OnInquire
+//---------------------------------------------------------------------------------------------------------------------------
+
+LRESULT
+CCPApplet::OnInquire(CPLINFO* pInfo)
+{
+	pInfo->idIcon = m_resourceId;
+	pInfo->idName = m_resourceId;
+	pInfo->idInfo = m_descId;
+	pInfo->lData  = reinterpret_cast<LONG>(this);
+
+	return 0;
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CCPApplet::OnNewInquire
+//---------------------------------------------------------------------------------------------------------------------------
+
+LRESULT
+CCPApplet::OnNewInquire(NEWCPLINFO* pInfo)
+{
+	DEBUG_UNUSED( pInfo );
+
+	return 1;
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CCPApplet::OnSelect
+//---------------------------------------------------------------------------------------------------------------------------
+
+LRESULT
+CCPApplet::OnSelect()
+{
+	return 0;
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CCPApplet::OnStop
+//---------------------------------------------------------------------------------------------------------------------------
+
+LRESULT
+CCPApplet::OnStop()
+{
+	return 0;
+}
+
+
+IMPLEMENT_DYNAMIC(CCPApp, CWinApp);
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CCPApp::CCPApp
+//---------------------------------------------------------------------------------------------------------------------------
+
+CCPApp::CCPApp()
+{
+	debug_initialize( kDebugOutputTypeWindowsEventLog, "DNS-SD Control Panel", GetModuleHandle( NULL ) );
+	debug_set_property( kDebugPropertyTagPrintLevel, kDebugLevelInfo );
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CCPApp::~CCPApp
+//---------------------------------------------------------------------------------------------------------------------------
+
+CCPApp::~CCPApp()
+{
+	while ( !m_applets.IsEmpty() )
+	{
+    	delete m_applets.RemoveHead();
+	}
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CCPApp::AddApplet
+//---------------------------------------------------------------------------------------------------------------------------
+
+void
+CCPApp::AddApplet( CCPApplet * applet )
+{
+	check( applet );
+
+	m_applets.AddTail( applet );
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CCPApp::OnInit
+//---------------------------------------------------------------------------------------------------------------------------
+
+LRESULT
+CCPApp::OnInit()
+{
+	CCPApplet * applet;
+
+	try
+	{
+		applet = new CCPApplet( IDR_APPLET, IDS_APPLET_DESCRIPTION, RUNTIME_CLASS( CConfigPropertySheet ) );
+	}
+	catch (...)
+	{
+		applet = NULL;
+	}
+   
+	require_action( applet, exit, kNoMemoryErr );
+	
+	AddApplet( applet );
+
+exit:
+
+	return m_applets.GetCount();
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CCPApp::OnExit
+//---------------------------------------------------------------------------------------------------------------------------
+
+LRESULT
+CCPApp::OnExit()
+{
+  return 1;
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CCPApp::OnCplMsg
+//---------------------------------------------------------------------------------------------------------------------------
+
+LRESULT
+CCPApp::OnCplMsg(HWND hWndCPl, UINT uMsg, LPARAM lParam1, LPARAM lParam2)
+{
+	LRESULT lResult = 1;
+
+	switch ( uMsg )
+	{
+		case CPL_INIT:
+		{
+			lResult = OnInit();
+		}
+		break;
+
+		case CPL_EXIT:
+		{
+			lResult = OnExit();
+		}
+		break;
+
+		case CPL_GETCOUNT:
+		{      
+    		lResult = m_applets.GetCount();
+  		}
+		break;
+
+		default:
+  		{
+    		POSITION pos = m_applets.FindIndex( lParam1 );
+			check( pos );
+
+			CCPApplet * applet = m_applets.GetAt( pos );  
+			check( applet );
+
+    		switch (uMsg)
+    		{
+      			case CPL_INQUIRE:
+      			{
+					LPCPLINFO pInfo = reinterpret_cast<LPCPLINFO>(lParam2);
+        			lResult = applet->OnInquire(pInfo);
+				}  
+        		break;
+
+				case CPL_NEWINQUIRE:
+      			{
+        			LPNEWCPLINFO pInfo = reinterpret_cast<LPNEWCPLINFO>(lParam2);
+					lResult = applet->OnNewInquire(pInfo);
+				}  
+        		break;
+
+				case CPL_STARTWPARMS:
+      			{
+        			CWnd * pParentWnd = CWnd::FromHandle(hWndCPl);
+        			LPCTSTR lpszExtra = reinterpret_cast<LPCTSTR>(lParam2);
+        			lResult = applet->OnStartParms(pParentWnd, lpszExtra);
+				}
+				break;
+
+				case CPL_DBLCLK:
+				{
+        			CWnd* pParentWnd = CWnd::FromHandle(hWndCPl);
+        			lResult = applet->OnRun(pParentWnd);
+				}
+        		break;
+
+				case CPL_SELECT:
+				{
+        			lResult = applet->OnSelect();
+				}
+				break;
+
+				case CPL_STOP:
+				{
+					lResult = applet->OnStop();
+				}
+				break;
+
+				default:
+				{
+					// TRACE(_T("Warning, Received an unknown control panel message:%d\n"), uMsg);
+					lResult = 1;
+				}
+				break;
+    		}
+  		}
+		break;
+	}
+
+	return lResult;
+}
diff --git a/mdnsresponder/mDNSWindows/ControlPanel/ControlPanel.def b/mdnsresponder/mDNSWindows/ControlPanel/ControlPanel.def
new file mode 100644
index 0000000..3cb05eb
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/ControlPanel/ControlPanel.def
@@ -0,0 +1,20 @@
+; -*- tab-width: 4 -*-
+;
+; Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+;
+; Licensed 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.
+    
+LIBRARY	"Bonjour"
+
+EXPORTS
+	CPlApplet
diff --git a/mdnsresponder/mDNSWindows/ControlPanel/ControlPanel.h b/mdnsresponder/mDNSWindows/ControlPanel/ControlPanel.h
new file mode 100644
index 0000000..dec5e58
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/ControlPanel/ControlPanel.h
@@ -0,0 +1,84 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+    
+#pragma once
+
+#include "stdafx.h"
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CCPApplet
+//---------------------------------------------------------------------------------------------------------------------------
+
+class CCPApplet : public CCmdTarget
+{
+public:
+
+	CCPApplet( UINT nResourceID, UINT nDescriptionID, CRuntimeClass* pUIClass );
+
+	virtual ~CCPApplet();
+
+protected:
+
+	virtual LRESULT OnRun(CWnd* pParentWnd);
+	virtual LRESULT OnStartParms(CWnd* pParentWnd, LPCTSTR lpszExtra);
+	virtual LRESULT OnInquire(CPLINFO* pInfo);
+	virtual LRESULT OnNewInquire(NEWCPLINFO* pInfo);
+	virtual LRESULT OnSelect();
+	virtual LRESULT OnStop();
+
+	CRuntimeClass	*	m_uiClass;
+	UINT				m_resourceId;
+	UINT				m_descId;
+	CString				m_name;
+	int					m_pageNumber;
+  
+	friend class CCPApp;
+
+	DECLARE_DYNAMIC(CCPApplet);
+};
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CCPApp
+//---------------------------------------------------------------------------------------------------------------------------
+
+class CCPApp : public CWinApp
+{
+public:
+
+	CCPApp();
+	virtual ~CCPApp();
+
+	void AddApplet( CCPApplet* pApplet );
+
+protected:
+
+	CList<CCPApplet*, CCPApplet*&> m_applets;
+
+	friend LONG APIENTRY
+	CPlApplet(HWND hWndCPl, UINT uMsg, LONG lParam1, LONG lParam2);
+
+	virtual LRESULT OnCplMsg(HWND hWndCPl, UINT msg, LPARAM lp1, LPARAM lp2);
+	virtual LRESULT OnInit();
+	virtual LRESULT OnExit();
+
+	DECLARE_DYNAMIC(CCPApp);
+};
+
+
+CCPApp * GetControlPanelApp();
diff --git a/mdnsresponder/mDNSWindows/ControlPanel/ControlPanel.rc b/mdnsresponder/mDNSWindows/ControlPanel/ControlPanel.rc
new file mode 100644
index 0000000..1df3e90
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/ControlPanel/ControlPanel.rc
@@ -0,0 +1,141 @@
+// Microsoft Visual C++ generated resource script.

+//

+#include "resource.h"

+

+#define APSTUDIO_READONLY_SYMBOLS

+/////////////////////////////////////////////////////////////////////////////

+//

+// Generated from the TEXTINCLUDE 2 resource.

+//

+#include "afxres.h"

+#include "WinVersRes.h"

+

+/////////////////////////////////////////////////////////////////////////////

+#undef APSTUDIO_READONLY_SYMBOLS

+

+/////////////////////////////////////////////////////////////////////////////

+// English (U.S.) resources

+

+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)

+#ifdef _WIN32

+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US

+#pragma code_page(1252)

+#endif //_WIN32

+

+#ifdef APSTUDIO_INVOKED

+/////////////////////////////////////////////////////////////////////////////

+//

+// TEXTINCLUDE

+//

+

+1 TEXTINCLUDE 

+BEGIN

+    "resource.h\0"

+END

+

+2 TEXTINCLUDE 

+BEGIN

+    "#include ""afxres.h""\r\n"

+    "#include ""WinVersRes.h""\r\n"

+    "\0"

+END

+

+3 TEXTINCLUDE 

+BEGIN

+    "#define _AFX_NO_SPLITTER_RESOURCES\r\n"

+    "#define _AFX_NO_OLE_RESOURCES\r\n"

+    "#define _AFX_NO_TRACKER_RESOURCES\r\n"

+    "#define _AFX_NO_PROPERTY_RESOURCES\r\n"

+    "\r\n"

+    "#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\r\n"

+    "LANGUAGE 9, 1\r\n"

+    "#pragma code_page(1252)\r\n"

+    "#include ""afxres.rc""     // Standard components\r\n"

+    "#endif\r\n"

+    "\0"

+END

+

+#endif    // APSTUDIO_INVOKED

+

+/////////////////////////////////////////////////////////////////////////////

+//

+// Icon

+//

+

+// Icon with lowest ID value placed first to ensure application icon

+// remains consistent on all systems.

+IDR_APPLET              ICON                    "res\\controlpanel.ico"

+

+

+/////////////////////////////////////////////////////////////////////////////

+//

+// Version

+//

+

+VS_VERSION_INFO VERSIONINFO

+ FILEVERSION MASTER_PROD_VERS

+ PRODUCTVERSION MASTER_PROD_VERS

+ FILEFLAGSMASK 0x3fL

+#ifdef _DEBUG

+ FILEFLAGS 0x1L

+#else

+ FILEFLAGS 0x0L

+#endif

+ FILEOS 0x4L

+ FILETYPE 0x2L

+ FILESUBTYPE 0x0L

+BEGIN

+    BLOCK "StringFileInfo"

+    BEGIN

+        BLOCK "040904b0"

+        BEGIN

+            VALUE "CompanyName", MASTER_COMPANY_NAME

+            VALUE "FileDescription", "Bonjour Control Panel"

+            VALUE "FileVersion", MASTER_PROD_VERS_STR

+            VALUE "InternalName", "ControlPanel.cpl"

+            VALUE "LegalCopyright", MASTER_LEGAL_COPYRIGHT

+            VALUE "OriginalFilename", "ControlPanel.cpl"

+            VALUE "ProductName", MASTER_PROD_NAME

+            VALUE "ProductVersion", MASTER_PROD_VERS_STR

+        END

+    END

+    BLOCK "VarFileInfo"

+    BEGIN

+        VALUE "Translation", 0x409, 1200

+    END

+END

+

+/////////////////////////////////////////////////////////////////////////////

+//

+// String Table

+//

+

+STRINGTABLE

+BEGIN

+	IDS_REINSTALL    "Bonjour Control Panel cannot run because some of its required files are missing.  Please reinstall Bonjour Control Panel."

+	IDS_REINSTALL_CAPTION "Bonjour"

+END

+#endif    // English (U.S.) resources

+/////////////////////////////////////////////////////////////////////////////

+

+

+

+#ifndef APSTUDIO_INVOKED

+/////////////////////////////////////////////////////////////////////////////

+//

+// Generated from the TEXTINCLUDE 3 resource.

+//

+#define _AFX_NO_SPLITTER_RESOURCES

+#define _AFX_NO_OLE_RESOURCES

+#define _AFX_NO_TRACKER_RESOURCES

+#define _AFX_NO_PROPERTY_RESOURCES

+

+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)

+LANGUAGE 9, 1

+#pragma code_page(1252)

+#include "afxres.rc"     // Standard components

+#endif

+

+/////////////////////////////////////////////////////////////////////////////

+#endif    // not APSTUDIO_INVOKED

+

diff --git a/mdnsresponder/mDNSWindows/ControlPanel/ControlPanel.vcproj b/mdnsresponder/mDNSWindows/ControlPanel/ControlPanel.vcproj
new file mode 100755
index 0000000..2f2d0ab
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/ControlPanel/ControlPanel.vcproj
@@ -0,0 +1,727 @@
+<?xml version="1.0" encoding="Windows-1252"?>

+<VisualStudioProject

+	ProjectType="Visual C++"

+	Version="8.00"

+	Name="ControlPanel"

+	ProjectGUID="{0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}"

+	RootNamespace="ControlPanel"

+	Keyword="MFCProj"

+	>

+	<Platforms>

+		<Platform

+			Name="Win32"

+		/>

+		<Platform

+			Name="x64"

+		/>

+	</Platforms>

+	<ToolFiles>

+	</ToolFiles>

+	<Configurations>

+		<Configuration

+			Name="Debug|Win32"

+			OutputDirectory="$(PlatformName)\$(ConfigurationName)"

+			IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"

+			ConfigurationType="1"

+			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"

+			UseOfMFC="1"

+			ATLMinimizesCRunTimeLibraryUsage="false"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+				PreprocessorDefinitions="_DEBUG"

+				MkTypLibCompatible="false"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				Optimization="0"

+				AdditionalIncludeDirectories="..;../../mDNSCore;../../mDNSShared;../../Clients"

+				PreprocessorDefinitions="WIN32;_DEBUG;DEBUG=1;UNICODE;_UNICODE;_WINDOWS;WINVER=0x0501;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1"

+				StringPooling="true"

+				MinimalRebuild="true"

+				BasicRuntimeChecks="3"

+				RuntimeLibrary="1"

+				UsePrecompiledHeader="0"

+				PrecompiledHeaderThrough=""

+				PrecompiledHeaderFile=""

+				AssemblerListingLocation="$(IntDir)\"

+				ObjectFile="$(IntDir)\"

+				ProgramDataBaseFileName="$(IntDir)\vc80.pdb"

+				WarningLevel="4"

+				SuppressStartupBanner="true"

+				Detect64BitPortabilityProblems="true"

+				DebugInformationFormat="3"

+				CallingConvention="0"

+				DisableSpecificWarnings="4311;4312"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+				PreprocessorDefinitions="_DEBUG"

+				Culture="1033"

+				AdditionalIncludeDirectories="../"

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				AdditionalOptions="/NXCOMPAT /DYNAMICBASE /SAFESEH"

+				AdditionalDependencies="../DLLStub/$(PlatformName)/$(ConfigurationName)/dnssdStatic.lib ws2_32.lib"

+				OutputFile="$(OutDir)/ControlPanel.exe"

+				LinkIncremental="2"

+				SuppressStartupBanner="true"

+				GenerateDebugInformation="true"

+				ProgramDatabaseFile="$(OutDir)\ControlPanel.pdb"

+				SubSystem="2"

+				EntryPointSymbol="wWinMainCRTStartup"

+				TargetMachine="1"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+				AdditionalManifestFiles="res\ControlPanel.manifest"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+			/>

+		</Configuration>

+		<Configuration

+			Name="Debug|x64"

+			OutputDirectory="$(PlatformName)\$(ConfigurationName)"

+			IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"

+			ConfigurationType="1"

+			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"

+			UseOfMFC="1"

+			ATLMinimizesCRunTimeLibraryUsage="false"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+				PreprocessorDefinitions="_DEBUG"

+				MkTypLibCompatible="false"

+				TargetEnvironment="3"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				Optimization="0"

+				AdditionalIncludeDirectories="..;../../mDNSCore;../../mDNSShared;../../Clients"

+				PreprocessorDefinitions="WIN32;_DEBUG;DEBUG=1;UNICODE;_UNICODE;_WINDOWS;WINVER=0x0501;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1"

+				StringPooling="true"

+				MinimalRebuild="true"

+				BasicRuntimeChecks="3"

+				RuntimeLibrary="1"

+				UsePrecompiledHeader="0"

+				PrecompiledHeaderThrough=""

+				PrecompiledHeaderFile=""

+				AssemblerListingLocation="$(IntDir)\"

+				ObjectFile="$(IntDir)\"

+				ProgramDataBaseFileName="$(IntDir)\vc80.pdb"

+				WarningLevel="4"

+				SuppressStartupBanner="true"

+				Detect64BitPortabilityProblems="true"

+				DebugInformationFormat="3"

+				CallingConvention="0"

+				DisableSpecificWarnings="4311;4312"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+				PreprocessorDefinitions="_DEBUG"

+				Culture="1033"

+				AdditionalIncludeDirectories="../"

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				AdditionalOptions="/NXCOMPAT /DYNAMICBASE"

+				AdditionalDependencies="../DLLStub/$(PlatformName)/$(ConfigurationName)/dnssdStatic.lib ws2_32.lib"

+				OutputFile="$(OutDir)/ControlPanel.exe"

+				LinkIncremental="2"

+				SuppressStartupBanner="true"

+				GenerateDebugInformation="true"

+				ProgramDatabaseFile="$(OutDir)\ControlPanel.pdb"

+				SubSystem="2"

+				EntryPointSymbol="wWinMainCRTStartup"

+				TargetMachine="17"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+				AdditionalManifestFiles="res\ControlPanel64.manifest"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+			/>

+		</Configuration>

+		<Configuration

+			Name="Release|Win32"

+			OutputDirectory="$(PlatformName)\$(ConfigurationName)"

+			IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"

+			ConfigurationType="1"

+			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"

+			UseOfMFC="1"

+			CharacterSet="2"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+				PreprocessorDefinitions="NDEBUG"

+				MkTypLibCompatible="false"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				Optimization="2"

+				InlineFunctionExpansion="1"

+				AdditionalIncludeDirectories="..;../../mDNSCore;../../mDNSShared;../../Clients"

+				PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;WINVER=0x0501;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1"

+				StringPooling="true"

+				MinimalRebuild="false"

+				RuntimeLibrary="0"

+				EnableFunctionLevelLinking="true"

+				TreatWChar_tAsBuiltInType="true"

+				UsePrecompiledHeader="0"

+				AssemblerListingLocation="$(IntDir)\"

+				ObjectFile="$(IntDir)\"

+				ProgramDataBaseFileName="$(IntDir)\vc80.pdb"

+				WarningLevel="4"

+				SuppressStartupBanner="true"

+				DisableSpecificWarnings="4702"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+				PreprocessorDefinitions="NDEBUG"

+				Culture="1033"

+				AdditionalIncludeDirectories="../"

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				AdditionalOptions="/NXCOMPAT /DYNAMICBASE /SAFESEH"

+				AdditionalDependencies="../DLLStub/$(PlatformName)/$(ConfigurationName)/dnssdStatic.lib ws2_32.lib"

+				OutputFile="$(OutDir)/ControlPanel.exe"

+				LinkIncremental="1"

+				SuppressStartupBanner="true"

+				ProgramDatabaseFile="$(OutDir)\ControlPanel.pdb"

+				SubSystem="2"

+				OptimizeReferences="0"

+				EnableCOMDATFolding="0"

+				EntryPointSymbol="wWinMainCRTStartup"

+				TargetMachine="1"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+				AdditionalManifestFiles="res\ControlPanel.manifest"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+				CommandLine="if not &quot;%RC_XBS%&quot; == &quot;YES&quot; goto END&#x0D;&#x0A;if not exist &quot;$(DSTROOT)\Program Files\Bonjour SDK\bin\$(PlatformName)&quot;   mkdir &quot;$(DSTROOT)\Program Files\Bonjour SDK\bin\$(PlatformName)&quot;&#x0D;&#x0A;xcopy /I/Y &quot;$(TargetPath)&quot;                                                                  &quot;$(DSTROOT)\Program Files\Bonjour SDK\bin\$(PlatformName)&quot;&#x0D;&#x0A;:END&#x0D;&#x0A;"

+			/>

+		</Configuration>

+		<Configuration

+			Name="Release|x64"

+			OutputDirectory="$(PlatformName)\$(ConfigurationName)"

+			IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"

+			ConfigurationType="1"

+			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"

+			UseOfMFC="1"

+			CharacterSet="2"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+				PreprocessorDefinitions="NDEBUG"

+				MkTypLibCompatible="false"

+				TargetEnvironment="3"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				Optimization="2"

+				InlineFunctionExpansion="1"

+				AdditionalIncludeDirectories="..;../../mDNSCore;../../mDNSShared;../../Clients"

+				PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;WINVER=0x0501;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1"

+				StringPooling="true"

+				MinimalRebuild="false"

+				RuntimeLibrary="0"

+				EnableFunctionLevelLinking="true"

+				TreatWChar_tAsBuiltInType="true"

+				UsePrecompiledHeader="0"

+				AssemblerListingLocation="$(IntDir)\"

+				ObjectFile="$(IntDir)\"

+				ProgramDataBaseFileName="$(IntDir)\vc80.pdb"

+				WarningLevel="4"

+				SuppressStartupBanner="true"

+				DisableSpecificWarnings="4702"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+				PreprocessorDefinitions="NDEBUG"

+				Culture="1033"

+				AdditionalIncludeDirectories="../"

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				AdditionalOptions="/NXCOMPAT /DYNAMICBASE"

+				AdditionalDependencies="../DLLStub/$(PlatformName)/$(ConfigurationName)/dnssdStatic.lib ws2_32.lib"

+				OutputFile="$(OutDir)/ControlPanel.exe"

+				LinkIncremental="1"

+				SuppressStartupBanner="true"

+				ProgramDatabaseFile="$(OutDir)\ControlPanel.pdb"

+				SubSystem="2"

+				OptimizeReferences="0"

+				EnableCOMDATFolding="0"

+				EntryPointSymbol="wWinMainCRTStartup"

+				TargetMachine="17"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+				AdditionalManifestFiles="res\ControlPanel64.manifest"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+				CommandLine="if not &quot;%RC_XBS%&quot; == &quot;YES&quot; goto END&#x0D;&#x0A;if not exist &quot;$(DSTROOT)\Program Files\Bonjour SDK\bin\$(PlatformName)&quot;   mkdir &quot;$(DSTROOT)\Program Files\Bonjour SDK\bin\$(PlatformName)&quot;&#x0D;&#x0A;xcopy /I/Y &quot;$(TargetPath)&quot;                                                                  &quot;$(DSTROOT)\Program Files\Bonjour SDK\bin\$(PlatformName)&quot;&#x0D;&#x0A;:END&#x0D;&#x0A;"

+			/>

+		</Configuration>

+	</Configurations>

+	<References>

+	</References>

+	<Files>

+		<Filter

+			Name="Source Files"

+			Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"

+			>

+			<File

+				RelativePath="ConfigDialog.cpp"

+				>

+				<FileConfiguration

+					Name="Debug|Win32"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						Optimization="0"

+						PreprocessorDefinitions=""

+					/>

+				</FileConfiguration>

+				<FileConfiguration

+					Name="Debug|x64"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						Optimization="0"

+						PreprocessorDefinitions=""

+					/>

+				</FileConfiguration>

+				<FileConfiguration

+					Name="Release|Win32"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						Optimization="2"

+						PreprocessorDefinitions=""

+					/>

+				</FileConfiguration>

+				<FileConfiguration

+					Name="Release|x64"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						Optimization="2"

+						PreprocessorDefinitions=""

+					/>

+				</FileConfiguration>

+			</File>

+			<File

+				RelativePath="ConfigPropertySheet.cpp"

+				>

+				<FileConfiguration

+					Name="Debug|Win32"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						Optimization="0"

+						PreprocessorDefinitions=""

+					/>

+				</FileConfiguration>

+				<FileConfiguration

+					Name="Debug|x64"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						Optimization="0"

+						PreprocessorDefinitions=""

+					/>

+				</FileConfiguration>

+				<FileConfiguration

+					Name="Release|Win32"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						Optimization="2"

+						PreprocessorDefinitions=""

+					/>

+				</FileConfiguration>

+				<FileConfiguration

+					Name="Release|x64"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						Optimization="2"

+						PreprocessorDefinitions=""

+					/>

+				</FileConfiguration>

+			</File>

+			<File

+				RelativePath=".\ControlPanelExe.cpp"

+				>

+			</File>

+			<File

+				RelativePath=".\ServicesPage.cpp"

+				>

+			</File>

+			<File

+				RelativePath="RegistrationPage.cpp"

+				>

+				<FileConfiguration

+					Name="Debug|Win32"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						Optimization="0"

+						PreprocessorDefinitions=""

+					/>

+				</FileConfiguration>

+				<FileConfiguration

+					Name="Debug|x64"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						Optimization="0"

+						PreprocessorDefinitions=""

+					/>

+				</FileConfiguration>

+				<FileConfiguration

+					Name="Release|Win32"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						Optimization="2"

+						PreprocessorDefinitions=""

+					/>

+				</FileConfiguration>

+				<FileConfiguration

+					Name="Release|x64"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						Optimization="2"

+						PreprocessorDefinitions=""

+					/>

+				</FileConfiguration>

+			</File>

+			<File

+				RelativePath="..\loclibrary.c"

+				>

+			</File>

+			<File

+				RelativePath="stdafx.cpp"

+				>

+				<FileConfiguration

+					Name="Debug|Win32"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						Optimization="0"

+						PreprocessorDefinitions=""

+						UsePrecompiledHeader="0"

+					/>

+				</FileConfiguration>

+				<FileConfiguration

+					Name="Debug|x64"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						Optimization="0"

+						PreprocessorDefinitions=""

+						UsePrecompiledHeader="0"

+					/>

+				</FileConfiguration>

+				<FileConfiguration

+					Name="Release|Win32"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						Optimization="2"

+						PreprocessorDefinitions=""

+						UsePrecompiledHeader="0"

+					/>

+				</FileConfiguration>

+				<FileConfiguration

+					Name="Release|x64"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						Optimization="2"

+						PreprocessorDefinitions=""

+						UsePrecompiledHeader="0"

+					/>

+				</FileConfiguration>

+			</File>

+			<File

+				RelativePath="BrowsingPage.cpp"

+				>

+			</File>

+		</Filter>

+		<Filter

+			Name="Header Files"

+			Filter="h;hpp;hxx;hm;inl"

+			>

+			<File

+				RelativePath="ConfigDialog.h"

+				>

+			</File>

+			<File

+				RelativePath="ConfigPropertySheet.h"

+				>

+			</File>

+			<File

+				RelativePath=".\ControlPanelExe.h"

+				>

+			</File>

+			<File

+				RelativePath=".\ServicesPage.h"

+				>

+			</File>

+			<File

+				RelativePath="RegistrationPage.h"

+				>

+			</File>

+			<File

+				RelativePath="..\loclibrary.h"

+				>

+			</File>

+			<File

+				RelativePath="Resource.h"

+				>

+			</File>

+			<File

+				RelativePath="stdafx.h"

+				>

+			</File>

+			<File

+				RelativePath="BrowsingPage.h"

+				>

+			</File>

+		</Filter>

+		<Filter

+			Name="Resource Files"

+			Filter="ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe"

+			>

+			<File

+				RelativePath="res\configurator.ico"

+				>

+			</File>

+			<File

+				RelativePath="res\controlpanel.ico"

+				>

+			</File>

+			<File

+				RelativePath=".\ControlPanel.rc"

+				>

+			</File>

+			<File

+				RelativePath=".\res\ControlPanel.rc2"

+				>

+			</File>

+			<File

+				RelativePath=".\res\EnergySaver.ico"

+				>

+			</File>

+			<File

+				RelativePath="res\failure.ico"

+				>

+			</File>

+			<File

+				RelativePath="res\success.ico"

+				>

+			</File>

+		</Filter>

+		<Filter

+			Name="Support"

+			>

+			<File

+				RelativePath="..\..\Clients\ClientCommon.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\Clients\ClientCommon.h"

+				>

+			</File>

+			<File

+				RelativePath="..\..\mDNSShared\CommonServices.h"

+				>

+			</File>

+			<File

+				RelativePath="..\..\mDNSShared\DebugServices.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\mDNSShared\DebugServices.h"

+				>

+			</File>

+			<File

+				RelativePath="..\..\mDNSShared\dns_sd.h"

+				>

+			</File>

+			<File

+				RelativePath="..\Secret.c"

+				>

+			</File>

+			<File

+				RelativePath="..\Secret.h"

+				>

+			</File>

+			<File

+				RelativePath="..\WinServices.cpp"

+				>

+			</File>

+			<File

+				RelativePath="..\WinServices.h"

+				>

+			</File>

+		</Filter>

+	</Files>

+	<Globals>

+	</Globals>

+</VisualStudioProject>

diff --git a/mdnsresponder/mDNSWindows/ControlPanel/ControlPanelDll.rc b/mdnsresponder/mDNSWindows/ControlPanel/ControlPanelDll.rc
new file mode 100644
index 0000000..5d1f495
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/ControlPanel/ControlPanelDll.rc
@@ -0,0 +1,123 @@
+// Microsoft Visual C++ generated resource script.

+//

+#include "resource.h"

+

+#define APSTUDIO_READONLY_SYMBOLS

+/////////////////////////////////////////////////////////////////////////////

+//

+// Generated from the TEXTINCLUDE 2 resource.

+//

+#include "afxres.h"

+#include "WinVersRes.h"

+

+/////////////////////////////////////////////////////////////////////////////

+#undef APSTUDIO_READONLY_SYMBOLS

+

+/////////////////////////////////////////////////////////////////////////////

+// English (U.S.) resources

+

+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)

+#ifdef _WIN32

+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US

+#pragma code_page(1252)

+#endif //_WIN32

+

+

+/////////////////////////////////////////////////////////////////////////////

+//

+// Version

+//

+

+VS_VERSION_INFO VERSIONINFO

+ FILEVERSION MASTER_PROD_VERS

+ PRODUCTVERSION MASTER_PROD_VERS

+ FILEFLAGSMASK 0x3fL

+#ifdef _DEBUG

+ FILEFLAGS 0x1L

+#else

+ FILEFLAGS 0x0L

+#endif

+ FILEOS 0x4L

+ FILETYPE 0x1L

+ FILESUBTYPE 0x0L

+BEGIN

+    BLOCK "StringFileInfo"

+    BEGIN

+        BLOCK "040904e4"

+        BEGIN

+            VALUE "CompanyName", MASTER_COMPANY_NAME

+            VALUE "FileDescription", "Bonjour Configuration Applet"

+            VALUE "FileVersion", MASTER_PROD_VERS_STR

+            VALUE "InternalName", "Bonjour.cpl"

+            VALUE "LegalCopyright", MASTER_LEGAL_COPYRIGHT

+            VALUE "OriginalFilename", "Bonjour.cpl"

+            VALUE "ProductName", MASTER_PROD_NAME

+            VALUE "ProductVersion", MASTER_PROD_VERS_STR

+        END

+    END

+    BLOCK "VarFileInfo"

+    BEGIN

+        VALUE "Translation", 0x409, 1252

+    END

+END

+

+

+#ifdef APSTUDIO_INVOKED

+/////////////////////////////////////////////////////////////////////////////

+//

+// TEXTINCLUDE

+//

+

+1 TEXTINCLUDE 

+BEGIN

+    "resource.h\0"

+END

+

+2 TEXTINCLUDE 

+BEGIN

+    "#include ""afxres.h""\r\n"

+    "#include ""WinVersRes.h""\r\n"

+    "\0"

+END

+

+3 TEXTINCLUDE 

+BEGIN

+    "#define _AFX_NO_SPLITTER_RESOURCES\r\n"

+    "#define _AFX_NO_OLE_RESOURCES\r\n"

+    "#define _AFX_NO_TRACKER_RESOURCES\r\n"

+    "#define _AFX_NO_PROPERTY_RESOURCES\r\n"

+    "\r\n"

+    "#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\r\n"

+    "LANGUAGE 9, 1\r\n"

+    "#pragma code_page(1252)\r\n"

+    "#include ""afxres.rc""         // Standard components\r\n"

+    "#include ""ControlPanel.rc""\r\n"

+    "#endif\r\n"

+    "\0"

+END

+

+#endif    // APSTUDIO_INVOKED

+

+

+#endif    // English (U.S.) resources

+

+

+#ifndef APSTUDIO_INVOKED

+/////////////////////////////////////////////////////////////////////////////

+//

+// Generated from the TEXTINCLUDE 3 resource.

+//

+#define _AFX_NO_SPLITTER_RESOURCES

+#define _AFX_NO_OLE_RESOURCES

+#define _AFX_NO_TRACKER_RESOURCES

+#define _AFX_NO_PROPERTY_RESOURCES

+

+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)

+LANGUAGE 9, 1

+#pragma code_page(1252)

+#include "afxres.rc"         // Standard components

+#include "ControlPanel.rc"

+#endif

+

+/////////////////////////////////////////////////////////////////////////////

+#endif    // not APSTUDIO_INVOKED

diff --git a/mdnsresponder/mDNSWindows/ControlPanel/ControlPanelExe.cpp b/mdnsresponder/mDNSWindows/ControlPanel/ControlPanelExe.cpp
new file mode 100755
index 0000000..36447d1
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/ControlPanel/ControlPanelExe.cpp
@@ -0,0 +1,373 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2007 Apple Inc. All rights reserved.
+ *
+ * Licensed 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 "ControlPanelExe.h"
+#include "ConfigDialog.h"
+#include "ConfigPropertySheet.h"
+#include "resource.h"
+
+#include <DebugServices.h>
+#include "loclibrary.h"
+
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+#ifndef HeapEnableTerminationOnCorruption
+#	define HeapEnableTerminationOnCorruption (HEAP_INFORMATION_CLASS) 1
+#endif
+
+
+// Stash away pointers to our resource DLLs
+
+static HINSTANCE g_nonLocalizedResources	= NULL;
+static HINSTANCE g_localizedResources		= NULL;
+
+
+HINSTANCE	GetNonLocalizedResources()
+{
+	return g_nonLocalizedResources;
+}
+
+
+HINSTANCE	GetLocalizedResources()
+{
+	return g_localizedResources;
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	Static Declarations
+//---------------------------------------------------------------------------------------------------------------------------
+DEFINE_GUID(CLSID_ControlPanel, 
+
+0x1207552c, 0xe59, 0x4d9f, 0x85, 0x54, 0xf1, 0xf8, 0x6, 0xcd, 0x7f, 0xa9);
+
+static LPCTSTR g_controlPanelGUID			=	TEXT( "{1207552C-0E59-4d9f-8554-F1F806CD7FA9}" );
+static LPCTSTR g_controlPanelName			=	TEXT( "Bonjour" );
+static LPCTSTR g_controlPanelCanonicalName	=	TEXT( "Apple.Bonjour" );
+static LPCTSTR g_controlPanelCategory		=	TEXT( "3,8" );
+
+static CCPApp theApp;
+
+//===========================================================================================================================
+//	MyRegDeleteKey
+//===========================================================================================================================
+
+DEBUG_LOCAL OSStatus MyRegDeleteKey( HKEY hKeyRoot, LPTSTR lpSubKey )
+{
+    LPTSTR lpEnd;
+    OSStatus err;
+    DWORD dwSize;
+    TCHAR szName[MAX_PATH];
+    HKEY hKey;
+    FILETIME ftWrite;
+
+    // First, see if we can delete the key without having to recurse.
+
+    err = RegDeleteKey( hKeyRoot, lpSubKey );
+
+    if ( !err )
+	{
+		goto exit;
+	}
+
+    err = RegOpenKeyEx( hKeyRoot, lpSubKey, 0, KEY_READ, &hKey );
+	require_noerr( err, exit );
+
+    // Check for an ending slash and add one if it is missing.
+
+    lpEnd = lpSubKey + lstrlen(lpSubKey);
+
+    if ( *( lpEnd - 1 ) != TEXT( '\\' ) ) 
+    {
+        *lpEnd =  TEXT('\\');
+        lpEnd++;
+        *lpEnd =  TEXT('\0');
+    }
+
+    // Enumerate the keys
+
+    dwSize = MAX_PATH;
+    err = RegEnumKeyEx(hKey, 0, szName, &dwSize, NULL, NULL, NULL, &ftWrite);
+
+    if ( !err ) 
+    {
+        do
+		{
+            lstrcpy (lpEnd, szName);
+
+            if ( !MyRegDeleteKey( hKeyRoot, lpSubKey ) )
+			{
+                break;
+            }
+
+            dwSize = MAX_PATH;
+
+            err = RegEnumKeyEx( hKey, 0, szName, &dwSize, NULL, NULL, NULL, &ftWrite );
+
+        }
+		while ( !err );
+    }
+
+    lpEnd--;
+    *lpEnd = TEXT('\0');
+
+    RegCloseKey( hKey );
+
+    // Try again to delete the key.
+
+    err = RegDeleteKey(hKeyRoot, lpSubKey);
+	require_noerr( err, exit );
+
+exit:
+
+	return err;
+}
+
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CCPApp::CCPApp
+//---------------------------------------------------------------------------------------------------------------------------
+IMPLEMENT_DYNAMIC(CCPApp, CWinApp);
+
+CCPApp::CCPApp()
+{
+	debug_initialize( kDebugOutputTypeWindowsEventLog, "DNS-SD Control Panel", GetModuleHandle( NULL ) );
+	debug_set_property( kDebugPropertyTagPrintLevel, kDebugLevelInfo );
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CCPApp::~CCPApp
+//---------------------------------------------------------------------------------------------------------------------------
+
+CCPApp::~CCPApp()
+{
+}
+
+
+void
+CCPApp::Register( LPCTSTR inClsidString, LPCTSTR inName, LPCTSTR inCanonicalName, LPCTSTR inCategory, LPCTSTR inLocalizedName, LPCTSTR inInfoTip, LPCTSTR inIconPath, LPCTSTR inExePath )
+{
+	typedef struct	RegistryBuilder		RegistryBuilder;
+	
+	struct	RegistryBuilder
+	{
+		HKEY		rootKey;
+		LPCTSTR		subKey;
+		LPCTSTR		valueName;
+		DWORD		valueType;
+		LPCTSTR		data;
+	};
+	
+	OSStatus			err;
+	size_t				n;
+	size_t				i;
+	HKEY				key;
+	TCHAR				keyName[ MAX_PATH ];
+	RegistryBuilder		entries[] = 
+	{
+		{ HKEY_LOCAL_MACHINE,	TEXT( "SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ControlPanel\\NameSpace\\%s" ),	NULL,									REG_SZ,		inName },
+		{ HKEY_CLASSES_ROOT,	TEXT( "CLSID\\%s" ),																			NULL,									NULL,		NULL },
+		{ HKEY_CLASSES_ROOT,	TEXT( "CLSID\\%s" ),																			TEXT( "System.ApplicationName" ),		REG_SZ,		inCanonicalName },
+		{ HKEY_CLASSES_ROOT,	TEXT( "CLSID\\%s" ),																			TEXT( "System.ControlPanel.Category" ),	REG_SZ,		inCategory },
+		{ HKEY_CLASSES_ROOT,	TEXT( "CLSID\\%s" ),																			TEXT( "LocalizedString" ),				REG_SZ,		inLocalizedName },
+		{ HKEY_CLASSES_ROOT,	TEXT( "CLSID\\%s" ),																			TEXT( "InfoTip" ),						REG_SZ,		inInfoTip },
+		{ HKEY_CLASSES_ROOT,	TEXT( "CLSID\\%s\\DefaultIcon" ),																NULL,									REG_SZ,		inIconPath },
+		{ HKEY_CLASSES_ROOT,	TEXT( "CLSID\\%s\\Shell" ),																		NULL,									NULL,		NULL },
+		{ HKEY_CLASSES_ROOT,	TEXT( "CLSID\\%s\\Shell\\Open" ),																NULL,									NULL,		NULL },
+		{ HKEY_CLASSES_ROOT,	TEXT( "CLSID\\%s\\Shell\\Open\\Command" ),														NULL,									REG_SZ,		inExePath }
+	};
+	DWORD				size;
+	
+	// Register the registry entries.
+
+	n = sizeof_array( entries );
+	for( i = 0; i < n; ++i )
+	{
+		wsprintf( keyName, entries[ i ].subKey, inClsidString );		
+		err = RegCreateKeyEx( entries[ i ].rootKey, keyName, 0, NULL, REG_OPTION_NON_VOLATILE, KEY_WRITE, NULL, &key, NULL );
+		require_noerr( err, exit );
+		
+		if ( entries[ i ].data )
+		{
+			size = (DWORD)( ( lstrlen( entries[ i ].data ) + 1 ) * sizeof( TCHAR ) );
+			err = RegSetValueEx( key, entries[ i ].valueName, 0, entries[ i ].valueType, (LPBYTE) entries[ i ].data, size );
+			require_noerr( err, exit );
+		}
+
+		RegCloseKey( key );
+	}
+	
+exit:
+	return;
+}
+
+
+//-----------------------------------------------------------
+//	CCPApp::Unregister
+//-----------------------------------------------------------
+void
+CCPApp::Unregister( LPCTSTR clsidString )
+{
+	TCHAR keyName[ MAX_PATH * 2 ];
+
+	wsprintf( keyName, L"SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Explorer\\ControlPanel\\NameSpace\\%s", clsidString );							
+	MyRegDeleteKey( HKEY_LOCAL_MACHINE, keyName );
+
+	wsprintf( keyName, L"CLSID\\%s", clsidString );
+	MyRegDeleteKey( HKEY_CLASSES_ROOT, keyName );
+}
+
+
+
+//-----------------------------------------------------------
+//	CCPApp::InitInstance
+//-----------------------------------------------------------
+
+BOOL
+CCPApp::InitInstance()
+{
+	CCommandLineInfo	commandLine;
+	wchar_t				resource[MAX_PATH];
+	CString				errorMessage;
+	CString				errorCaption;
+	int					res;
+	OSStatus			err = kNoErr;
+
+	HeapSetInformation( NULL, HeapEnableTerminationOnCorruption, NULL, 0 );
+
+	//
+	// initialize the debugging framework
+	//
+	debug_initialize( kDebugOutputTypeWindowsDebugger, "ControlPanel", NULL );
+	debug_set_property( kDebugPropertyTagPrintLevel, kDebugLevelTrace );
+
+	// Before we load the resources, let's load the error string
+
+	errorMessage.LoadString( IDS_REINSTALL );
+	errorCaption.LoadString( IDS_REINSTALL_CAPTION );
+
+	res = PathForResource( NULL, L"ControlPanelResources.dll", resource, MAX_PATH );
+	err = translate_errno( res != 0, kUnknownErr, kUnknownErr );
+	require_noerr( err, exit );
+
+	g_nonLocalizedResources = LoadLibrary( resource );
+	translate_errno( g_nonLocalizedResources, GetLastError(), kUnknownErr );
+	require_noerr( err, exit );
+
+	res = PathForResource( NULL, L"ControlPanelLocalized.dll", resource, MAX_PATH );
+	err = translate_errno( res != 0, kUnknownErr, kUnknownErr );
+	require_noerr( err, exit );
+
+	g_localizedResources = LoadLibrary( resource );
+	translate_errno( g_localizedResources, GetLastError(), kUnknownErr );
+	require_noerr( err, exit );
+
+	AfxSetResourceHandle( g_localizedResources );
+
+	// InitCommonControls() is required on Windows XP if an application
+	// manifest specifies use of ComCtl32.dll version 6 or later to enable
+	// visual styles.  Otherwise, any window creation will fail.
+
+	InitCommonControls();
+
+	CWinApp::InitInstance();
+
+	AfxEnableControlContainer();
+
+	ParseCommandLine( commandLine );
+
+	if ( commandLine.m_nShellCommand == CCommandLineInfo::AppRegister )
+	{
+		CString		localizedName;
+		CString		toolTip;
+		TCHAR		iconPath[ MAX_PATH + 12 ]	= TEXT( "" );
+		TCHAR		exePath[ MAX_PATH ]			= TEXT( "" );
+		DWORD		nChars;
+		OSStatus	err;
+
+		nChars = GetModuleFileName( NULL, exePath, sizeof_array( exePath ) );
+
+		err = translate_errno( nChars > 0, (OSStatus) GetLastError(), kUnknownErr );
+
+		require_noerr( err, exit );
+
+		wsprintf( iconPath, L"%s,-%d", exePath, IDR_APPLET );
+
+		localizedName.LoadString( IDS_APPLET_NAME );
+		toolTip.LoadString( IDS_APPLET_TOOLTIP );
+
+		Register( g_controlPanelGUID, g_controlPanelName, g_controlPanelCanonicalName, g_controlPanelCategory, localizedName, toolTip, iconPath, exePath );
+	}
+	else if ( commandLine.m_nShellCommand == CCommandLineInfo::AppUnregister )
+	{
+		Unregister( g_controlPanelGUID );
+	}
+	else
+	{
+		CString					name;
+		CConfigPropertySheet	dlg;
+		
+		name.LoadString( IDR_APPLET );
+		dlg.Construct( name, NULL, 0 );
+
+		m_pMainWnd = &dlg;
+
+		try
+		{
+			INT_PTR nResponse = dlg.DoModal();
+		
+			if (nResponse == IDOK)
+			{
+				// TODO: Place code here to handle when the dialog is
+				//  dismissed with OK
+			}
+			else if (nResponse == IDCANCEL)
+			{
+				// TODO: Place code here to handle when the dialog is
+				//  dismissed with Cancel
+			}
+		}
+		catch (...)
+		{
+			MessageBox(NULL, L"", L"", MB_OK|MB_ICONEXCLAMATION);
+		}
+	}
+
+	if ( err )
+	{
+		MessageBox( NULL, L"", L"", MB_ICONERROR | MB_OK );
+	}
+
+exit:
+
+	if ( err )
+	{
+		MessageBox( NULL, errorMessage, errorCaption, MB_ICONERROR | MB_OK );
+	}
+
+	// Since the dialog has been closed, return FALSE so that we exit the
+	//  application, rather than start the application's message pump.
+	return FALSE;
+}
diff --git a/mdnsresponder/mDNSWindows/ControlPanel/ControlPanelExe.h b/mdnsresponder/mDNSWindows/ControlPanel/ControlPanelExe.h
new file mode 100644
index 0000000..079422b
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/ControlPanel/ControlPanelExe.h
@@ -0,0 +1,48 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2007 Apple Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+    
+#pragma once
+
+#include "stdafx.h"

+
+extern HINSTANCE	GetNonLocalizedResources();
+extern HINSTANCE	GetLocalizedResources();
+
+//-------------------------------------------------
+//	CCPApp
+//-------------------------------------------------
+
+class CCPApp : public CWinApp
+{
+public:
+
+	CCPApp();
+	virtual ~CCPApp();
+
+protected:
+
+	virtual BOOL    InitInstance();
+
+	void
+	Register( LPCTSTR inClsidString, LPCTSTR inName, LPCTSTR inCanonicalName, LPCTSTR inCategory, LPCTSTR inLocalizedName, LPCTSTR inInfoTip, LPCTSTR inIconPath, LPCTSTR inExePath );
+
+	void
+	Unregister( LPCTSTR clsidString );
+
+	DECLARE_DYNAMIC(CCPApp);
+};
diff --git a/mdnsresponder/mDNSWindows/ControlPanel/ControlPanelExe.rc b/mdnsresponder/mDNSWindows/ControlPanel/ControlPanelExe.rc
new file mode 100644
index 0000000..4ea5f95
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/ControlPanel/ControlPanelExe.rc
@@ -0,0 +1,123 @@
+// Microsoft Visual C++ generated resource script.

+//

+#include "resource.h"

+

+#define APSTUDIO_READONLY_SYMBOLS

+/////////////////////////////////////////////////////////////////////////////

+//

+// Generated from the TEXTINCLUDE 2 resource.

+//

+#include "afxres.h"

+#include "WinVersRes.h"

+

+/////////////////////////////////////////////////////////////////////////////

+#undef APSTUDIO_READONLY_SYMBOLS

+

+/////////////////////////////////////////////////////////////////////////////

+// English (U.S.) resources

+

+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)

+#ifdef _WIN32

+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US

+#pragma code_page(1252)

+#endif //_WIN32

+

+

+/////////////////////////////////////////////////////////////////////////////

+//

+// Version

+//

+

+VS_VERSION_INFO VERSIONINFO

+ FILEVERSION MASTER_PROD_VERS

+ PRODUCTVERSION MASTER_PROD_VERS

+ FILEFLAGSMASK 0x3fL

+#ifdef _DEBUG

+ FILEFLAGS 0x1L

+#else

+ FILEFLAGS 0x0L

+#endif

+ FILEOS 0x4L

+ FILETYPE 0x1L

+ FILESUBTYPE 0x0L

+BEGIN

+    BLOCK "StringFileInfo"

+    BEGIN

+        BLOCK "040904e4"

+        BEGIN

+            VALUE "CompanyName", MASTER_COMPANY_NAME

+            VALUE "FileDescription", "Bonjour Configuration Applet"

+            VALUE "FileVersion", MASTER_PROD_VERS_STR

+            VALUE "InternalName", "ControlPanel.exe"

+            VALUE "LegalCopyright", MASTER_LEGAL_COPYRIGHT

+            VALUE "OriginalFilename", "ControlPanel.exe"

+            VALUE "ProductName", MASTER_PROD_NAME

+            VALUE "ProductVersion", MASTER_PROD_VERS_STR

+        END

+    END

+    BLOCK "VarFileInfo"

+    BEGIN

+        VALUE "Translation", 0x409, 1252

+    END

+END

+

+

+#ifdef APSTUDIO_INVOKED

+/////////////////////////////////////////////////////////////////////////////

+//

+// TEXTINCLUDE

+//

+

+1 TEXTINCLUDE 

+BEGIN

+    "resource.h\0"

+END

+

+2 TEXTINCLUDE 

+BEGIN

+    "#include ""afxres.h""\r\n"

+    "#include ""WinVersRes.h""\r\n"

+    "\0"

+END

+

+3 TEXTINCLUDE 

+BEGIN

+    "#define _AFX_NO_SPLITTER_RESOURCES\r\n"

+    "#define _AFX_NO_OLE_RESOURCES\r\n"

+    "#define _AFX_NO_TRACKER_RESOURCES\r\n"

+    "#define _AFX_NO_PROPERTY_RESOURCES\r\n"

+    "\r\n"

+    "#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\r\n"

+    "LANGUAGE 9, 1\r\n"

+    "#pragma code_page(1252)\r\n"

+    "#include ""afxres.rc""         // Standard components\r\n"

+    "#include ""ControlPanel.rc""\r\n"

+    "#endif\r\n"

+    "\0"

+END

+

+#endif    // APSTUDIO_INVOKED

+

+

+#endif    // English (U.S.) resources

+

+

+#ifndef APSTUDIO_INVOKED

+/////////////////////////////////////////////////////////////////////////////

+//

+// Generated from the TEXTINCLUDE 3 resource.

+//

+#define _AFX_NO_SPLITTER_RESOURCES

+#define _AFX_NO_OLE_RESOURCES

+#define _AFX_NO_TRACKER_RESOURCES

+#define _AFX_NO_PROPERTY_RESOURCES

+

+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)

+LANGUAGE 9, 1

+#pragma code_page(1252)

+#include "afxres.rc"         // Standard components

+#include "ControlPanel.rc"

+#endif

+

+/////////////////////////////////////////////////////////////////////////////

+#endif    // not APSTUDIO_INVOKED

diff --git a/mdnsresponder/mDNSWindows/ControlPanel/ControlPanelExe.vcproj b/mdnsresponder/mDNSWindows/ControlPanel/ControlPanelExe.vcproj
new file mode 100755
index 0000000..8bc0b29
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/ControlPanel/ControlPanelExe.vcproj
@@ -0,0 +1,764 @@
+<?xml version="1.0" encoding="Windows-1252"?>

+<VisualStudioProject

+	ProjectType="Visual C++"

+	Version="8.00"

+	Name="ControlPanel (Vista)"

+	ProjectGUID="{0DF09484-B4C2-4AB4-9FC0-7B091ADEAFEB}"

+	Keyword="MFCProj"

+	>

+	<Platforms>

+		<Platform

+			Name="Win32"

+		/>

+		<Platform

+			Name="x64"

+		/>

+	</Platforms>

+	<ToolFiles>

+	</ToolFiles>

+	<Configurations>

+		<Configuration

+			Name="Debug|Win32"

+			OutputDirectory="ExeBuild\$(PlatformName)\$(ConfigurationName)"

+			IntermediateDirectory="ExeBuild\$(PlatformName)\$(ConfigurationName)"

+			ConfigurationType="1"

+			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"

+			UseOfMFC="1"

+			ATLMinimizesCRunTimeLibraryUsage="false"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+				PreprocessorDefinitions="_DEBUG"

+				MkTypLibCompatible="false"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				Optimization="0"

+				AdditionalIncludeDirectories="..;../../mDNSCore;../../mDNSShared"

+				PreprocessorDefinitions="WIN32;_DEBUG;DEBUG=1;UNICODE;_UNICODE;_WINDOWS;WINVER=0x0501;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1"

+				StringPooling="true"

+				MinimalRebuild="true"

+				BasicRuntimeChecks="3"

+				RuntimeLibrary="1"

+				UsePrecompiledHeader="0"

+				PrecompiledHeaderThrough=""

+				PrecompiledHeaderFile=""

+				AssemblerListingLocation="$(IntDir)\"

+				ObjectFile="$(IntDir)\"

+				ProgramDataBaseFileName="$(IntDir)\vc80.pdb"

+				WarningLevel="4"

+				SuppressStartupBanner="true"

+				Detect64BitPortabilityProblems="true"

+				DebugInformationFormat="3"

+				CallingConvention="0"

+				DisableSpecificWarnings="4311;4312"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+				PreprocessorDefinitions="_DEBUG"

+				Culture="1033"

+				AdditionalIncludeDirectories="../"

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				AdditionalOptions="/NXCOMPAT /DYNAMICBASE /SAFESEH"

+				AdditionalDependencies="../DLL/$(PlatformName)/$(ConfigurationName)/dnssd.lib ws2_32.lib"

+				OutputFile="$(OutDir)/ControlPanel.exe"

+				LinkIncremental="2"

+				SuppressStartupBanner="true"

+				GenerateDebugInformation="true"

+				ProgramDatabaseFile="$(OutDir)\ControlPanel.pdb"

+				SubSystem="2"

+				EntryPointSymbol="wWinMainCRTStartup"

+				TargetMachine="1"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+				AdditionalManifestFiles="res\ControlPanel.manifest"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+			/>

+		</Configuration>

+		<Configuration

+			Name="Debug|x64"

+			OutputDirectory="ExeBuild\$(PlatformName)\$(ConfigurationName)"

+			IntermediateDirectory="ExeBuild\$(PlatformName)\$(ConfigurationName)"

+			ConfigurationType="1"

+			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"

+			UseOfMFC="1"

+			ATLMinimizesCRunTimeLibraryUsage="false"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+				PreprocessorDefinitions="_DEBUG"

+				MkTypLibCompatible="false"

+				TargetEnvironment="3"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				Optimization="0"

+				AdditionalIncludeDirectories="..;../../mDNSCore;../../mDNSShared"

+				PreprocessorDefinitions="WIN32;_DEBUG;DEBUG=1;UNICODE;_UNICODE;_WINDOWS;WINVER=0x0501;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1"

+				StringPooling="true"

+				MinimalRebuild="true"

+				BasicRuntimeChecks="3"

+				RuntimeLibrary="1"

+				UsePrecompiledHeader="0"

+				PrecompiledHeaderThrough=""

+				PrecompiledHeaderFile=""

+				AssemblerListingLocation="$(IntDir)\"

+				ObjectFile="$(IntDir)\"

+				ProgramDataBaseFileName="$(IntDir)\vc80.pdb"

+				WarningLevel="4"

+				SuppressStartupBanner="true"

+				Detect64BitPortabilityProblems="true"

+				DebugInformationFormat="3"

+				CallingConvention="0"

+				DisableSpecificWarnings="4311;4312"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+				PreprocessorDefinitions="_DEBUG"

+				Culture="1033"

+				AdditionalIncludeDirectories="../"

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				AdditionalOptions="/NXCOMPAT /DYNAMICBASE"

+				AdditionalDependencies="../DLL/$(PlatformName)/$(ConfigurationName)/dnssd.lib ws2_32.lib"

+				OutputFile="$(OutDir)/ControlPanel.exe"

+				LinkIncremental="2"

+				SuppressStartupBanner="true"

+				GenerateDebugInformation="true"

+				ProgramDatabaseFile="$(OutDir)\ControlPanel.pdb"

+				SubSystem="2"

+				EntryPointSymbol="wWinMainCRTStartup"

+				TargetMachine="17"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+				AdditionalManifestFiles="res\ControlPanel64.manifest"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+			/>

+		</Configuration>

+		<Configuration

+			Name="Release|Win32"

+			OutputDirectory="ExeBuild\$(PlatformName)\$(ConfigurationName)"

+			IntermediateDirectory="ExeBuild\$(PlatformName)\$(ConfigurationName)"

+			ConfigurationType="1"

+			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"

+			UseOfMFC="1"

+			CharacterSet="2"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+				PreprocessorDefinitions="NDEBUG"

+				MkTypLibCompatible="false"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				Optimization="2"

+				InlineFunctionExpansion="1"

+				AdditionalIncludeDirectories="..;../../mDNSCore;../../mDNSShared"

+				PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;WINVER=0x0501;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1"

+				StringPooling="true"

+				MinimalRebuild="false"

+				RuntimeLibrary="0"

+				EnableFunctionLevelLinking="true"

+				TreatWChar_tAsBuiltInType="true"

+				UsePrecompiledHeader="0"

+				AssemblerListingLocation="$(IntDir)\"

+				ObjectFile="$(IntDir)\"

+				ProgramDataBaseFileName="$(IntDir)\vc80.pdb"

+				WarningLevel="4"

+				SuppressStartupBanner="true"

+				DisableSpecificWarnings="4702"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+				PreprocessorDefinitions="NDEBUG"

+				Culture="1033"

+				AdditionalIncludeDirectories="../"

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				AdditionalOptions="/NXCOMPAT /DYNAMICBASE /SAFESEH"

+				AdditionalDependencies="../DLL/$(PlatformName)/$(ConfigurationName)/dnssd.lib ws2_32.lib"

+				OutputFile="$(OutDir)/ControlPanel.exe"

+				LinkIncremental="1"

+				SuppressStartupBanner="true"

+				ProgramDatabaseFile="$(OutDir)\ControlPanel.pdb"

+				SubSystem="2"

+				OptimizeReferences="0"

+				EnableCOMDATFolding="0"

+				EntryPointSymbol="wWinMainCRTStartup"

+				TargetMachine="1"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+				AdditionalManifestFiles="res\ControlPanel.manifest"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+			/>

+		</Configuration>

+		<Configuration

+			Name="Release|x64"

+			OutputDirectory="ExeBuild\$(PlatformName)\$(ConfigurationName)"

+			IntermediateDirectory="ExeBuild\$(PlatformName)\$(ConfigurationName)"

+			ConfigurationType="1"

+			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"

+			UseOfMFC="1"

+			CharacterSet="2"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+				PreprocessorDefinitions="NDEBUG"

+				MkTypLibCompatible="false"

+				TargetEnvironment="3"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				Optimization="2"

+				InlineFunctionExpansion="1"

+				AdditionalIncludeDirectories="..;../../mDNSCore;../../mDNSShared"

+				PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;WINVER=0x0501;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1"

+				StringPooling="true"

+				MinimalRebuild="false"

+				RuntimeLibrary="0"

+				EnableFunctionLevelLinking="true"

+				TreatWChar_tAsBuiltInType="true"

+				UsePrecompiledHeader="0"

+				AssemblerListingLocation="$(IntDir)\"

+				ObjectFile="$(IntDir)\"

+				ProgramDataBaseFileName="$(IntDir)\vc80.pdb"

+				WarningLevel="4"

+				SuppressStartupBanner="true"

+				DisableSpecificWarnings="4702"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+				PreprocessorDefinitions="NDEBUG"

+				Culture="1033"

+				AdditionalIncludeDirectories="../"

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				AdditionalOptions="/NXCOMPAT /DYNAMICBASE"

+				AdditionalDependencies="../DLL/$(PlatformName)/$(ConfigurationName)/dnssd.lib ws2_32.lib"

+				OutputFile="$(OutDir)/ControlPanel.exe"

+				LinkIncremental="1"

+				SuppressStartupBanner="true"

+				ProgramDatabaseFile="$(OutDir)\ControlPanel.pdb"

+				SubSystem="2"

+				OptimizeReferences="0"

+				EnableCOMDATFolding="0"

+				EntryPointSymbol="wWinMainCRTStartup"

+				TargetMachine="17"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+				AdditionalManifestFiles="res\ControlPanel64.manifest"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+			/>

+		</Configuration>

+	</Configurations>

+	<References>

+	</References>

+	<Files>

+		<Filter

+			Name="Source Files"

+			Filter="cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"

+			>

+			<File

+				RelativePath="ConfigDialog.cpp"

+				>

+				<FileConfiguration

+					Name="Debug|Win32"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						Optimization="0"

+						PreprocessorDefinitions=""

+					/>

+				</FileConfiguration>

+				<FileConfiguration

+					Name="Debug|x64"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						Optimization="0"

+						PreprocessorDefinitions=""

+					/>

+				</FileConfiguration>

+				<FileConfiguration

+					Name="Release|Win32"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						Optimization="2"

+						PreprocessorDefinitions=""

+					/>

+				</FileConfiguration>

+				<FileConfiguration

+					Name="Release|x64"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						Optimization="2"

+						PreprocessorDefinitions=""

+					/>

+				</FileConfiguration>

+			</File>

+			<File

+				RelativePath="ConfigPropertySheet.cpp"

+				>

+				<FileConfiguration

+					Name="Debug|Win32"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						Optimization="0"

+						PreprocessorDefinitions=""

+					/>

+				</FileConfiguration>

+				<FileConfiguration

+					Name="Debug|x64"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						Optimization="0"

+						PreprocessorDefinitions=""

+					/>

+				</FileConfiguration>

+				<FileConfiguration

+					Name="Release|Win32"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						Optimization="2"

+						PreprocessorDefinitions=""

+					/>

+				</FileConfiguration>

+				<FileConfiguration

+					Name="Release|x64"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						Optimization="2"

+						PreprocessorDefinitions=""

+					/>

+				</FileConfiguration>

+			</File>

+			<File

+				RelativePath=".\ControlPanelExe.cpp"

+				>

+			</File>

+			<File

+				RelativePath=".\FifthPage.cpp"

+				>

+			</File>

+			<File

+				RelativePath="FirstPage.cpp"

+				>

+				<FileConfiguration

+					Name="Debug|Win32"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						Optimization="0"

+						PreprocessorDefinitions=""

+					/>

+				</FileConfiguration>

+				<FileConfiguration

+					Name="Debug|x64"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						Optimization="0"

+						PreprocessorDefinitions=""

+					/>

+				</FileConfiguration>

+				<FileConfiguration

+					Name="Release|Win32"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						Optimization="2"

+						PreprocessorDefinitions=""

+					/>

+				</FileConfiguration>

+				<FileConfiguration

+					Name="Release|x64"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						Optimization="2"

+						PreprocessorDefinitions=""

+					/>

+				</FileConfiguration>

+			</File>

+			<File

+				RelativePath=".\FourthPage.cpp"

+				>

+			</File>

+			<File

+				RelativePath="SecondPage.cpp"

+				>

+				<FileConfiguration

+					Name="Debug|Win32"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						Optimization="0"

+						PreprocessorDefinitions=""

+					/>

+				</FileConfiguration>

+				<FileConfiguration

+					Name="Debug|x64"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						Optimization="0"

+						PreprocessorDefinitions=""

+					/>

+				</FileConfiguration>

+				<FileConfiguration

+					Name="Release|Win32"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						Optimization="2"

+						PreprocessorDefinitions=""

+					/>

+				</FileConfiguration>

+				<FileConfiguration

+					Name="Release|x64"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						Optimization="2"

+						PreprocessorDefinitions=""

+					/>

+				</FileConfiguration>

+			</File>

+			<File

+				RelativePath="SharedSecret.cpp"

+				>

+			</File>

+			<File

+				RelativePath="stdafx.cpp"

+				>

+				<FileConfiguration

+					Name="Debug|Win32"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						Optimization="0"

+						PreprocessorDefinitions=""

+						UsePrecompiledHeader="0"

+					/>

+				</FileConfiguration>

+				<FileConfiguration

+					Name="Debug|x64"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						Optimization="0"

+						PreprocessorDefinitions=""

+						UsePrecompiledHeader="0"

+					/>

+				</FileConfiguration>

+				<FileConfiguration

+					Name="Release|Win32"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						Optimization="2"

+						PreprocessorDefinitions=""

+						UsePrecompiledHeader="0"

+					/>

+				</FileConfiguration>

+				<FileConfiguration

+					Name="Release|x64"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						Optimization="2"

+						PreprocessorDefinitions=""

+						UsePrecompiledHeader="0"

+					/>

+				</FileConfiguration>

+			</File>

+			<File

+				RelativePath="ThirdPage.cpp"

+				>

+			</File>

+		</Filter>

+		<Filter

+			Name="Header Files"

+			Filter="h;hpp;hxx;hm;inl"

+			>

+			<File

+				RelativePath="ConfigDialog.h"

+				>

+			</File>

+			<File

+				RelativePath="ConfigPropertySheet.h"

+				>

+			</File>

+			<File

+				RelativePath=".\ControlPanelExe.h"

+				>

+			</File>

+			<File

+				RelativePath=".\FifthPage.h"

+				>

+			</File>

+			<File

+				RelativePath="FirstPage.h"

+				>

+			</File>

+			<File

+				RelativePath=".\FourthPage.h"

+				>

+			</File>

+			<File

+				RelativePath="Resource.h"

+				>

+			</File>

+			<File

+				RelativePath="SecondPage.h"

+				>

+			</File>

+			<File

+				RelativePath="SharedSecret.h"

+				>

+			</File>

+			<File

+				RelativePath="stdafx.h"

+				>

+			</File>

+			<File

+				RelativePath="ThirdPage.h"

+				>

+			</File>

+		</Filter>

+		<Filter

+			Name="Resource Files"

+			Filter="ico;cur;bmp;dlg;rc2;rct;bin;cnt;rtf;gif;jpg;jpeg;jpe"

+			>

+			<File

+				RelativePath="res\configurator.ico"

+				>

+			</File>

+			<File

+				RelativePath="res\controlpanel.ico"

+				>

+			</File>

+			<File

+				RelativePath=".\res\ControlPanel.rc2"

+				>

+			</File>

+			<File

+				RelativePath=".\ControlPanelExe.rc"

+				>

+			</File>

+			<File

+				RelativePath="res\failure.ico"

+				>

+			</File>

+			<File

+				RelativePath="res\success.ico"

+				>

+			</File>

+		</Filter>

+		<Filter

+			Name="Support"

+			>

+			<File

+				RelativePath="..\..\mDNSShared\CommonServices.h"

+				>

+			</File>

+			<File

+				RelativePath="..\..\mDNSShared\DebugServices.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\mDNSShared\DebugServices.h"

+				>

+			</File>

+			<File

+				RelativePath="..\..\mDNSShared\dns_sd.h"

+				>

+			</File>

+			<File

+				RelativePath="..\Secret.c"

+				>

+			</File>

+			<File

+				RelativePath="..\Secret.h"

+				>

+			</File>

+			<File

+				RelativePath="..\WinServices.cpp"

+				>

+			</File>

+			<File

+				RelativePath="..\WinServices.h"

+				>

+			</File>

+		</Filter>

+	</Files>

+	<Globals>

+	</Globals>

+</VisualStudioProject>

diff --git a/mdnsresponder/mDNSWindows/ControlPanel/ControlPanelLocRes.rc b/mdnsresponder/mDNSWindows/ControlPanel/ControlPanelLocRes.rc
new file mode 100755
index 0000000..4ba22b4
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/ControlPanel/ControlPanelLocRes.rc
@@ -0,0 +1,270 @@
+// Microsoft Visual C++ generated resource script.

+//

+#include "resource.h"

+

+#define APSTUDIO_READONLY_SYMBOLS

+/////////////////////////////////////////////////////////////////////////////

+//

+// Generated from the TEXTINCLUDE 2 resource.

+//

+#include "afxres.h"

+#include "WinVersRes.h"

+

+/////////////////////////////////////////////////////////////////////////////

+#undef APSTUDIO_READONLY_SYMBOLS

+

+/////////////////////////////////////////////////////////////////////////////

+// English (U.S.) resources

+

+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)

+#ifdef _WIN32

+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US

+#pragma code_page(1252)

+#endif //_WIN32

+

+#ifdef APSTUDIO_INVOKED

+/////////////////////////////////////////////////////////////////////////////

+//

+// TEXTINCLUDE

+//

+

+1 TEXTINCLUDE 

+BEGIN

+    "resource.h\0"

+END

+

+2 TEXTINCLUDE 

+BEGIN

+    "#include ""afxres.h""\r\n"

+    "#include ""WinVersRes.h""\r\n"

+    "\0"

+END

+

+3 TEXTINCLUDE 

+BEGIN

+    "#define _AFX_NO_SPLITTER_RESOURCES\r\n"

+    "#define _AFX_NO_OLE_RESOURCES\r\n"

+    "#define _AFX_NO_TRACKER_RESOURCES\r\n"

+    "#define _AFX_NO_PROPERTY_RESOURCES\r\n"

+    "\r\n"

+    "#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\r\n"

+    "LANGUAGE 9, 1\r\n"

+    "#pragma code_page(1252)\r\n"

+    "#include ""afxres.rc""     // Standard components\r\n"

+    "#endif\r\n"

+    "\0"

+END

+

+#endif    // APSTUDIO_INVOKED

+

+

+/////////////////////////////////////////////////////////////////////////////

+//

+// Version

+//

+

+VS_VERSION_INFO VERSIONINFO

+ FILEVERSION MASTER_PROD_VERS

+ PRODUCTVERSION MASTER_PROD_VERS

+ FILEFLAGSMASK 0x3fL

+#ifdef _DEBUG

+ FILEFLAGS 0x1L

+#else

+ FILEFLAGS 0x0L

+#endif

+ FILEOS 0x4L

+ FILETYPE 0x2L

+ FILESUBTYPE 0x0L

+BEGIN

+    BLOCK "StringFileInfo"

+    BEGIN

+        BLOCK "040904b0"

+        BEGIN

+            VALUE "CompanyName", MASTER_COMPANY_NAME

+            VALUE "FileDescription", "Bonjour Resource Module"

+            VALUE "FileVersion", MASTER_PROD_VERS_STR

+            VALUE "InternalName", "ControlPanelLocalized.dll"

+            VALUE "LegalCopyright", MASTER_LEGAL_COPYRIGHT

+            VALUE "OriginalFilename", "ControlPanelLocalized.dll"

+            VALUE "ProductName", MASTER_PROD_NAME

+            VALUE "ProductVersion", MASTER_PROD_VERS_STR

+        END

+    END

+    BLOCK "VarFileInfo"

+    BEGIN

+        VALUE "Translation", 0x409, 1200

+    END

+END

+

+

+/////////////////////////////////////////////////////////////////////////////

+//

+// Dialog

+//

+

+IDR_APPLET_PAGE1 DIALOGEX 0, 0, 262, 140

+STYLE DS_SETFONT | WS_CHILD | WS_CAPTION

+CAPTION "Registration"

+FONT 8, "MS Sans Serif", 0, 0, 0x0

+BEGIN

+    LTEXT           "Hostname:",IDC_STATIC,13,22,35,8

+    EDITTEXT        IDC_HOSTNAME,55,20,184,12,ES_AUTOHSCROLL

+    LTEXT           "User:",IDC_STATIC,13,38,35,8

+    EDITTEXT        IDC_USERNAME,55,36,184,12,ES_AUTOHSCROLL

+    LTEXT           "Password:",IDC_STATIC,13,54,35,8

+    EDITTEXT        IDC_PASSWORD,55,52,184,12,ES_PASSWORD | ES_AUTOHSCROLL

+    CONTROL         "Advertise services in this domain using Bonjour",IDC_ADVERTISE_SERVICES,

+                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,55,80,199,8

+END

+

+IDR_APPLET_PAGE3 DIALOGEX 0, 0, 262, 140

+STYLE DS_SETFONT | WS_CHILD | WS_CAPTION

+CAPTION "Browsing"

+FONT 8, "MS Sans Serif", 0, 0, 0x0

+BEGIN

+    LTEXT           "Choose which domains to browse using wide-area Bonjour",

+                    -1,7,16,248,12

+    CONTROL         "",IDC_BROWSE_LIST,"SysListView32",LVS_REPORT | 

+                    LVS_ALIGNLEFT | LVS_NOCOLUMNHEADER | WS_BORDER | 

+                    WS_TABSTOP,7,37,248,57

+    PUSHBUTTON      "Add",IDC_ADD_BROWSE_DOMAIN,152,100,50,14

+    PUSHBUTTON      "Remove",IDC_REMOVE_BROWSE_DOMAIN,205,100,50,14

+END

+

+IDR_APPLET_PAGE5 DIALOGEX 0, 0, 262, 140

+STYLE DS_SETFONT | WS_CHILD | WS_CAPTION

+CAPTION "Services"

+FONT 8, "MS Sans Serif", 0, 0, 0x0

+BEGIN

+    CONTROL         "Advertise shared folders using Bonjour",IDC_ADVERTISE_SMB,

+                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,13,19,199,8

+    CONTROL         "Enable Wake on Demand",IDC_POWER_MANAGEMENT,

+                    "Button",BS_AUTOCHECKBOX | WS_TABSTOP,13,34,199,8

+END

+

+IDR_POWER_MANAGEMENT_WARNING DIALOGEX 0, 0, 230, 95

+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | 

+    WS_SYSMENU

+CAPTION "Power Management"

+FONT 8, "MS Shell Dlg", 400, 0, 0x1

+BEGIN

+    DEFPUSHBUTTON   "OK",IDOK,173,74,50,14

+    LTEXT           "When 'Wake On Demand' is enabled, you may hear your computer wake up occasionally.",

+					IDC_STATIC,50,12,175,26

+    LTEXT           "This informs other devices that your computer is still available on the network.",

+                    IDC_STATIC,50,38,175,26

+    ICON            IDI_ENERGY_SAVER,IDC_ENERGY_SAVER,2,10,64,64,SS_REALSIZEIMAGE

+END

+

+IDR_ADD_BROWSE_DOMAIN DIALOGEX 0, 0, 230, 95

+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION | 

+    WS_SYSMENU

+CAPTION "Add Browse Domain"

+FONT 8, "MS Shell Dlg", 400, 0, 0x1

+BEGIN

+    DEFPUSHBUTTON   "OK",IDOK,117,74,50,14

+    PUSHBUTTON      "Cancel",IDCANCEL,173,74,50,14

+    COMBOBOX        IDC_COMBO1,35,42,188,100,CBS_DROPDOWN | CBS_SORT | 

+                    WS_VSCROLL | WS_TABSTOP

+    LTEXT           "Domain:",IDC_STATIC,7,43,27,8

+    LTEXT           "The following domain will be added to your list of Bonjour browse domains.",

+                    IDC_STATIC,7,15,216,16

+END

+

+

+/////////////////////////////////////////////////////////////////////////////

+//

+// DESIGNINFO

+//

+

+#ifdef APSTUDIO_INVOKED

+GUIDELINES DESIGNINFO 

+BEGIN

+    IDR_APPLET_PAGE1, DIALOG

+    BEGIN

+        LEFTMARGIN, 7

+        RIGHTMARGIN, 255

+        TOPMARGIN, 7

+        BOTTOMMARGIN, 133

+    END

+

+    IDR_APPLET_PAGE2, DIALOG

+    BEGIN

+        LEFTMARGIN, 7

+        RIGHTMARGIN, 255

+        TOPMARGIN, 7

+        BOTTOMMARGIN, 133

+    END

+

+    IDR_SECRET, DIALOG

+    BEGIN

+        LEFTMARGIN, 7

+        RIGHTMARGIN, 244

+        TOPMARGIN, 7

+        BOTTOMMARGIN, 83

+    END

+

+    IDR_APPLET_PAGE3, DIALOG

+    BEGIN

+        LEFTMARGIN, 7

+        RIGHTMARGIN, 255

+        TOPMARGIN, 7

+        BOTTOMMARGIN, 133

+    END

+

+	IDR_POWER_MANAGEMENT_WARNING, DIALOG

+	BEGIN

+		LEFTMARGIN, 7

+		RIGHTMARGIN, 223

+		TOPMARGIN, 7,

+		BOTTOMMARGIN, 88

+	END

+

+    IDR_ADD_BROWSE_DOMAIN, DIALOG

+    BEGIN

+        LEFTMARGIN, 7

+        RIGHTMARGIN, 223

+        TOPMARGIN, 7

+        BOTTOMMARGIN, 88

+    END

+END

+#endif    // APSTUDIO_INVOKED

+

+

+/////////////////////////////////////////////////////////////////////////////

+//

+// String Table

+//

+

+STRINGTABLE 

+BEGIN

+    IDR_APPLET              "Bonjour"

+	IDS_APPLET_NAME			"Bonjour"

+    IDS_APPLET_DESCRIPTION  "Bonjour"

+	IDS_APPLET_TOOLTIP		"Change Bonjour settings for this computer."

+END

+

+#endif    // English (U.S.) resources

+/////////////////////////////////////////////////////////////////////////////

+

+

+

+#ifndef APSTUDIO_INVOKED

+/////////////////////////////////////////////////////////////////////////////

+//

+// Generated from the TEXTINCLUDE 3 resource.

+//

+#define _AFX_NO_SPLITTER_RESOURCES

+#define _AFX_NO_OLE_RESOURCES

+#define _AFX_NO_TRACKER_RESOURCES

+#define _AFX_NO_PROPERTY_RESOURCES

+

+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)

+LANGUAGE 9, 1

+#pragma code_page(1252)

+#include "afxres.rc"     // Standard components

+#endif

+

+/////////////////////////////////////////////////////////////////////////////

+#endif    // not APSTUDIO_INVOKED

+

diff --git a/mdnsresponder/mDNSWindows/ControlPanel/ControlPanelLocRes.vcproj b/mdnsresponder/mDNSWindows/ControlPanel/ControlPanelLocRes.vcproj
new file mode 100755
index 0000000..cdcee9b
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/ControlPanel/ControlPanelLocRes.vcproj
@@ -0,0 +1,487 @@
+<?xml version="1.0" encoding="Windows-1252"?>

+<VisualStudioProject

+	ProjectType="Visual C++"

+	Version="8.00"

+	Name="ControlPanelLocRes"

+	ProjectGUID="{4490229E-025A-478F-A2CF-51154DA83E39}"

+	RootNamespace="ControlPanelLocRes"

+	>

+	<Platforms>

+		<Platform

+			Name="Win32"

+		/>

+		<Platform

+			Name="x64"

+		/>

+	</Platforms>

+	<ToolFiles>

+	</ToolFiles>

+	<Configurations>

+		<Configuration

+			Name="Debug|Win32"

+			OutputDirectory="$(PlatformName)\$(ConfigurationName)"

+			IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"

+			ConfigurationType="2"

+			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"

+			UseOfMFC="1"

+			ATLMinimizesCRunTimeLibraryUsage="false"

+			CharacterSet="1"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+				PreprocessorDefinitions="_DEBUG"

+				MkTypLibCompatible="true"

+				SuppressStartupBanner="true"

+				TargetEnvironment="1"

+				TypeLibraryName="$(OutDir)/$(ProjectName).tlb"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				Optimization="0"

+				AdditionalIncludeDirectories=".."

+				PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;DEBUG=1;ENABLE_DOT_LOCAL_NAMES;WINVER=0x0400"

+				StringPooling="true"

+				BasicRuntimeChecks="3"

+				RuntimeLibrary="1"

+				ForceConformanceInForLoopScope="true"

+				UsePrecompiledHeader="0"

+				PrecompiledHeaderFile=""

+				AssemblerListingLocation=".\Debug/"

+				ObjectFile=".\Debug/"

+				ProgramDataBaseFileName=".\Debug/"

+				BrowseInformation="1"

+				WarningLevel="4"

+				WarnAsError="false"

+				SuppressStartupBanner="true"

+				Detect64BitPortabilityProblems="true"

+				DebugInformationFormat="3"

+				CallingConvention="2"

+				CompileAs="0"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+				PreprocessorDefinitions="_DEBUG"

+				Culture="1033"

+				AdditionalIncludeDirectories=".."

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+				Description="Building Output Directories"

+				CommandLine="if not exist $(OutDir)\ControlPanel.Resources mkdir $(OutDir)\ControlPanel.Resources&#x0D;&#x0A;if not exist $(OutDir)\ControlPanel.Resources\en.lproj mkdir $(OutDir)\ControlPanel.Resources\en.lproj&#x0D;&#x0A;"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				AdditionalOptions="/MACHINE:I386 /IGNORE:4089 "

+				AdditionalDependencies=""

+				OutputFile="$(OutDir)\ControlPanel.Resources\en.lproj\ControlPanelLocalized.dll"

+				LinkIncremental="1"

+				SuppressStartupBanner="true"

+				IgnoreDefaultLibraryNames=""

+				ModuleDefinitionFile=""

+				GenerateDebugInformation="true"

+				ProgramDatabaseFile="$(OutDir)/$(ProjectName).pdb"

+				SubSystem="2"

+				ResourceOnlyDLL="true"

+				ImportLibrary="$(OutDir)/$(ProjectName).lib"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+			/>

+		</Configuration>

+		<Configuration

+			Name="Debug|x64"

+			OutputDirectory="$(PlatformName)\$(ConfigurationName)"

+			IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"

+			ConfigurationType="2"

+			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"

+			UseOfMFC="1"

+			ATLMinimizesCRunTimeLibraryUsage="false"

+			CharacterSet="1"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+				PreprocessorDefinitions="_DEBUG"

+				MkTypLibCompatible="true"

+				SuppressStartupBanner="true"

+				TargetEnvironment="3"

+				TypeLibraryName="$(OutDir)/$(ProjectName).tlb"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				Optimization="0"

+				AdditionalIncludeDirectories=".."

+				PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;DEBUG=1;ENABLE_DOT_LOCAL_NAMES;WINVER=0x0400"

+				StringPooling="true"

+				BasicRuntimeChecks="3"

+				RuntimeLibrary="1"

+				ForceConformanceInForLoopScope="true"

+				UsePrecompiledHeader="0"

+				PrecompiledHeaderFile=""

+				AssemblerListingLocation=".\Debug/"

+				ObjectFile=".\Debug/"

+				ProgramDataBaseFileName=".\Debug/"

+				BrowseInformation="1"

+				WarningLevel="4"

+				WarnAsError="false"

+				SuppressStartupBanner="true"

+				Detect64BitPortabilityProblems="true"

+				DebugInformationFormat="3"

+				CallingConvention="2"

+				CompileAs="0"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+				PreprocessorDefinitions="_DEBUG"

+				Culture="1033"

+				AdditionalIncludeDirectories=".."

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+				Description="Building Output Directories"

+				CommandLine="if not exist $(OutDir)\ControlPanel.Resources mkdir $(OutDir)\ControlPanel.Resources&#x0D;&#x0A;if not exist $(OutDir)\ControlPanel.Resources\en.lproj mkdir $(OutDir)\ControlPanel.Resources\en.lproj&#x0D;&#x0A;"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				AdditionalOptions="/MACHINE:I386 /IGNORE:4089 "

+				AdditionalDependencies=""

+				OutputFile="$(OutDir)\ControlPanel.Resources\en.lproj\ControlPanelLocalized.dll"

+				LinkIncremental="1"

+				SuppressStartupBanner="true"

+				IgnoreDefaultLibraryNames=""

+				ModuleDefinitionFile=""

+				GenerateDebugInformation="true"

+				ProgramDatabaseFile="$(OutDir)/$(ProjectName).pdb"

+				SubSystem="2"

+				ResourceOnlyDLL="true"

+				ImportLibrary="$(OutDir)/$(ProjectName).lib"

+				TargetMachine="17"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+			/>

+		</Configuration>

+		<Configuration

+			Name="Release|Win32"

+			OutputDirectory="$(PlatformName)\$(ConfigurationName)"

+			IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"

+			ConfigurationType="2"

+			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"

+			UseOfMFC="1"

+			ATLMinimizesCRunTimeLibraryUsage="false"

+			CharacterSet="1"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+				PreprocessorDefinitions="NDEBUG"

+				MkTypLibCompatible="true"

+				SuppressStartupBanner="true"

+				TargetEnvironment="1"

+				TypeLibraryName="$(OutDir)/$(ProjectName).tlb"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				Optimization="2"

+				InlineFunctionExpansion="2"

+				FavorSizeOrSpeed="2"

+				OmitFramePointers="true"

+				AdditionalIncludeDirectories=".."

+				PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;WINVER=0x0400"

+				StringPooling="true"

+				RuntimeLibrary="0"

+				BufferSecurityCheck="false"

+				EnableFunctionLevelLinking="false"

+				ForceConformanceInForLoopScope="true"

+				UsePrecompiledHeader="0"

+				PrecompiledHeaderFile=""

+				AssemblerListingLocation=".\Release/"

+				ObjectFile=".\Release/"

+				ProgramDataBaseFileName=".\Release/"

+				BrowseInformation="1"

+				WarningLevel="4"

+				WarnAsError="false"

+				SuppressStartupBanner="true"

+				Detect64BitPortabilityProblems="true"

+				CompileAs="0"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+				PreprocessorDefinitions="NDEBUG"

+				Culture="1033"

+				AdditionalIncludeDirectories=".."

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+				Description="Building Output Directories"

+				CommandLine="if not exist $(OutDir)\ControlPanel.Resources mkdir $(OutDir)\ControlPanel.Resources&#x0D;&#x0A;if not exist $(OutDir)\ControlPanel.Resources\en.lproj mkdir $(OutDir)\ControlPanel.Resources\en.lproj&#x0D;&#x0A;"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				AdditionalOptions="/MACHINE:I386 /IGNORE:4089 "

+				AdditionalDependencies=""

+				OutputFile="$(OutDir)\ControlPanel.Resources\en.lproj\ControlPanelLocalized.dll"

+				LinkIncremental="1"

+				SuppressStartupBanner="true"

+				IgnoreDefaultLibraryNames=""

+				ModuleDefinitionFile=""

+				ProgramDatabaseFile=""

+				SubSystem="2"

+				OptimizeReferences="0"

+				EnableCOMDATFolding="0"

+				ResourceOnlyDLL="true"

+				ImportLibrary="$(IntDir)/$(ProjectName).lib"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+				CommandLine="if not &quot;%RC_XBS%&quot; == &quot;YES&quot; goto END&#x0D;&#x0A;if not exist &quot;$(DSTROOT)\Program Files\Bonjour SDK\bin\$(PlatformName)\ControlPanel.Resources\en.lproj&quot;   mkdir &quot;$(DSTROOT)\Program Files\Bonjour SDK\bin\$(PlatformName)\ControlPanel.Resources\en.lproj&quot;&#x0D;&#x0A;xcopy /I/Y &quot;$(TargetPath)&quot;                                                                                                                          &quot;$(DSTROOT)\Program Files\Bonjour SDK\bin\$(PlatformName)\ControlPanel.Resources\en.lproj&quot;&#x0D;&#x0A;:END&#x0D;&#x0A;"

+			/>

+		</Configuration>

+		<Configuration

+			Name="Release|x64"

+			OutputDirectory="$(PlatformName)\$(ConfigurationName)"

+			IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"

+			ConfigurationType="2"

+			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"

+			UseOfMFC="1"

+			ATLMinimizesCRunTimeLibraryUsage="false"

+			CharacterSet="1"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+				PreprocessorDefinitions="NDEBUG"

+				MkTypLibCompatible="true"

+				SuppressStartupBanner="true"

+				TargetEnvironment="3"

+				TypeLibraryName="$(OutDir)/$(ProjectName).tlb"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				Optimization="2"

+				InlineFunctionExpansion="2"

+				FavorSizeOrSpeed="2"

+				OmitFramePointers="true"

+				AdditionalIncludeDirectories=".."

+				PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;WINVER=0x0400"

+				StringPooling="true"

+				RuntimeLibrary="0"

+				BufferSecurityCheck="false"

+				EnableFunctionLevelLinking="false"

+				ForceConformanceInForLoopScope="true"

+				UsePrecompiledHeader="0"

+				PrecompiledHeaderFile=""

+				AssemblerListingLocation=".\Release/"

+				ObjectFile=".\Release/"

+				ProgramDataBaseFileName=".\Release/"

+				BrowseInformation="1"

+				WarningLevel="4"

+				WarnAsError="false"

+				SuppressStartupBanner="true"

+				Detect64BitPortabilityProblems="true"

+				CompileAs="0"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+				PreprocessorDefinitions="NDEBUG"

+				Culture="1033"

+				AdditionalIncludeDirectories=".."

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+				Description="Building Output Directories"

+				CommandLine="if not exist $(OutDir)\ControlPanel.Resources mkdir $(OutDir)\ControlPanel.Resources&#x0D;&#x0A;if not exist $(OutDir)\ControlPanel.Resources\en.lproj mkdir $(OutDir)\ControlPanel.Resources\en.lproj&#x0D;&#x0A;"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				AdditionalOptions="/MACHINE:I386 /IGNORE:4089 "

+				AdditionalDependencies=""

+				OutputFile="$(OutDir)\ControlPanel.Resources\en.lproj\ControlPanelLocalized.dll"

+				LinkIncremental="1"

+				SuppressStartupBanner="true"

+				IgnoreDefaultLibraryNames=""

+				ModuleDefinitionFile=""

+				ProgramDatabaseFile=""

+				SubSystem="2"

+				OptimizeReferences="0"

+				EnableCOMDATFolding="0"

+				ResourceOnlyDLL="true"

+				ImportLibrary="$(IntDir)/$(ProjectName).lib"

+				TargetMachine="17"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+				CommandLine="if not &quot;%RC_XBS%&quot; == &quot;YES&quot; goto END&#x0D;&#x0A;if not exist &quot;$(DSTROOT)\Program Files\Bonjour SDK\bin\$(PlatformName)\ControlPanel.Resources\en.lproj&quot;   mkdir &quot;$(DSTROOT)\Program Files\Bonjour SDK\bin\$(PlatformName)\ControlPanel.Resources\en.lproj&quot;&#x0D;&#x0A;xcopy /I/Y &quot;$(TargetPath)&quot;                                                                                                                          &quot;$(DSTROOT)\Program Files\Bonjour SDK\bin\$(PlatformName)\ControlPanel.Resources\en.lproj&quot;&#x0D;&#x0A;:END&#x0D;&#x0A;"

+			/>

+		</Configuration>

+	</Configurations>

+	<References>

+	</References>

+	<Files>

+		<Filter

+			Name="Header Files"

+			Filter="h;hpp;hxx;hm;inl;inc"

+			>

+			<File

+				RelativePath="resource_loc_res.h"

+				>

+			</File>

+		</Filter>

+		<Filter

+			Name="Resource Files"

+			Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;jpg;jpeg;jpe;manifest"

+			>

+			<File

+				RelativePath="ControlPanelLocRes.rc"

+				>

+			</File>

+		</Filter>

+	</Files>

+	<Globals>

+		<Global

+			Name="RESOURCE_FILE"

+			Value="ControlPanelLocRes.rc"

+		/>

+	</Globals>

+</VisualStudioProject>

diff --git a/mdnsresponder/mDNSWindows/ControlPanel/ControlPanelRes.rc b/mdnsresponder/mDNSWindows/ControlPanel/ControlPanelRes.rc
new file mode 100755
index 0000000..b74c59b
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/ControlPanel/ControlPanelRes.rc
@@ -0,0 +1,134 @@
+// Microsoft Visual C++ generated resource script.

+//

+#include "resource.h"

+

+#define APSTUDIO_READONLY_SYMBOLS

+/////////////////////////////////////////////////////////////////////////////

+//

+// Generated from the TEXTINCLUDE 2 resource.

+//

+#include "afxres.h"

+#include "WinVersRes.h"

+

+/////////////////////////////////////////////////////////////////////////////

+#undef APSTUDIO_READONLY_SYMBOLS

+

+/////////////////////////////////////////////////////////////////////////////

+// English (U.S.) resources

+

+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)

+#ifdef _WIN32

+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US

+#pragma code_page(1252)

+#endif //_WIN32

+

+#ifdef APSTUDIO_INVOKED

+/////////////////////////////////////////////////////////////////////////////

+//

+// TEXTINCLUDE

+//

+

+1 TEXTINCLUDE 

+BEGIN

+    "resource.h\0"

+END

+

+2 TEXTINCLUDE 

+BEGIN

+    "#include ""afxres.h""\r\n"

+    "#include ""WinVersRes.h""\r\n"

+    "\0"

+END

+

+3 TEXTINCLUDE 

+BEGIN

+    "#define _AFX_NO_SPLITTER_RESOURCES\r\n"

+    "#define _AFX_NO_OLE_RESOURCES\r\n"

+    "#define _AFX_NO_TRACKER_RESOURCES\r\n"

+    "#define _AFX_NO_PROPERTY_RESOURCES\r\n"

+    "\r\n"

+    "#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\r\n"

+    "LANGUAGE 9, 1\r\n"

+    "#pragma code_page(1252)\r\n"

+    "#include ""afxres.rc""     // Standard components\r\n"

+    "#endif\r\n"

+    "\0"

+END

+

+#endif    // APSTUDIO_INVOKED

+

+

+/////////////////////////////////////////////////////////////////////////////

+//

+// Version

+//

+

+VS_VERSION_INFO VERSIONINFO

+ FILEVERSION MASTER_PROD_VERS

+ PRODUCTVERSION MASTER_PROD_VERS

+ FILEFLAGSMASK 0x3fL

+#ifdef _DEBUG

+ FILEFLAGS 0x1L

+#else

+ FILEFLAGS 0x0L

+#endif

+ FILEOS 0x4L

+ FILETYPE 0x2L

+ FILESUBTYPE 0x0L

+BEGIN

+    BLOCK "StringFileInfo"

+    BEGIN

+        BLOCK "040904b0"

+        BEGIN

+            VALUE "CompanyName", MASTER_COMPANY_NAME

+            VALUE "FileDescription", "Bonjour Resource Module"

+            VALUE "FileVersion", MASTER_PROD_VERS_STR

+            VALUE "InternalName", "ControlPanelResources.dll"

+            VALUE "LegalCopyright", MASTER_LEGAL_COPYRIGHT

+            VALUE "OriginalFilename", "ControlPanelResources.dll"

+            VALUE "ProductName", MASTER_PROD_NAME

+            VALUE "ProductVersion", MASTER_PROD_VERS_STR

+        END

+    END

+    BLOCK "VarFileInfo"

+    BEGIN

+        VALUE "Translation", 0x409, 1200

+    END

+END

+

+

+/////////////////////////////////////////////////////////////////////////////

+//

+// Icon

+//

+

+// Icon with lowest ID value placed first to ensure application icon

+// remains consistent on all systems.

+IDR_APPLET              ICON                    "res\\controlpanel.ico"

+IDI_FAILURE             ICON                    "res\\failure.ico"

+IDI_SUCCESS             ICON                    "res\\success.ico"

+IDI_ENERGY_SAVER	ICON			"res\\EnergySaver.ico"

+#endif    // English (U.S.) resources

+/////////////////////////////////////////////////////////////////////////////

+

+

+

+#ifndef APSTUDIO_INVOKED

+/////////////////////////////////////////////////////////////////////////////

+//

+// Generated from the TEXTINCLUDE 3 resource.

+//

+#define _AFX_NO_SPLITTER_RESOURCES

+#define _AFX_NO_OLE_RESOURCES

+#define _AFX_NO_TRACKER_RESOURCES

+#define _AFX_NO_PROPERTY_RESOURCES

+

+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)

+LANGUAGE 9, 1

+#pragma code_page(1252)

+#include "afxres.rc"     // Standard components

+#endif

+

+/////////////////////////////////////////////////////////////////////////////

+#endif    // not APSTUDIO_INVOKED

+

diff --git a/mdnsresponder/mDNSWindows/ControlPanel/ControlPanelRes.vcproj b/mdnsresponder/mDNSWindows/ControlPanel/ControlPanelRes.vcproj
new file mode 100755
index 0000000..3e36161
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/ControlPanel/ControlPanelRes.vcproj
@@ -0,0 +1,518 @@
+<?xml version="1.0" encoding="Windows-1252"?>

+<VisualStudioProject

+	ProjectType="Visual C++"

+	Version="8.00"

+	Name="ControlPanelRes"

+	ProjectGUID="{5254AA9C-3D2E-4539-86D9-5EB0F4151215}"

+	>

+	<Platforms>

+		<Platform

+			Name="Win32"

+		/>

+		<Platform

+			Name="x64"

+		/>

+	</Platforms>

+	<ToolFiles>

+	</ToolFiles>

+	<Configurations>

+		<Configuration

+			Name="Debug|Win32"

+			OutputDirectory="$(PlatformName)\$(ConfigurationName)"

+			IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"

+			ConfigurationType="2"

+			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"

+			UseOfMFC="1"

+			ATLMinimizesCRunTimeLibraryUsage="false"

+			CharacterSet="1"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+				PreprocessorDefinitions="_DEBUG"

+				MkTypLibCompatible="true"

+				SuppressStartupBanner="true"

+				TargetEnvironment="1"

+				TypeLibraryName="$(OutDir)/$(ProjectName).tlb"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				Optimization="0"

+				AdditionalIncludeDirectories=".."

+				PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;DEBUG=1;ENABLE_DOT_LOCAL_NAMES;WINVER=0x0400"

+				StringPooling="true"

+				BasicRuntimeChecks="3"

+				RuntimeLibrary="1"

+				ForceConformanceInForLoopScope="true"

+				UsePrecompiledHeader="0"

+				PrecompiledHeaderFile=""

+				AssemblerListingLocation=".\Debug/"

+				ObjectFile=".\Debug/"

+				ProgramDataBaseFileName=".\Debug/"

+				BrowseInformation="1"

+				WarningLevel="4"

+				WarnAsError="false"

+				SuppressStartupBanner="true"

+				Detect64BitPortabilityProblems="true"

+				DebugInformationFormat="3"

+				CallingConvention="2"

+				CompileAs="0"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+				PreprocessorDefinitions="_DEBUG"

+				Culture="1033"

+				AdditionalIncludeDirectories=".."

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+				Description="Building Output Directories"

+				CommandLine="if not exist $(OutDir)\ControlPanel.Resources mkdir $(OutDir)\ControlPanel.Resources&#x0D;&#x0A;"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				AdditionalOptions="/MACHINE:I386 /IGNORE:4089 "

+				OutputFile="$(OutDir)\ControlPanel.Resources\ControlPanelResources.dll"

+				LinkIncremental="1"

+				SuppressStartupBanner="true"

+				IgnoreDefaultLibraryNames=""

+				ModuleDefinitionFile=""

+				GenerateDebugInformation="true"

+				ProgramDatabaseFile="$(OutDir)/$(ProjectName).pdb"

+				SubSystem="2"

+				ResourceOnlyDLL="true"

+				ImportLibrary="$(OutDir)/$(ProjectName).lib"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+			/>

+		</Configuration>

+		<Configuration

+			Name="Debug|x64"

+			OutputDirectory="$(PlatformName)\$(ConfigurationName)"

+			IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"

+			ConfigurationType="2"

+			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"

+			UseOfMFC="1"

+			ATLMinimizesCRunTimeLibraryUsage="false"

+			CharacterSet="1"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+				PreprocessorDefinitions="_DEBUG"

+				MkTypLibCompatible="true"

+				SuppressStartupBanner="true"

+				TargetEnvironment="3"

+				TypeLibraryName="$(OutDir)/$(ProjectName).tlb"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				Optimization="0"

+				AdditionalIncludeDirectories=".."

+				PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;DEBUG=1;ENABLE_DOT_LOCAL_NAMES;WINVER=0x0400"

+				StringPooling="true"

+				BasicRuntimeChecks="3"

+				RuntimeLibrary="1"

+				ForceConformanceInForLoopScope="true"

+				UsePrecompiledHeader="0"

+				PrecompiledHeaderFile=""

+				AssemblerListingLocation=".\Debug/"

+				ObjectFile=".\Debug/"

+				ProgramDataBaseFileName=".\Debug/"

+				BrowseInformation="1"

+				WarningLevel="4"

+				WarnAsError="false"

+				SuppressStartupBanner="true"

+				Detect64BitPortabilityProblems="true"

+				DebugInformationFormat="3"

+				CallingConvention="2"

+				CompileAs="0"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+				PreprocessorDefinitions="_DEBUG"

+				Culture="1033"

+				AdditionalIncludeDirectories=".."

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+				Description="Building Output Directories"

+				CommandLine="if not exist $(OutDir)\ControlPanel.Resources mkdir $(OutDir)\ControlPanel.Resources&#x0D;&#x0A;"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				AdditionalOptions="/MACHINE:I386 /IGNORE:4089 "

+				OutputFile="$(OutDir)\ControlPanel.Resources\ControlPanelResources.dll"

+				LinkIncremental="1"

+				SuppressStartupBanner="true"

+				IgnoreDefaultLibraryNames=""

+				ModuleDefinitionFile=""

+				GenerateDebugInformation="true"

+				ProgramDatabaseFile="$(OutDir)/$(ProjectName).pdb"

+				SubSystem="2"

+				ResourceOnlyDLL="true"

+				ImportLibrary="$(OutDir)/$(ProjectName).lib"

+				TargetMachine="17"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+			/>

+		</Configuration>

+		<Configuration

+			Name="Release|Win32"

+			OutputDirectory="$(PlatformName)\$(ConfigurationName)"

+			IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"

+			ConfigurationType="2"

+			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"

+			UseOfMFC="1"

+			ATLMinimizesCRunTimeLibraryUsage="false"

+			CharacterSet="1"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+				PreprocessorDefinitions="NDEBUG"

+				MkTypLibCompatible="true"

+				SuppressStartupBanner="true"

+				TargetEnvironment="1"

+				TypeLibraryName="$(OutDir)/$(ProjectName).tlb"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				Optimization="2"

+				InlineFunctionExpansion="2"

+				FavorSizeOrSpeed="2"

+				OmitFramePointers="true"

+				AdditionalIncludeDirectories="..\..\mDNSShared;.."

+				PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;WINVER=0x0400"

+				StringPooling="true"

+				RuntimeLibrary="0"

+				BufferSecurityCheck="false"

+				EnableFunctionLevelLinking="false"

+				ForceConformanceInForLoopScope="true"

+				UsePrecompiledHeader="0"

+				PrecompiledHeaderFile=""

+				AssemblerListingLocation=".\Release/"

+				ObjectFile=".\Release/"

+				ProgramDataBaseFileName=".\Release/"

+				BrowseInformation="1"

+				WarningLevel="4"

+				WarnAsError="false"

+				SuppressStartupBanner="true"

+				Detect64BitPortabilityProblems="true"

+				CompileAs="0"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+				PreprocessorDefinitions="NDEBUG"

+				Culture="1033"

+				AdditionalIncludeDirectories=".."

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+				Description="Building Output Directories"

+				CommandLine="if not exist $(OutDir)\ControlPanel.Resources mkdir $(OutDir)\ControlPanel.Resources&#x0D;&#x0A;"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				AdditionalOptions="/MACHINE:I386 /IGNORE:4089 "

+				OutputFile="$(OutDir)\ControlPanel.Resources\ControlPanelResources.dll"

+				LinkIncremental="1"

+				SuppressStartupBanner="true"

+				IgnoreDefaultLibraryNames=""

+				ModuleDefinitionFile=""

+				ProgramDatabaseFile=""

+				SubSystem="2"

+				OptimizeReferences="0"

+				EnableCOMDATFolding="0"

+				ResourceOnlyDLL="true"

+				ImportLibrary="$(IntDir)/$(ProjectName).lib"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+				CommandLine="if not &quot;%RC_XBS%&quot; == &quot;YES&quot; goto END&#x0D;&#x0A;if not exist &quot;$(DSTROOT)\Program Files\Bonjour SDK\bin\$(PlatformName)\ControlPanel.Resources&quot;   mkdir &quot;$(DSTROOT)\Program Files\Bonjour SDK\bin\$(PlatformName)\ControlPanel.Resources&quot;&#x0D;&#x0A;xcopy /I/Y &quot;$(TargetPath)&quot;                                                                                                            &quot;$(DSTROOT)\Program Files\Bonjour SDK\bin\$(PlatformName)\ControlPanel.Resources&quot;&#x0D;&#x0A;:END&#x0D;&#x0A;"

+			/>

+		</Configuration>

+		<Configuration

+			Name="Release|x64"

+			OutputDirectory="$(PlatformName)\$(ConfigurationName)"

+			IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"

+			ConfigurationType="2"

+			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"

+			UseOfMFC="1"

+			ATLMinimizesCRunTimeLibraryUsage="false"

+			CharacterSet="1"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+				PreprocessorDefinitions="NDEBUG"

+				MkTypLibCompatible="true"

+				SuppressStartupBanner="true"

+				TargetEnvironment="3"

+				TypeLibraryName="$(OutDir)/$(ProjectName).tlb"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				Optimization="2"

+				InlineFunctionExpansion="2"

+				FavorSizeOrSpeed="2"

+				OmitFramePointers="true"

+				AdditionalIncludeDirectories="..\..\mDNSShared;.."

+				PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;WINVER=0x0400"

+				StringPooling="true"

+				RuntimeLibrary="0"

+				BufferSecurityCheck="false"

+				EnableFunctionLevelLinking="false"

+				ForceConformanceInForLoopScope="true"

+				UsePrecompiledHeader="0"

+				PrecompiledHeaderFile=""

+				AssemblerListingLocation=".\Release/"

+				ObjectFile=".\Release/"

+				ProgramDataBaseFileName=".\Release/"

+				BrowseInformation="1"

+				WarningLevel="4"

+				WarnAsError="false"

+				SuppressStartupBanner="true"

+				Detect64BitPortabilityProblems="true"

+				CompileAs="0"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+				PreprocessorDefinitions="NDEBUG"

+				Culture="1033"

+				AdditionalIncludeDirectories=".."

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+				Description="Building Output Directories"

+				CommandLine="if not exist $(OutDir)\ControlPanel.Resources mkdir $(OutDir)\ControlPanel.Resources&#x0D;&#x0A;"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				AdditionalOptions="/MACHINE:I386 /IGNORE:4089 "

+				OutputFile="$(OutDir)\ControlPanel.Resources\ControlPanelResources.dll"

+				LinkIncremental="1"

+				SuppressStartupBanner="true"

+				IgnoreDefaultLibraryNames=""

+				ModuleDefinitionFile=""

+				ProgramDatabaseFile=""

+				SubSystem="2"

+				OptimizeReferences="0"

+				EnableCOMDATFolding="0"

+				ResourceOnlyDLL="true"

+				ImportLibrary="$(IntDir)/$(ProjectName).lib"

+				TargetMachine="17"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+				CommandLine="if not &quot;%RC_XBS%&quot; == &quot;YES&quot; goto END&#x0D;&#x0A;if not exist &quot;$(DSTROOT)\Program Files\Bonjour SDK\bin\$(PlatformName)\ControlPanel.Resources&quot;   mkdir &quot;$(DSTROOT)\Program Files\Bonjour SDK\bin\$(PlatformName)\ControlPanel.Resources&quot;&#x0D;&#x0A;xcopy /I/Y &quot;$(TargetPath)&quot;                                                                                                            &quot;$(DSTROOT)\Program Files\Bonjour SDK\bin\$(PlatformName)\ControlPanel.Resources&quot;&#x0D;&#x0A;:END&#x0D;&#x0A;"

+			/>

+		</Configuration>

+	</Configurations>

+	<References>

+	</References>

+	<Files>

+		<Filter

+			Name="Header Files"

+			Filter="h;hpp;hxx;hm;inl;inc"

+			>

+			<File

+				RelativePath="resource_res.h"

+				>

+			</File>

+		</Filter>

+		<Filter

+			Name="Resource Files"

+			Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;jpg;jpeg;jpe;manifest"

+			>

+			<File

+				RelativePath="res\about.bmp"

+				>

+			</File>

+			<File

+				RelativePath=".\about.bmp"

+				>

+			</File>

+			<File

+				RelativePath="res\button-2k.ico"

+				>

+			</File>

+			<File

+				RelativePath="res\button-xp.ico"

+				>

+			</File>

+			<File

+				RelativePath=".\res\cold.ico"

+				>

+			</File>

+			<File

+				RelativePath="ControlPanelRes.rc"

+				>

+			</File>

+			<File

+				RelativePath=".\hot.ico"

+				>

+			</File>

+			<File

+				RelativePath="res\logo.bmp"

+				>

+			</File>

+			<File

+				RelativePath=".\logo.bmp"

+				>

+			</File>

+			<File

+				RelativePath="Web.ico"

+				>

+			</File>

+		</Filter>

+	</Files>

+	<Globals>

+		<Global

+			Name="RESOURCE_FILE"

+			Value="ControlPanelRes.rc"

+		/>

+	</Globals>

+</VisualStudioProject>

diff --git a/mdnsresponder/mDNSWindows/ControlPanel/FourthPage.cpp b/mdnsresponder/mDNSWindows/ControlPanel/FourthPage.cpp
new file mode 100755
index 0000000..f748efd
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/ControlPanel/FourthPage.cpp
@@ -0,0 +1,197 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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 "FourthPage.h"
+#include "resource.h"
+
+#include "ConfigPropertySheet.h"
+#include "SharedSecret.h"
+
+#include <WinServices.h>
+    
+#define MAX_KEY_LENGTH 255
+
+
+IMPLEMENT_DYNCREATE(CFourthPage, CPropertyPage)
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CFourthPage::CFourthPage
+//---------------------------------------------------------------------------------------------------------------------------
+
+CFourthPage::CFourthPage()
+:
+	CPropertyPage(CFourthPage::IDD)
+{
+	//{{AFX_DATA_INIT(CFourthPage)
+	//}}AFX_DATA_INIT
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CFourthPage::~CFourthPage
+//---------------------------------------------------------------------------------------------------------------------------
+
+CFourthPage::~CFourthPage()
+{
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CFourthPage::DoDataExchange
+//---------------------------------------------------------------------------------------------------------------------------
+
+void CFourthPage::DoDataExchange(CDataExchange* pDX)
+{
+	CPropertyPage::DoDataExchange(pDX);
+	//{{AFX_DATA_MAP(CFourthPage)
+	//}}AFX_DATA_MAP
+	DDX_Control(pDX, IDC_POWER_MANAGEMENT, m_checkBox);
+}
+
+BEGIN_MESSAGE_MAP(CFourthPage, CPropertyPage)
+	//{{AFX_MSG_MAP(CFourthPage)
+	//}}AFX_MSG_MAP
+
+	ON_BN_CLICKED(IDC_POWER_MANAGEMENT, &CFourthPage::OnBnClickedPowerManagement)
+
+END_MESSAGE_MAP()
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CFourthPage::SetModified
+//---------------------------------------------------------------------------------------------------------------------------
+
+void CFourthPage::SetModified( BOOL bChanged )
+{
+	m_modified = bChanged;
+
+	CPropertyPage::SetModified( bChanged );
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CFourthPage::OnSetActive
+//---------------------------------------------------------------------------------------------------------------------------
+
+BOOL
+CFourthPage::OnSetActive()
+{
+	CConfigPropertySheet	*	psheet;
+	HKEY						key = NULL;
+	DWORD						dwSize;
+	DWORD						enabled;
+	DWORD						err;
+	BOOL						b = CPropertyPage::OnSetActive();
+
+	psheet = reinterpret_cast<CConfigPropertySheet*>(GetParent());
+	require_quiet( psheet, exit );
+
+	m_checkBox.SetCheck( 0 );
+
+	// Now populate the browse domain box
+
+	err = RegCreateKeyEx( HKEY_LOCAL_MACHINE, kServiceParametersNode L"\\Power Management", 0,
+		                  NULL, REG_OPTION_NON_VOLATILE, KEY_READ|KEY_WRITE|KEY_WOW64_32KEY, NULL, &key, NULL );
+	require_noerr( err, exit );
+
+	dwSize = sizeof( DWORD );
+	err = RegQueryValueEx( key, L"Enabled", NULL, NULL, (LPBYTE) &enabled, &dwSize );
+	require_noerr( err, exit );
+
+	m_checkBox.SetCheck( enabled );
+ 
+exit:
+
+	if ( key )
+	{
+		RegCloseKey( key );
+	}
+
+	return b;
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CFourthPage::OnOK
+//---------------------------------------------------------------------------------------------------------------------------
+
+void
+CFourthPage::OnOK()
+{
+	if ( m_modified )
+	{
+		Commit();
+	}
+}
+
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CFourthPage::Commit
+//---------------------------------------------------------------------------------------------------------------------------
+
+void
+CFourthPage::Commit()
+{
+	HKEY		key		= NULL;
+	DWORD		enabled;
+	DWORD		err;
+
+	err = RegCreateKeyEx( HKEY_LOCAL_MACHINE, kServiceParametersNode L"\\Power Management", 0,
+		                  NULL, REG_OPTION_NON_VOLATILE, KEY_READ|KEY_WRITE|KEY_WOW64_32KEY, NULL, &key, NULL );
+	require_noerr( err, exit );
+
+	enabled = m_checkBox.GetCheck();
+	err = RegSetValueEx( key, L"Enabled", NULL, REG_DWORD, (LPBYTE) &enabled, sizeof( enabled ) );
+	require_noerr( err, exit );
+	
+exit:
+
+	if ( key )
+	{
+		RegCloseKey( key );
+	}
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CFourthPage::OnBnClickedRemoveBrowseDomain
+//---------------------------------------------------------------------------------------------------------------------------
+
+
+
+void CFourthPage::OnBnClickedPowerManagement()
+
+{
+
+	char buf[ 256 ];
+
+
+
+	sprintf( buf, "check box: %d", m_checkBox.GetCheck() );
+
+	OutputDebugStringA( buf );
+
+	// TODO: Add your control notification handler code here
+
+
+
+	SetModified( TRUE );
+
+}
+
diff --git a/mdnsresponder/mDNSWindows/ControlPanel/FourthPage.h b/mdnsresponder/mDNSWindows/ControlPanel/FourthPage.h
new file mode 100755
index 0000000..d595291
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/ControlPanel/FourthPage.h
@@ -0,0 +1,87 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+#pragma once
+
+#include "stdafx.h"
+#include "resource.h"
+
+#include <DebugServices.h>
+#include <list>
+#include "afxcmn.h"
+
+#include "afxwin.h"
+
+
+
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CFourthPage
+//---------------------------------------------------------------------------------------------------------------------------
+
+class CFourthPage : public CPropertyPage
+{
+public:
+	CFourthPage();
+	~CFourthPage();
+
+protected:
+
+	//{{AFX_DATA(CFourthPage)
+	enum { IDD = IDR_APPLET_PAGE4 };
+	//}}AFX_DATA
+
+	//{{AFX_VIRTUAL(CFourthPage)
+	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
+	//}}AFX_VIRTUAL
+
+	DECLARE_DYNCREATE(CFourthPage)
+
+	//{{AFX_MSG(CFourthPage)
+	//}}AFX_MSG
+	DECLARE_MESSAGE_MAP()
+	
+private:
+	
+	typedef std::list<CString> StringList;
+
+	afx_msg BOOL
+	OnSetActive();
+	
+	afx_msg void
+	OnOK();
+	
+	void
+	SetModified( BOOL bChanged = TRUE );
+	
+	void
+	Commit();
+
+	BOOL			m_modified;
+
+public:
+private:
+
+	CButton m_checkBox;
+
+public:
+
+
+	afx_msg void OnBnClickedPowerManagement();
+
+};
diff --git a/mdnsresponder/mDNSWindows/ControlPanel/RegistrationPage.cpp b/mdnsresponder/mDNSWindows/ControlPanel/RegistrationPage.cpp
new file mode 100755
index 0000000..9328a75
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/ControlPanel/RegistrationPage.cpp
@@ -0,0 +1,387 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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 <Secret.h>
+#include "RegistrationPage.h"
+#include "resource.h"
+
+#include "ConfigPropertySheet.h"
+extern "C"
+{
+#include <ClientCommon.h>
+}
+#include <WinServices.h>
+
+#define MAX_KEY_LENGTH 255
+
+
+IMPLEMENT_DYNCREATE(CRegistrationPage, CPropertyPage)
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CRegistrationPage::CRegistrationPage
+//---------------------------------------------------------------------------------------------------------------------------
+
+CRegistrationPage::CRegistrationPage()
+:
+	CPropertyPage(CRegistrationPage::IDD),
+	m_ignoreChanges( false ),
+	m_hostnameSetupKey( NULL ),
+	m_registrationSetupKey( NULL ),
+	m_statusKey( NULL )
+{
+	//{{AFX_DATA_INIT(CRegistrationPage)
+	//}}AFX_DATA_INIT
+
+	OSStatus err;
+
+	err = RegCreateKeyEx( HKEY_LOCAL_MACHINE, kServiceParametersNode L"\\DynDNS\\Setup\\Hostnames", 0,
+	                      NULL, REG_OPTION_NON_VOLATILE, KEY_READ|KEY_WRITE|KEY_WOW64_32KEY, NULL, &m_hostnameSetupKey, NULL );
+	check_noerr( err );
+
+	err = RegCreateKeyEx( HKEY_LOCAL_MACHINE, kServiceParametersNode L"\\DynDNS\\Setup\\" kServiceDynDNSRegistrationDomains, 0,
+	                      NULL, REG_OPTION_NON_VOLATILE, KEY_READ|KEY_WRITE|KEY_WOW64_32KEY, NULL, &m_registrationSetupKey, NULL );
+	check_noerr( err );
+
+	err = RegCreateKeyEx( HKEY_LOCAL_MACHINE, kServiceParametersNode L"\\DynDNS\\State\\Hostnames", 0,
+	                      NULL, REG_OPTION_NON_VOLATILE, KEY_READ|KEY_WRITE|KEY_WOW64_32KEY, NULL, &m_statusKey, NULL );
+	check_noerr( err );
+
+	
+}
+
+CRegistrationPage::~CRegistrationPage()
+{
+	if ( m_hostnameSetupKey )
+	{
+		RegCloseKey( m_hostnameSetupKey );
+		m_hostnameSetupKey = NULL;
+	}
+
+	if ( m_registrationSetupKey )
+	{
+		RegCloseKey( m_registrationSetupKey );
+		m_registrationSetupKey = NULL;
+	}
+
+	if ( m_statusKey )
+	{
+		RegCloseKey( m_statusKey );
+		m_statusKey = NULL;
+	}
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CRegistrationPage::DoDataExchange
+//---------------------------------------------------------------------------------------------------------------------------
+
+void CRegistrationPage::DoDataExchange(CDataExchange* pDX)
+{
+	CPropertyPage::DoDataExchange(pDX);
+	//{{AFX_DATA_MAP(CRegistrationPage)
+	//}}AFX_DATA_MAP
+	DDX_Control(pDX, IDC_HOSTNAME, m_hostnameControl);
+	DDX_Control(pDX, IDC_USERNAME, m_usernameControl);
+	DDX_Control(pDX, IDC_PASSWORD, m_passwordControl);
+	DDX_Control(pDX, IDC_ADVERTISE_SERVICES, m_advertiseServices);
+}
+
+BEGIN_MESSAGE_MAP(CRegistrationPage, CPropertyPage)
+	//{{AFX_MSG_MAP(CRegistrationPage)
+	//}}AFX_MSG_MAP
+	ON_EN_CHANGE(IDC_HOSTNAME, OnEnChangeHostname)
+	ON_EN_CHANGE(IDC_USERNAME, OnEnChangeUsername)
+	ON_EN_CHANGE(IDC_PASSWORD, OnEnChangePassword)
+	ON_BN_CLICKED(IDC_ADVERTISE_SERVICES, OnBnClickedAdvertiseServices)
+END_MESSAGE_MAP()
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CRegistrationPage::OnEnChangedHostname
+//---------------------------------------------------------------------------------------------------------------------------
+
+void CRegistrationPage::OnEnChangeHostname()
+{
+	if ( !m_ignoreChanges )
+	{
+		SetModified( TRUE );
+	}
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CRegistrationPage::OnEnChangedUsername
+//---------------------------------------------------------------------------------------------------------------------------
+
+void CRegistrationPage::OnEnChangeUsername()
+{
+	if ( !m_ignoreChanges )
+	{
+		SetModified( TRUE );
+	}
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CRegistrationPage::OnEnChangedPassword
+//---------------------------------------------------------------------------------------------------------------------------
+
+void CRegistrationPage::OnEnChangePassword()
+{
+	if ( !m_ignoreChanges )
+	{
+		SetModified( TRUE );
+	}
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CRegistrationPage::OnBnClickedAdvertiseServices
+//---------------------------------------------------------------------------------------------------------------------------
+
+void CRegistrationPage::OnBnClickedAdvertiseServices()
+{
+	if ( !m_ignoreChanges )
+	{
+		SetModified( TRUE );
+	}
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CRegistrationPage::SetModified
+//---------------------------------------------------------------------------------------------------------------------------
+
+void CRegistrationPage::SetModified( BOOL bChanged )
+{
+	m_modified = bChanged ? true : false;
+
+	CPropertyPage::SetModified( bChanged );
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CRegistrationPage::OnSetActive
+//---------------------------------------------------------------------------------------------------------------------------
+
+BOOL
+CRegistrationPage::OnSetActive()
+{
+	TCHAR	name[kDNSServiceMaxDomainName + 1];
+	DWORD	nameLen = ( kDNSServiceMaxDomainName + 1 ) * sizeof( TCHAR );
+	DWORD	err;
+
+	BOOL b = CPropertyPage::OnSetActive();
+
+	m_ignoreChanges = true;
+	m_modified = FALSE;
+	
+	if ( m_hostnameSetupKey )
+	{
+		err = RegQueryValueEx( m_hostnameSetupKey, L"", NULL, NULL, (LPBYTE) name, &nameLen );
+
+		if ( !err )
+		{
+			char	hostnameUTF8[ 256 ];
+			char	outDomain[ 256 ];
+			char	outUsername[ 256 ];
+			char	outPassword[ 256 ];
+			CString hostname = name;
+			CString username;
+			CString password;
+
+			m_hostnameControl.SetWindowText( hostname );
+
+			StringObjectToUTF8String( hostname, hostnameUTF8, sizeof( hostnameUTF8 ) );
+
+			if ( LsaGetSecret( hostnameUTF8, outDomain, sizeof( outDomain ) / sizeof( TCHAR ), outUsername, sizeof( outUsername ) / sizeof( TCHAR ), outPassword, sizeof( outPassword ) / sizeof( TCHAR ) ) )
+			{
+				username = outUsername;
+				m_usernameControl.SetWindowText( username );
+
+				password = outPassword;
+				m_passwordControl.SetWindowText( password );
+			}
+		}
+	}
+
+	m_advertiseServices.SetCheck( 0 );
+
+	if ( m_registrationSetupKey )
+	{
+		HKEY		subKey = NULL;
+		DWORD		dwSize;
+		DWORD		enabled = 0;
+		TCHAR		subKeyName[MAX_KEY_LENGTH];
+		DWORD		cSubKeys = 0;
+		DWORD		cbMaxSubKey;
+		DWORD		cchMaxClass;
+		OSStatus	err;
+
+		err = RegQueryInfoKey( m_registrationSetupKey, NULL, NULL, NULL, &cSubKeys, &cbMaxSubKey, &cchMaxClass, NULL, NULL, NULL, NULL, NULL );       
+		if ( !err )
+		{
+			if ( cSubKeys > 0 )
+			{	
+				dwSize = MAX_KEY_LENGTH;
+	            
+				err = RegEnumKeyEx( m_registrationSetupKey, 0, subKeyName, &dwSize, NULL, NULL, NULL, NULL );
+				if ( !err )
+				{
+					err = RegOpenKey( m_registrationSetupKey, subKeyName, &subKey );
+					if ( !err )
+					{
+						dwSize = sizeof( DWORD );
+						err = RegQueryValueEx( subKey, L"Enabled", NULL, NULL, (LPBYTE) &enabled, &dwSize );
+						if ( !err && enabled )
+						{
+							m_advertiseServices.SetCheck( enabled );
+						}
+
+						RegCloseKey( subKey );
+						subKey = NULL;
+					}
+				}
+			}
+		}
+	}
+
+	m_ignoreChanges = false;
+
+	return b;
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CRegistrationPage::OnOK
+//---------------------------------------------------------------------------------------------------------------------------
+
+void
+CRegistrationPage::OnOK()
+{
+	if ( m_modified )
+	{
+		Commit();
+	}
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CRegistrationPage::Commit
+//---------------------------------------------------------------------------------------------------------------------------
+
+void
+CRegistrationPage::Commit()
+{
+	CString	hostname;
+	char	hostnameUTF8[ 256 ];
+	CString username;
+	char	usernameUTF8[ 256 ];
+	CString password;
+	char	passwordUTF8[ 256 ];
+	DWORD	enabled = 1;
+	BOOL	secret = FALSE;
+	DWORD	err;
+
+	m_hostnameControl.GetWindowText( hostname );
+	hostname.MakeLower();
+	hostname.TrimRight( '.' );
+	StringObjectToUTF8String( hostname, hostnameUTF8, sizeof( hostnameUTF8 ) );
+	
+	m_usernameControl.GetWindowText( username );
+	m_passwordControl.GetWindowText( password );
+	
+	if ( username.GetLength() && password.GetLength() )
+	{
+		StringObjectToUTF8String( username, usernameUTF8, sizeof( usernameUTF8 ) );	
+		StringObjectToUTF8String( password, passwordUTF8, sizeof( passwordUTF8 ) );
+		secret = TRUE;
+	}
+
+	if ( m_hostnameSetupKey != NULL )
+	{
+		err = RegSetValueEx( m_hostnameSetupKey, L"", 0, REG_SZ, (LPBYTE) (LPCTSTR) hostname, ( hostname.GetLength() + 1 ) * sizeof( TCHAR ) );
+		require_noerr( err, exit );
+		
+		err = RegSetValueEx( m_hostnameSetupKey, L"Enabled", 0, REG_DWORD, (LPBYTE) &enabled, sizeof( DWORD ) );
+		require_noerr( err, exit );
+
+		if ( secret )
+		{
+			LsaSetSecret( hostnameUTF8, usernameUTF8, passwordUTF8 );
+		}
+	}
+
+	if ( m_registrationSetupKey != NULL )
+	{
+		TCHAR		subKeyName[MAX_KEY_LENGTH];
+		DWORD		cSubKeys = 0;
+		DWORD		cbMaxSubKey;
+		DWORD		cchMaxClass;
+		DWORD		dwSize;
+		int			i;
+		OSStatus	err = kNoErr;
+
+		// First, remove all the entries that are there
+
+		err = RegQueryInfoKey( m_registrationSetupKey, NULL, NULL, NULL, &cSubKeys, &cbMaxSubKey, &cchMaxClass, NULL, NULL, NULL, NULL, NULL );       
+		if ( !err )
+		{
+			for ( i = 0; i < (int) cSubKeys; i++ )
+			{	
+				dwSize = MAX_KEY_LENGTH;
+		            
+				err = RegEnumKeyEx( m_registrationSetupKey, 0, subKeyName, &dwSize, NULL, NULL, NULL, NULL );
+				require_noerr( err, exit );
+					
+				err = RegDeleteKey( m_registrationSetupKey, subKeyName );
+				require_noerr( err, exit );
+			}
+		}
+
+		if ( m_advertiseServices.GetCheck() )
+		{
+			const char	* domainUTF8;
+			CString		  domain;
+			char		  label[ 64 ];
+			HKEY		  subKey = NULL;
+
+			domainUTF8	= GetNextLabel( hostnameUTF8, label );
+			domain		= domainUTF8;
+
+			err = RegCreateKeyEx( m_registrationSetupKey, domain, 0,
+									 NULL, REG_OPTION_NON_VOLATILE, KEY_READ|KEY_WRITE|KEY_WOW64_32KEY, NULL, &subKey, NULL );
+			if ( !err )
+			{
+				err = RegSetValueEx( subKey, L"Enabled", 0, REG_DWORD, (LPBYTE) &enabled, sizeof( DWORD ) );
+				check_noerr( err );
+
+				RegCloseKey( subKey );
+				subKey = NULL;
+			}
+
+			if ( secret )
+			{
+				LsaSetSecret( domainUTF8, usernameUTF8, passwordUTF8 );
+			}
+		}
+	}
+
+exit:
+
+	return;
+}
diff --git a/mdnsresponder/mDNSWindows/ControlPanel/RegistrationPage.h b/mdnsresponder/mDNSWindows/ControlPanel/RegistrationPage.h
new file mode 100755
index 0000000..935a418
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/ControlPanel/RegistrationPage.h
@@ -0,0 +1,75 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+#pragma once
+
+#include "stdafx.h"
+#include "resource.h"
+#include <DebugServices.h>
+#include "afxwin.h"
+
+    
+//---------------------------------------------------------------------------------------------------------------------------
+//	CRegistrationPage
+//---------------------------------------------------------------------------------------------------------------------------
+
+class CRegistrationPage : public CPropertyPage
+{
+public:
+	CRegistrationPage();
+	~CRegistrationPage();
+
+protected:
+	//{{AFX_DATA(CRegistrationPage)
+	enum { IDD = IDR_APPLET_PAGE1 };
+	//}}AFX_DATA
+
+	//{{AFX_VIRTUAL(CRegistrationPage)
+	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
+	//}}AFX_VIRTUAL
+
+	DECLARE_DYNCREATE(CRegistrationPage)
+
+	//{{AFX_MSG(CRegistrationPage)
+	//}}AFX_MSG
+	DECLARE_MESSAGE_MAP()
+
+private:
+
+	afx_msg BOOL	OnSetActive();
+	afx_msg void	OnOK();
+
+	void			SetModified( BOOL bChanged = TRUE );
+	void			Commit();
+
+	CEdit			m_hostnameControl;
+	CEdit			m_usernameControl;
+	CEdit			m_passwordControl;
+	CButton			m_advertiseServices;
+	bool			m_ignoreChanges;
+	bool			m_modified;
+	HKEY			m_hostnameSetupKey;
+	HKEY			m_registrationSetupKey;
+	HKEY			m_statusKey;
+	
+public:
+	
+	afx_msg void OnEnChangeHostname();
+	afx_msg void OnEnChangeUsername();
+	afx_msg void OnEnChangePassword();
+	afx_msg void OnBnClickedAdvertiseServices();
+};
diff --git a/mdnsresponder/mDNSWindows/ControlPanel/SecondPage.cpp b/mdnsresponder/mDNSWindows/ControlPanel/SecondPage.cpp
new file mode 100755
index 0000000..d3bc133
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/ControlPanel/SecondPage.cpp
@@ -0,0 +1,544 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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 "SecondPage.h"
+#include "resource.h"
+
+#include "ConfigPropertySheet.h"
+#include "SharedSecret.h"
+
+#include <WinServices.h>
+    
+#define MAX_KEY_LENGTH 255
+
+IMPLEMENT_DYNCREATE(CSecondPage, CPropertyPage)
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CSecondPage::CSecondPage
+//---------------------------------------------------------------------------------------------------------------------------
+
+CSecondPage::CSecondPage()
+:
+	CPropertyPage(CSecondPage::IDD),
+	m_setupKey( NULL )
+{
+	//{{AFX_DATA_INIT(CSecondPage)
+	//}}AFX_DATA_INIT
+
+	OSStatus err;
+
+	err = RegCreateKeyEx( HKEY_LOCAL_MACHINE, kServiceParametersNode L"\\DynDNS\\Setup\\" kServiceDynDNSRegistrationDomains, 0,
+	                      NULL, REG_OPTION_NON_VOLATILE, KEY_READ|KEY_WRITE|KEY_WOW64_32KEY, NULL, &m_setupKey, NULL );
+	check_noerr( err );
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CSecondPage::~CSecondPage
+//---------------------------------------------------------------------------------------------------------------------------
+
+CSecondPage::~CSecondPage()
+{
+	if ( m_setupKey )
+	{
+		RegCloseKey( m_setupKey );
+		m_setupKey = NULL;
+	}
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CSecondPage::DoDataExchange
+//---------------------------------------------------------------------------------------------------------------------------
+
+void CSecondPage::DoDataExchange(CDataExchange* pDX)
+{
+	CPropertyPage::DoDataExchange(pDX);
+	//{{AFX_DATA_MAP(CSecondPage)
+	//}}AFX_DATA_MAP
+	DDX_Control(pDX, IDC_CHECK1, m_advertiseServicesButton);
+	DDX_Control(pDX, IDC_BUTTON1, m_sharedSecretButton);
+	DDX_Control(pDX, IDC_COMBO2, m_regDomainsBox);
+}
+
+BEGIN_MESSAGE_MAP(CSecondPage, CPropertyPage)
+	//{{AFX_MSG_MAP(CSecondPage)
+	//}}AFX_MSG_MAP
+	ON_BN_CLICKED(IDC_BUTTON1, OnBnClickedSharedSecret)
+	ON_BN_CLICKED(IDC_CHECK1, OnBnClickedAdvertise)
+	ON_CBN_SELCHANGE(IDC_COMBO1, OnCbnSelChange)
+	ON_CBN_EDITCHANGE(IDC_COMBO1, OnCbnEditChange)
+	ON_CBN_EDITCHANGE(IDC_COMBO2, OnCbnEditChange)
+	ON_CBN_SELCHANGE(IDC_COMBO2, OnCbnSelChange)
+	
+END_MESSAGE_MAP()
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CSecondPage::SetModified
+//---------------------------------------------------------------------------------------------------------------------------
+
+void CSecondPage::SetModified( BOOL bChanged )
+{
+	m_modified = bChanged;
+
+	CPropertyPage::SetModified( bChanged );
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CSecondPage::OnSetActive
+//---------------------------------------------------------------------------------------------------------------------------
+
+BOOL
+CSecondPage::OnSetActive()
+{
+	CConfigPropertySheet	*	psheet;
+	DWORD						err;
+	BOOL						b = CPropertyPage::OnSetActive();
+
+	psheet = reinterpret_cast<CConfigPropertySheet*>(GetParent());
+	require_quiet( psheet, exit );
+	
+	m_modified = FALSE;
+
+	// Clear out what's there
+
+	EmptyComboBox( m_regDomainsBox );
+
+	// Now populate the registration domain box
+
+	err = Populate( m_regDomainsBox, m_setupKey, psheet->m_regDomains );
+	check_noerr( err );
+
+exit:
+
+	return b;
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CSecondPage::OnOK
+//---------------------------------------------------------------------------------------------------------------------------
+
+void
+CSecondPage::OnOK()
+{
+	if ( m_modified )
+	{
+		Commit();
+	}
+}
+
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CSecondPage::Commit
+//---------------------------------------------------------------------------------------------------------------------------
+
+void
+CSecondPage::Commit()
+{
+	DWORD err;
+
+	if ( m_setupKey != NULL )
+	{
+		err = Commit( m_regDomainsBox, m_setupKey, m_advertiseServicesButton.GetCheck() == BST_CHECKED );
+		check_noerr( err );
+	}
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CSecondPage::Commit
+//---------------------------------------------------------------------------------------------------------------------------
+
+OSStatus
+CSecondPage::Commit( CComboBox & box, HKEY key, DWORD enabled )
+{
+	CString		selected;
+	HKEY		subKey	= NULL;
+	TCHAR		subKeyName[MAX_KEY_LENGTH];
+	DWORD		cSubKeys = 0;
+	DWORD		cbMaxSubKey;
+	DWORD		cchMaxClass;
+	DWORD		dwSize;
+	int			i;
+	OSStatus	err = kNoErr;
+
+	// First, remove all the entries that are there
+
+    err = RegQueryInfoKey( key, NULL, NULL, NULL, &cSubKeys, &cbMaxSubKey, &cchMaxClass, NULL, NULL, NULL, NULL, NULL );       
+	require_noerr( err, exit );
+
+	for ( i = 0; i < (int) cSubKeys; i++ )
+	{	
+		dwSize = MAX_KEY_LENGTH;
+            
+		err = RegEnumKeyEx( key, 0, subKeyName, &dwSize, NULL, NULL, NULL, NULL );
+		require_noerr( err, exit );
+			
+		err = RegDeleteKey( key, subKeyName );
+		require_noerr( err, exit );
+	}
+
+	// Get selected text
+	
+	box.GetWindowText( selected );
+	
+	// If we haven't seen this string before, add the string to the box and
+	// the registry
+	
+	if ( ( selected.GetLength() > 0 ) && ( box.FindStringExact( -1, selected ) == CB_ERR ) )
+	{
+		CString string;
+
+		box.AddString( selected );
+
+		err = RegQueryString( key, L"UserDefined", string );
+		check_noerr( err );
+
+		if ( string.GetLength() )
+		{
+			string += L"," + selected;
+		}
+		else
+		{
+			string = selected;
+		}
+
+		err = RegSetValueEx( key, L"UserDefined", 0, REG_SZ, (LPBYTE) (LPCTSTR) string, ( string.GetLength() + 1) * sizeof( TCHAR ) );
+		check_noerr ( err );
+	}
+
+	// Save selected text in registry.  This will trigger mDNSResponder to setup
+	// DynDNS config again
+
+	err = RegCreateKeyEx( key, selected, 0,
+	                      NULL, REG_OPTION_NON_VOLATILE, KEY_READ|KEY_WRITE|KEY_WOW64_32KEY, NULL, &subKey, NULL );
+	require_noerr( err, exit );
+
+	err = RegSetValueEx( subKey, L"Enabled", 0, REG_DWORD, (LPBYTE) &enabled, sizeof( DWORD ) );
+	check_noerr( err );
+
+exit:
+
+	if ( subKey )
+	{
+		RegCloseKey( subKey );
+		subKey = NULL;
+	}
+
+	return err;
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CSecondPage::OnBnClickedSharedSecret
+//---------------------------------------------------------------------------------------------------------------------------
+
+void CSecondPage::OnBnClickedSharedSecret()
+{
+	CString name;
+
+	m_regDomainsBox.GetWindowText( name );
+
+	CSharedSecret dlg;
+
+	dlg.Load( name );
+
+	if ( dlg.DoModal() == IDOK )
+	{
+		DWORD		wakeup = 0;
+		DWORD		dwSize = sizeof( DWORD );
+		OSStatus	err;
+
+		dlg.Commit( name );
+
+		// We have now updated the secret, however the system service
+		// doesn't know about it yet.  So we're going to update the
+		// registry with a dummy value which will cause the system
+		// service to re-initialize it's DynDNS setup
+		//
+
+		RegQueryValueEx( m_setupKey, L"Wakeup", NULL, NULL, (LPBYTE) &wakeup, &dwSize );      
+
+		wakeup++;
+		
+		err = RegSetValueEx( m_setupKey, L"Wakeup", 0, REG_DWORD, (LPBYTE) &wakeup, sizeof( DWORD ) );
+		require_noerr( err, exit );
+	}
+
+exit:
+
+	return;
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CSecondPage::OnBnClickedAdvertise
+//---------------------------------------------------------------------------------------------------------------------------
+
+void CSecondPage::OnBnClickedAdvertise()
+{
+	int state;
+
+	state = m_advertiseServicesButton.GetCheck();
+
+	m_regDomainsBox.EnableWindow( state );
+	m_sharedSecretButton.EnableWindow( state );
+
+	SetModified( TRUE );
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CSecondPage::OnCbnSelChange
+//---------------------------------------------------------------------------------------------------------------------------
+
+void CSecondPage::OnCbnSelChange()
+{
+	SetModified( TRUE );
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CSecondPage::OnCbnEditChange
+//---------------------------------------------------------------------------------------------------------------------------
+
+void CSecondPage::OnCbnEditChange()
+{
+	SetModified( TRUE );
+}
+
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CSecondPage::OnAddRegistrationDomain
+//---------------------------------------------------------------------------------------------------------------------------
+
+void
+CSecondPage::OnAddRegistrationDomain( CString & domain )
+{
+	int index = m_regDomainsBox.FindStringExact( -1, domain );
+
+	if ( index == CB_ERR )
+	{
+		m_regDomainsBox.AddString( domain );
+	}
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CSecondPage::OnRemoveRegistrationDomain
+//---------------------------------------------------------------------------------------------------------------------------
+
+void
+CSecondPage::OnRemoveRegistrationDomain( CString & domain )
+{
+	int index = m_regDomainsBox.FindStringExact( -1, domain );
+
+	if ( index != CB_ERR )
+	{
+		m_regDomainsBox.DeleteString( index );
+	}
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CSecondPage::EmptyComboBox
+//---------------------------------------------------------------------------------------------------------------------------
+
+void
+CSecondPage::EmptyComboBox( CComboBox & box )
+{
+	while ( box.GetCount() > 0 )
+	{
+		box.DeleteString( 0 );
+	}
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CSecondPage::Populate
+//---------------------------------------------------------------------------------------------------------------------------
+
+OSStatus
+CSecondPage::Populate( CComboBox & box, HKEY key, StringList & l )
+{
+	CString		string;
+	HKEY		subKey = NULL;
+	DWORD		dwSize;
+	DWORD		enabled = 0;
+	TCHAR		subKeyName[MAX_KEY_LENGTH];
+	DWORD		cSubKeys = 0;
+	DWORD		cbMaxSubKey;
+	DWORD		cchMaxClass;
+	OSStatus	err;
+
+	err = RegQueryString( key, L"UserDefined", string );
+
+	if ( !err && string.GetLength() )
+	{
+		bool done = false;
+
+		while ( !done )
+		{
+			CString tok;
+
+			tok = string.SpanExcluding( L"," );
+
+			box.AddString( tok );
+
+			if ( tok != string )
+			{
+				// Get rid of that string and comma
+
+				string = string.Right( string.GetLength() - tok.GetLength() - 1 );
+			}
+			else
+			{
+				done = true;
+			}
+		}
+	}
+
+	StringList::iterator it;
+
+	for ( it = l.begin(); it != l.end(); it++ )
+	{
+		if ( box.FindStringExact( -1, *it ) == CB_ERR )
+		{
+			box.AddString( *it );
+		}
+	}
+
+	err = RegQueryInfoKey( key, NULL, NULL, NULL, &cSubKeys, &cbMaxSubKey, &cchMaxClass, NULL, NULL, NULL, NULL, NULL );       
+	require_noerr( err, exit );
+
+	if ( cSubKeys > 0 )
+	{	
+		dwSize = MAX_KEY_LENGTH;
+            
+		err = RegEnumKeyEx( key, 0, subKeyName, &dwSize, NULL, NULL, NULL, NULL );
+		require_noerr( err, exit );
+
+		err = RegOpenKey( key, subKeyName, &subKey );
+		require_noerr( err, exit );
+
+		dwSize = sizeof( DWORD );
+		err = RegQueryValueEx( subKey, L"Enabled", NULL, NULL, (LPBYTE) &enabled, &dwSize );
+		require_noerr( err, exit );
+
+		// See if it's there
+
+		if ( box.SelectString( -1, subKeyName ) == CB_ERR )
+		{
+			// If not, add it
+
+			box.AddString( subKeyName );
+		}
+
+		box.SelectString( -1, subKeyName );
+
+		RegCloseKey( subKey );
+		subKey = NULL;
+	}
+
+exit:
+
+	m_advertiseServicesButton.SetCheck( ( !err && enabled ) ? BST_CHECKED : BST_UNCHECKED );
+	m_regDomainsBox.EnableWindow( ( !err && enabled ) );
+	m_sharedSecretButton.EnableWindow( (!err && enabled ) );
+
+	return err;
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CSecondPage::CreateKey
+//---------------------------------------------------------------------------------------------------------------------------
+
+OSStatus
+CSecondPage::CreateKey( CString & name, DWORD enabled )
+{
+	HKEY		key = NULL;
+	OSStatus	err;
+
+	err = RegCreateKeyEx( HKEY_LOCAL_MACHINE, (LPCTSTR) name, 0,
+		                  NULL, REG_OPTION_NON_VOLATILE, KEY_READ|KEY_WRITE|KEY_WOW64_32KEY, NULL, &key, NULL );
+	require_noerr( err, exit );
+
+	err = RegSetValueEx( key, L"Enabled", 0, REG_DWORD, (LPBYTE) &enabled, sizeof( DWORD ) );
+	check_noerr( err );
+
+exit:
+
+	if ( key )
+	{
+		RegCloseKey( key );
+	}
+
+	return err;
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CSecondPage::RegQueryString
+//---------------------------------------------------------------------------------------------------------------------------
+
+OSStatus
+CSecondPage::RegQueryString( HKEY key, CString valueName, CString & value )
+{
+	TCHAR	*	string;
+	DWORD		stringLen;
+	int			i;
+	OSStatus	err;
+
+	stringLen	= 1024;
+	string		= NULL;
+	i			= 0;
+
+	do
+	{
+		if ( string )
+		{
+			free( string );
+		}
+
+		string = (TCHAR*) malloc( stringLen );
+		require_action( string, exit, err = kUnknownErr );
+		*string = '\0';
+
+		err = RegQueryValueEx( key, valueName, 0, NULL, (LPBYTE) string, &stringLen );
+
+		i++;
+	}
+	while ( ( err == ERROR_MORE_DATA ) && ( i < 100 ) );
+
+	value = string;
+
+exit:
+
+	if ( string )
+	{
+		free( string );
+	}
+
+	return err;
+}
diff --git a/mdnsresponder/mDNSWindows/ControlPanel/SecondPage.h b/mdnsresponder/mDNSWindows/ControlPanel/SecondPage.h
new file mode 100755
index 0000000..3bd9e74
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/ControlPanel/SecondPage.h
@@ -0,0 +1,107 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+#pragma once
+
+#include "stdafx.h"
+#include "resource.h"
+
+#include <DebugServices.h>
+#include <list>
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CSecondPage
+//---------------------------------------------------------------------------------------------------------------------------
+
+class CSecondPage : public CPropertyPage
+{
+public:
+	CSecondPage();
+	~CSecondPage();
+
+protected:
+
+	//{{AFX_DATA(CSecondPage)
+	enum { IDD = IDR_APPLET_PAGE2 };
+	//}}AFX_DATA
+
+	//{{AFX_VIRTUAL(CSecondPage)
+	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
+	//}}AFX_VIRTUAL
+
+	DECLARE_DYNCREATE(CSecondPage)
+
+	//{{AFX_MSG(CSecondPage)
+	//}}AFX_MSG
+	DECLARE_MESSAGE_MAP()
+public:
+	
+	afx_msg void	OnBnClickedSharedSecret();
+	afx_msg void	OnBnClickedAdvertise();
+
+	void			OnAddRegistrationDomain( CString & domain );
+	void			OnRemoveRegistrationDomain( CString & domain );
+	
+private:
+	
+	typedef std::list<CString> StringList;
+
+	afx_msg BOOL
+	OnSetActive();
+	
+	afx_msg void
+	OnOK();
+
+	void
+	EmptyComboBox
+			(
+			CComboBox	&	box
+			);
+
+	OSStatus
+	Populate(
+			CComboBox	&	box,
+			HKEY			key,
+			StringList	&	l
+			);
+	
+	void
+	SetModified( BOOL bChanged = TRUE );
+	
+	void
+	Commit();
+
+	OSStatus
+	Commit( CComboBox & box, HKEY key, DWORD enabled );
+
+	OSStatus
+	CreateKey( CString & name, DWORD enabled );
+
+	OSStatus
+	RegQueryString( HKEY key, CString valueName, CString & value );
+
+	CComboBox		m_regDomainsBox;
+	CButton			m_advertiseServicesButton;
+	CButton			m_sharedSecretButton;
+	BOOL			m_modified;
+	HKEY			m_setupKey;
+
+public:
+	afx_msg void OnCbnSelChange();
+	afx_msg void OnCbnEditChange();
+}; 
diff --git a/mdnsresponder/mDNSWindows/ControlPanel/ServicesPage.cpp b/mdnsresponder/mDNSWindows/ControlPanel/ServicesPage.cpp
new file mode 100755
index 0000000..e087157
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/ControlPanel/ServicesPage.cpp
@@ -0,0 +1,273 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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 "ServicesPage.h"
+#include "resource.h"
+
+#include "ControlPanelExe.h"
+#include "ConfigPropertySheet.h"
+
+#include <WinServices.h>
+    
+#define MAX_KEY_LENGTH 255
+
+
+IMPLEMENT_DYNCREATE(CServicesPage, CPropertyPage)
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CServicesPage::CServicesPage
+//---------------------------------------------------------------------------------------------------------------------------
+
+CServicesPage::CServicesPage()
+:
+	CPropertyPage(CServicesPage::IDD)
+{
+	//{{AFX_DATA_INIT(CServicesPage)
+	//}}AFX_DATA_INIT
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CServicesPage::~CServicesPage
+//---------------------------------------------------------------------------------------------------------------------------
+
+CServicesPage::~CServicesPage()
+{
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CServicesPage::DoDataExchange
+//---------------------------------------------------------------------------------------------------------------------------
+
+void CServicesPage::DoDataExchange(CDataExchange* pDX)
+{
+	CPropertyPage::DoDataExchange(pDX);
+	//{{AFX_DATA_MAP(CServicesPage)
+	//}}AFX_DATA_MAP
+	DDX_Control(pDX, IDC_ADVERTISE_SMB, m_SMBCheckBox);
+	DDX_Control(pDX, IDC_POWER_MANAGEMENT, m_powerManagementCheckBox);
+}
+
+BEGIN_MESSAGE_MAP(CServicesPage, CPropertyPage)
+	//{{AFX_MSG_MAP(CServicesPage)
+	//}}AFX_MSG_MAP
+
+	ON_BN_CLICKED(IDC_ADVERTISE_SMB, &CServicesPage::OnBnClickedAdvertiseSMB)
+	ON_BN_CLICKED(IDC_POWER_MANAGEMENT, &CServicesPage::OnBnClickedPowerManagement)
+
+END_MESSAGE_MAP()
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CServicesPage::SetModified
+//---------------------------------------------------------------------------------------------------------------------------
+
+void CServicesPage::SetModified( BOOL bChanged )
+{
+	m_modified = bChanged;
+
+	CPropertyPage::SetModified( bChanged );
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CServicesPage::OnSetActive
+//---------------------------------------------------------------------------------------------------------------------------
+
+BOOL
+CServicesPage::OnSetActive()
+{
+	CConfigPropertySheet	*	psheet;
+	HKEY						key = NULL;
+	DWORD						dwSize;
+	DWORD						enabled;
+	DWORD						err;
+	BOOL						b = CPropertyPage::OnSetActive();
+
+	psheet = reinterpret_cast<CConfigPropertySheet*>(GetParent());
+	require_quiet( psheet, exit );
+
+	m_SMBCheckBox.SetCheck( 0 );
+
+	// Now populate the browse domain box
+
+	err = RegCreateKeyEx( HKEY_LOCAL_MACHINE, kServiceParametersNode L"\\Services\\SMB", 0,
+		                  NULL, REG_OPTION_NON_VOLATILE, KEY_READ|KEY_WRITE|KEY_WOW64_32KEY, NULL, &key, NULL );
+	require_noerr( err, exit );
+
+	dwSize = sizeof( DWORD );
+	err = RegQueryValueEx( key, L"Advertise", NULL, NULL, (LPBYTE) &enabled, &dwSize );
+	require_noerr( err, exit );
+
+	m_SMBCheckBox.SetCheck( enabled );
+
+	RegCloseKey( key );
+	key = NULL;
+
+	m_powerManagementCheckBox.SetCheck( 0 );
+
+	// Now populate the browse domain box
+
+	err = RegCreateKeyEx( HKEY_LOCAL_MACHINE, kServiceParametersNode L"\\Power Management", 0,
+		                  NULL, REG_OPTION_NON_VOLATILE, KEY_READ|KEY_WRITE|KEY_WOW64_32KEY, NULL, &key, NULL );
+	require_noerr( err, exit );
+
+	dwSize = sizeof( DWORD );
+	err = RegQueryValueEx( key, L"Enabled", NULL, NULL, (LPBYTE) &enabled, &dwSize );
+	require_noerr( err, exit );
+
+	m_powerManagementCheckBox.SetCheck( enabled );
+ 
+exit:
+
+	if ( key )
+	{
+		RegCloseKey( key );
+	}
+
+	return b;
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CServicesPage::OnOK
+//---------------------------------------------------------------------------------------------------------------------------
+
+void
+CServicesPage::OnOK()
+{
+	if ( m_modified )
+	{
+		Commit();
+	}
+}
+
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CServicesPage::Commit
+//---------------------------------------------------------------------------------------------------------------------------
+
+void
+CServicesPage::Commit()
+{
+	HKEY		key		= NULL;
+	DWORD		enabled;
+	DWORD		err;
+
+	err = RegCreateKeyEx( HKEY_LOCAL_MACHINE, kServiceParametersNode L"\\Services\\SMB", 0,
+	                   	NULL, REG_OPTION_NON_VOLATILE, KEY_READ|KEY_WRITE|KEY_WOW64_32KEY, NULL, &key, NULL );
+	require_noerr( err, exit );
+
+	enabled = m_SMBCheckBox.GetCheck();
+	err = RegSetValueEx( key, L"Advertise", NULL, REG_DWORD, (LPBYTE) &enabled, sizeof( enabled ) );
+	require_noerr( err, exit );
+
+	RegCloseKey( key );
+	key = NULL;
+
+	err = RegCreateKeyEx( HKEY_LOCAL_MACHINE, kServiceParametersNode L"\\Power Management", 0,
+		                  NULL, REG_OPTION_NON_VOLATILE, KEY_READ|KEY_WRITE|KEY_WOW64_32KEY, NULL, &key, NULL );
+	require_noerr( err, exit );
+
+	enabled = m_powerManagementCheckBox.GetCheck();
+	err = RegSetValueEx( key, L"Enabled", NULL, REG_DWORD, (LPBYTE) &enabled, sizeof( enabled ) );
+	require_noerr( err, exit );
+	
+exit:
+
+	if ( key )
+	{
+		RegCloseKey( key );
+	}
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CServicesPage::OnBnClickedAdvertiseSMB
+//---------------------------------------------------------------------------------------------------------------------------
+
+void CServicesPage::OnBnClickedAdvertiseSMB()
+{
+	SetModified( TRUE );
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CServicesPage::OnBnClickedPowerManagement
+//---------------------------------------------------------------------------------------------------------------------------
+
+void CServicesPage::OnBnClickedPowerManagement()
+{
+	SetModified( TRUE );
+
+	if ( m_powerManagementCheckBox.GetCheck() )
+	{
+		CPowerManagementWarning dlg( GetParent() );
+
+		dlg.DoModal();
+	}
+}
+
+
+// CPowerManagementWarning dialog
+
+IMPLEMENT_DYNAMIC(CPowerManagementWarning, CDialog)
+CPowerManagementWarning::CPowerManagementWarning(CWnd* pParent /*=NULL*/)
+	: CDialog(CPowerManagementWarning::IDD, pParent)
+{
+}
+
+CPowerManagementWarning::~CPowerManagementWarning()
+{
+}
+
+void CPowerManagementWarning::DoDataExchange(CDataExchange* pDX)
+{
+	CDialog::DoDataExchange(pDX);
+	DDX_Control(pDX, IDC_ENERGY_SAVER, m_energySaverIcon);
+}
+
+
+BOOL
+CPowerManagementWarning::OnInitDialog()
+{	
+	BOOL b = CDialog::OnInitDialog();
+
+	const HICON hIcon = ( HICON ) ::LoadImage( GetNonLocalizedResources(), MAKEINTRESOURCE( IDI_ENERGY_SAVER ), IMAGE_ICON, 0, 0, 0);
+	
+	if ( hIcon )
+	{
+		m_energySaverIcon.SetIcon( hIcon );
+	}
+
+	return b;
+}
+
+
+void
+CPowerManagementWarning::OnOK()
+{
+	CDialog::OnOK();
+}
+
+
+BEGIN_MESSAGE_MAP(CPowerManagementWarning, CDialog)
+END_MESSAGE_MAP()
+
diff --git a/mdnsresponder/mDNSWindows/ControlPanel/ServicesPage.h b/mdnsresponder/mDNSWindows/ControlPanel/ServicesPage.h
new file mode 100755
index 0000000..d593a72
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/ControlPanel/ServicesPage.h
@@ -0,0 +1,123 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+#pragma once
+
+#include "stdafx.h"
+#include "resource.h"
+
+#include <DebugServices.h>
+#include <list>
+#include "afxcmn.h"
+
+#include "afxwin.h"
+
+
+
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CServicesPage
+//---------------------------------------------------------------------------------------------------------------------------
+
+class CServicesPage : public CPropertyPage
+{
+public:
+	CServicesPage();
+	~CServicesPage();
+
+protected:
+
+	//{{AFX_DATA(CServicesPage)
+	enum { IDD = IDR_APPLET_PAGE5 };
+	//}}AFX_DATA
+
+	//{{AFX_VIRTUAL(CServicesPage)
+	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
+	//}}AFX_VIRTUAL
+
+	DECLARE_DYNCREATE(CServicesPage)
+
+	//{{AFX_MSG(CServicesPage)
+	//}}AFX_MSG
+	DECLARE_MESSAGE_MAP()
+	
+private:
+	
+	typedef std::list<CString> StringList;
+
+	afx_msg BOOL
+	OnSetActive();
+	
+	afx_msg void
+	OnOK();
+	
+	void
+	SetModified( BOOL bChanged = TRUE );
+	
+	void
+	Commit();
+
+	BOOL			m_modified;
+
+public:
+private:
+
+	CButton m_SMBCheckBox;
+	CButton m_powerManagementCheckBox;
+
+public:
+
+
+	afx_msg void OnBnClickedAdvertiseSMB();
+	afx_msg void OnBnClickedPowerManagement();
+};
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CPowerManagementWarning
+//---------------------------------------------------------------------------------------------------------------------------
+
+class CPowerManagementWarning : public CDialog
+{
+	DECLARE_DYNAMIC(CPowerManagementWarning)
+
+public:
+
+	CPowerManagementWarning(CWnd* pParent = NULL);   // standard constructor
+
+	virtual ~CPowerManagementWarning();
+
+// Dialog Data
+
+	enum { IDD = IDR_POWER_MANAGEMENT_WARNING };
+
+protected:
+
+	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
+
+	virtual BOOL OnInitDialog();
+
+	virtual void OnOK();
+
+	DECLARE_MESSAGE_MAP()
+
+public:
+
+	CStatic m_energySaverIcon;
+};
+
diff --git a/mdnsresponder/mDNSWindows/ControlPanel/SharedSecret.cpp b/mdnsresponder/mDNSWindows/ControlPanel/SharedSecret.cpp
new file mode 100644
index 0000000..3d19295
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/ControlPanel/SharedSecret.cpp
@@ -0,0 +1,115 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+    
+// SharedSecret.cpp : implementation file
+//
+
+
+#include <Secret.h>
+#include "stdafx.h"
+#include "SharedSecret.h"
+#include <WinServices.h>
+
+#include <DebugServices.h>
+
+
+// SharedSecret dialog
+
+IMPLEMENT_DYNAMIC(CSharedSecret, CDialog)
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CSharedSecret::CSharedSecret
+//---------------------------------------------------------------------------------------------------------------------------
+
+CSharedSecret::CSharedSecret(CWnd* pParent /*=NULL*/)
+	: CDialog(CSharedSecret::IDD, pParent)
+	, m_key(_T(""))
+	, m_secret(_T(""))
+{
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CSharedSecret::~CSharedSecret
+//---------------------------------------------------------------------------------------------------------------------------
+
+CSharedSecret::~CSharedSecret()
+{
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CSharedSecret::DoDataExchange
+//---------------------------------------------------------------------------------------------------------------------------
+
+void CSharedSecret::DoDataExchange(CDataExchange* pDX)
+{
+	CDialog::DoDataExchange(pDX);
+	DDX_Text(pDX, IDC_KEY, m_key );
+	DDX_Text(pDX, IDC_SECRET, m_secret );
+}
+
+
+BEGIN_MESSAGE_MAP(CSharedSecret, CDialog)
+END_MESSAGE_MAP()
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CSharedSecret::Load
+//---------------------------------------------------------------------------------------------------------------------------
+
+void
+CSharedSecret::Load( CString zone )
+{
+	char	zoneUTF8[ 256 ];
+	char	outDomain[ 256 ];
+	char	outKey[ 256 ];
+	char	outSecret[ 256 ];
+
+	StringObjectToUTF8String( zone, zoneUTF8, sizeof( zoneUTF8 ) );
+
+	if ( LsaGetSecret( zoneUTF8, outDomain, sizeof( outDomain ) / sizeof( TCHAR ), outKey, sizeof( outKey ) / sizeof( TCHAR ), outSecret, sizeof( outSecret ) / sizeof( TCHAR ) ) )
+	{
+		m_key		= outKey;
+		m_secret	= outSecret;
+	}
+	else
+	{
+		m_key = zone;
+	}
+}
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CSharedSecret::Commit
+//---------------------------------------------------------------------------------------------------------------------------
+
+void
+CSharedSecret::Commit( CString zone )
+{
+	char	zoneUTF8[ 256 ];
+	char	keyUTF8[ 256 ];
+	char	secretUTF8[ 256 ];
+
+	StringObjectToUTF8String( zone, zoneUTF8, sizeof( zoneUTF8 ) );
+	StringObjectToUTF8String( m_key, keyUTF8, sizeof( keyUTF8 ) );
+	StringObjectToUTF8String( m_secret, secretUTF8, sizeof( secretUTF8 ) );
+
+	LsaSetSecret( zoneUTF8, keyUTF8, secretUTF8 );
+}
diff --git a/mdnsresponder/mDNSWindows/ControlPanel/SharedSecret.h b/mdnsresponder/mDNSWindows/ControlPanel/SharedSecret.h
new file mode 100644
index 0000000..be82d8b
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/ControlPanel/SharedSecret.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+    
+#pragma once
+
+#include "resource.h"
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+//	CSharedSecret
+//---------------------------------------------------------------------------------------------------------------------------
+
+class CSharedSecret : public CDialog
+{
+	DECLARE_DYNAMIC(CSharedSecret)
+
+public:
+	CSharedSecret(CWnd* pParent = NULL);   // standard constructor
+	virtual ~CSharedSecret();
+
+// Dialog Data
+	enum { IDD = IDR_SECRET };
+
+	void
+	Load( CString zone );
+
+	void
+	Commit( CString zone );
+
+protected:
+	virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
+
+	DECLARE_MESSAGE_MAP()
+
+public:
+
+	CString m_key;
+	CString m_secret;
+};
diff --git a/mdnsresponder/mDNSWindows/ControlPanel/res/ControlPanel.dll.manifest b/mdnsresponder/mDNSWindows/ControlPanel/res/ControlPanel.dll.manifest
new file mode 100644
index 0000000..903b02b
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/ControlPanel/res/ControlPanel.dll.manifest
@@ -0,0 +1,10 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+	<assemblyIdentity version="1.0.0.0" processorArchitecture="X86" name="Apple.Bonjour.ControlPanel" type="win32"/>
+	<description>Control Panel applet for configuring Wide-Area Bonjour.</description>
+	<dependency>
+		<dependentAssembly>
+			<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="X86" publicKeyToken="6595b64144ccf1df" language="*" />
+		</dependentAssembly>
+	</dependency>
+</assembly>
diff --git a/mdnsresponder/mDNSWindows/ControlPanel/res/ControlPanel.exe.manifest b/mdnsresponder/mDNSWindows/ControlPanel/res/ControlPanel.exe.manifest
new file mode 100644
index 0000000..4879215
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/ControlPanel/res/ControlPanel.exe.manifest
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+	<assemblyIdentity version="1.0.0.0" processorArchitecture="X86" name="Apple.Bonjour.ControlPanel" type="win32"/>
+	<description>Control Panel applet for configuring Wide-Area Bonjour.</description>
+	<dependency>
+		<dependentAssembly>
+			<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="X86" publicKeyToken="6595b64144ccf1df" language="*" />
+		</dependentAssembly>
+	</dependency>
+	<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
+		<security>
+			<requestedPrivileges>
+				<requestedExecutionLevel level="requireAdministrator" uiAccess="false"/>
+			</requestedPrivileges>
+		</security>
+	</trustInfo>
+</assembly>
diff --git a/mdnsresponder/mDNSWindows/ControlPanel/res/ControlPanel.manifest b/mdnsresponder/mDNSWindows/ControlPanel/res/ControlPanel.manifest
new file mode 100644
index 0000000..4879215
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/ControlPanel/res/ControlPanel.manifest
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+	<assemblyIdentity version="1.0.0.0" processorArchitecture="X86" name="Apple.Bonjour.ControlPanel" type="win32"/>
+	<description>Control Panel applet for configuring Wide-Area Bonjour.</description>
+	<dependency>
+		<dependentAssembly>
+			<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="X86" publicKeyToken="6595b64144ccf1df" language="*" />
+		</dependentAssembly>
+	</dependency>
+	<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
+		<security>
+			<requestedPrivileges>
+				<requestedExecutionLevel level="requireAdministrator" uiAccess="false"/>
+			</requestedPrivileges>
+		</security>
+	</trustInfo>
+</assembly>
diff --git a/mdnsresponder/mDNSWindows/ControlPanel/res/ControlPanel.rc2 b/mdnsresponder/mDNSWindows/ControlPanel/res/ControlPanel.rc2
new file mode 100755
index 0000000..e3f7422
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/ControlPanel/res/ControlPanel.rc2
@@ -0,0 +1,13 @@
+//

+// CPL_PP.RC2 - resources Microsoft Visual C++ does not edit directly

+//

+

+#ifdef APSTUDIO_INVOKED

+	#error this file is not editable by Microsoft Visual C++

+#endif //APSTUDIO_INVOKED

+

+

+/////////////////////////////////////////////////////////////////////////////

+// Add manually edited resources here...

+

+/////////////////////////////////////////////////////////////////////////////

diff --git a/mdnsresponder/mDNSWindows/ControlPanel/res/ControlPanel64.manifest b/mdnsresponder/mDNSWindows/ControlPanel/res/ControlPanel64.manifest
new file mode 100644
index 0000000..a47a4e2
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/ControlPanel/res/ControlPanel64.manifest
@@ -0,0 +1,17 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+	<assemblyIdentity version="1.0.0.0" processorArchitecture="X86" name="Apple.Bonjour.ControlPanel" type="win32"/>
+	<description>Control Panel applet for configuring Wide-Area Bonjour.</description>
+	<dependency>
+		<dependentAssembly>
+			<assemblyIdentity type="win32" name="Microsoft.Windows.Common-Controls" version="6.0.0.0" processorArchitecture="amd64" publicKeyToken="6595b64144ccf1df" language="*" />
+		</dependentAssembly>
+	</dependency>
+	<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
+		<security>
+			<requestedPrivileges>
+				<requestedExecutionLevel level="requireAdministrator" uiAccess="false"/>
+			</requestedPrivileges>
+		</security>
+	</trustInfo>
+</assembly>
diff --git a/mdnsresponder/mDNSWindows/ControlPanel/res/EnergySaver.ico b/mdnsresponder/mDNSWindows/ControlPanel/res/EnergySaver.ico
new file mode 100755
index 0000000..c2b935b
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/ControlPanel/res/EnergySaver.ico
Binary files differ
diff --git a/mdnsresponder/mDNSWindows/ControlPanel/res/controlpanel.ico b/mdnsresponder/mDNSWindows/ControlPanel/res/controlpanel.ico
new file mode 100755
index 0000000..7ef6ebb
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/ControlPanel/res/controlpanel.ico
Binary files differ
diff --git a/mdnsresponder/mDNSWindows/ControlPanel/res/failure.ico b/mdnsresponder/mDNSWindows/ControlPanel/res/failure.ico
new file mode 100755
index 0000000..f0b8f2b
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/ControlPanel/res/failure.ico
Binary files differ
diff --git a/mdnsresponder/mDNSWindows/ControlPanel/res/success.ico b/mdnsresponder/mDNSWindows/ControlPanel/res/success.ico
new file mode 100755
index 0000000..9b97584
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/ControlPanel/res/success.ico
Binary files differ
diff --git a/mdnsresponder/mDNSWindows/ControlPanel/resource.h b/mdnsresponder/mDNSWindows/ControlPanel/resource.h
new file mode 100644
index 0000000..fec673f
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/ControlPanel/resource.h
@@ -0,0 +1,56 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by ControlPanel.rc
+//
+#define IDR_APPLET                      131
+#define IDR_APPLET_PAGE1                131
+#define IDS_APPLET_DESCRIPTION          132
+#define IDR_APPLET_PAGE2                132
+#define IDR_SECRET                      133
+#define IDR_APPLET_PAGE3                134
+#define IDR_APPLET_PAGE4                135
+#define IDR_APPLET_PAGE5                136
+#define IDI_FAILURE                     140
+#define IDI_SUCCESS                     141
+#define IDI_ENERGY_SAVER				142
+#define IDR_ADD_BROWSE_DOMAIN           143
+#define IDR_POWER_MANAGEMENT_WARNING	144
+#define IDS_REINSTALL					145
+#define IDS_REINSTALL_CAPTION			146
+#define IDS_APPLET_NAME					147
+#define IDS_APPLET_TOOLTIP				148
+#define IDC_HOSTNAME                    1000
+#define IDC_USERNAME					1001
+#define IDC_PASSWORD					1002
+#define IDC_ADVERTISE_SERVICES			1003
+#define IDC_BUTTON1                     1004
+#define IDC_COMBO1                      1005
+#define IDC_CHECK1                      1006
+#define IDC_COMBO2                      1007
+#define IDC_EDIT2                       1008
+#define IDC_SECRET                      1009
+#define IDC_COMBO3                      1010
+#define IDC_FAILURE                     1011
+#define IDC_SUCCESS                     1012
+#define IDC_SECRET_NAME                 1013
+#define IDC_NAME                        1014
+#define IDC_KEY                         1015
+#define IDC_LIST1                       1016
+#define IDC_BROWSE_LIST                 1017
+#define IDC_BUTTON2                     1018
+#define IDC_REMOVE_BROWSE_DOMAIN        1019
+#define IDC_ADD_BROWSE_DOMAIN           1020
+#define IDC_POWER_MANAGEMENT            1021
+#define IDC_ADVERTISE_SMB	            1022
+#define IDC_ENERGY_SAVER				1023
+
+// Next default values for new objects
+// 
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE        149
+#define _APS_NEXT_COMMAND_VALUE         32771
+#define _APS_NEXT_CONTROL_VALUE         1024
+#define _APS_NEXT_SYMED_VALUE           101
+#endif
+#endif
diff --git a/mdnsresponder/mDNSWindows/ControlPanel/stdafx.cpp b/mdnsresponder/mDNSWindows/ControlPanel/stdafx.cpp
new file mode 100755
index 0000000..e05ec3d
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/ControlPanel/stdafx.cpp
@@ -0,0 +1,20 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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 "stdafx.h"
+
+
diff --git a/mdnsresponder/mDNSWindows/ControlPanel/stdafx.h b/mdnsresponder/mDNSWindows/ControlPanel/stdafx.h
new file mode 100755
index 0000000..246752e
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/ControlPanel/stdafx.h
@@ -0,0 +1,64 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+#pragma once
+
+#ifndef VC_EXTRALEAN
+#define VC_EXTRALEAN		// Exclude rarely-used stuff from Windows headers
+#endif
+
+// Modify the following defines if you have to target a platform prior to the ones specified below.
+// Refer to MSDN for the latest info on corresponding values for different platforms.
+#ifndef WINVER				// Allow use of features specific to Windows 95 and Windows NT 4 or later.
+#define WINVER 0x0400		// Change this to the appropriate value to target Windows 98 and Windows 2000 or later.
+#endif
+
+#ifndef _WIN32_WINNT		// Allow use of features specific to Windows NT 4 or later.
+#define _WIN32_WINNT 0x0400	// Change this to the appropriate value to target Windows 98 and Windows 2000 or later.
+#endif						
+
+#ifndef _WIN32_WINDOWS		// Allow use of features specific to Windows 98 or later.
+#define _WIN32_WINDOWS 0x0410 // Change this to the appropriate value to target Windows Me or later.
+#endif
+
+// Step 3: We want to see one image, but not a tile
+#ifndef _WIN32_IE			// Allow use of features specific to IE 4.0 or later.
+#define _WIN32_IE 0x0500	// Change this to the appropriate value to target IE 5.0 or later.
+#endif
+
+#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS	// some CString constructors will be explicit
+
+// turns off MFC's hiding of some common and often safely ignored warning messages
+#define _AFX_ALL_WARNINGS
+
+#if !defined(_WSPIAPI_COUNTOF)
+#	define _WSPIAPI_COUNTOF(_Array) (sizeof(_Array) / sizeof(_Array[0]))
+#endif
+
+#include <afxwin.h>         // MFC core and standard components
+#include <afxext.h>         // MFC extensions
+#include <afxdisp.h>        // MFC Automation classes
+
+#include <afxdtctl.h>		// MFC support for Internet Explorer 4 Common Controls
+#ifndef _AFX_NO_AFXCMN_SUPPORT
+#include <afxcmn.h>			// MFC support for Windows Common Controls
+#endif // _AFX_NO_AFXCMN_SUPPORT
+#include <afxdlgs.h>
+
+#include <cpl.h>            // Control Panel Applet functions and defines
+
+#include <afxtempl.h>       // MFC Template support
diff --git a/mdnsresponder/mDNSWindows/DLL.NET/AssemblyInfo.cpp b/mdnsresponder/mDNSWindows/DLL.NET/AssemblyInfo.cpp
new file mode 100755
index 0000000..40f0b5d
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DLL.NET/AssemblyInfo.cpp
@@ -0,0 +1,84 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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 "stdafx.h"
+#include "WinVersRes.h"
+
+using namespace System;
+using namespace System::Reflection;
+using namespace System::Runtime::CompilerServices;
+using namespace System::Runtime::InteropServices;
+using namespace System::Security::Permissions;
+
+//
+// General Information about an assembly is controlled through the following 
+// set of attributes. Change these attribute values to modify the information
+// associated with an assembly.
+//
+[assembly:AssemblyTitleAttribute("dnssd.NET")];
+[assembly:AssemblyDescriptionAttribute(".NET wrapper for DNS-SD services")];
+[assembly:AssemblyConfigurationAttribute("")];
+[assembly:AssemblyCompanyAttribute("Apple Inc.")];
+[assembly:AssemblyProductAttribute("")];
+[assembly:AssemblyCopyrightAttribute("Apple Inc.")];
+[assembly:AssemblyTrademarkAttribute("")];
+[assembly:AssemblyCultureAttribute("")];		
+
+//
+// Version information for an assembly consists of the following four values:
+//
+//      Major Version
+//      Minor Version 
+//      Build Number
+//      Revision
+//
+// You can specify all the value or you can default the Revision and Build Numbers 
+// by using the '*' as shown below:
+
+[assembly:AssemblyVersionAttribute(MASTER_PROD_VERS_STR2)];
+
+//
+// In order to sign your assembly you must specify a key to use. Refer to the
+// Microsoft .NET Framework documentation for more information on assembly
+// signing.
+//
+// Use the attributes below to control which key is used for signing.
+//
+// Notes:
+//   (*) If no key is specified, the assembly is not signed.
+//   (*) KeyName refers to a key that has been installed in the Crypto Service
+//       Provider (CSP) on your machine. KeyFile refers to a file which contains
+//       a key.
+//   (*) If the KeyFile and the KeyName values are both specified, the
+//       following processing occurs:
+//       (1) If the KeyName can be found in the CSP, that key is used.
+//       (2) If the KeyName does not exist and the KeyFile does exist, the key
+//           in the KeyFile is installed into the CSP and used.
+//   (*) In order to create a KeyFile, you can use the sn.exe (Strong Name)
+//       utility
+//        When specifying the KeyFile, the location of the KeyFile should be
+//        relative to the project directory.
+//   (*) Delay Signing is an advanced option - see the Microsoft .NET Framework
+//       documentation for more information on this.
+//
+[assembly:AssemblyDelaySignAttribute(false)];
+[assembly:AssemblyKeyFileAttribute("dnssd_NET.snk")];
+[assembly:AssemblyKeyNameAttribute("")];
+
+[assembly:ComVisible(false)];
+[assembly:CLSCompliantAttribute(true)];
+[assembly:SecurityPermission(SecurityAction::RequestMinimum, UnmanagedCode = true)];
diff --git a/mdnsresponder/mDNSWindows/DLL.NET/PString.h b/mdnsresponder/mDNSWindows/DLL.NET/PString.h
new file mode 100755
index 0000000..0249c05
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DLL.NET/PString.h
@@ -0,0 +1,70 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+    
+#pragma once
+
+using namespace System;
+using namespace System::Text;
+
+namespace Apple
+{
+	__gc class PString
+	{
+	public:
+
+		PString(String* string)
+		{
+			if (string != NULL)
+			{
+				Byte unicodeBytes[] = Encoding::Unicode->GetBytes(string);
+				Byte utf8Bytes[] = Encoding::Convert(Encoding::Unicode, Encoding::UTF8, unicodeBytes);
+				m_p = Marshal::AllocHGlobal(utf8Bytes->Length + 1);
+
+				Byte __pin * p = &utf8Bytes[0];
+				char * hBytes = static_cast<char*>(m_p.ToPointer());
+				memcpy(hBytes, p, utf8Bytes->Length);
+				hBytes[utf8Bytes->Length] = '\0';
+			}
+			else
+			{
+				m_p = NULL;
+			}
+		}
+
+		~PString()
+		{
+			Marshal::FreeHGlobal(m_p);
+		}
+
+		const char*
+		c_str()
+		{
+			if (m_p != NULL)
+			{
+				return static_cast<const char*>(m_p.ToPointer());
+			}
+			else
+			{
+				return NULL;
+			}
+		}
+		
+	protected:
+
+		IntPtr m_p;
+	};
+}
diff --git a/mdnsresponder/mDNSWindows/DLL.NET/Stdafx.cpp b/mdnsresponder/mDNSWindows/DLL.NET/Stdafx.cpp
new file mode 100755
index 0000000..ef03e21
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DLL.NET/Stdafx.cpp
@@ -0,0 +1,22 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+    
+// stdafx.cpp : source file that includes just the standard includes
+// dotNET.pch will be the pre-compiled header
+// stdafx.obj will contain the pre-compiled type information
+
+#include "stdafx.h"
diff --git a/mdnsresponder/mDNSWindows/DLL.NET/Stdafx.h b/mdnsresponder/mDNSWindows/DLL.NET/Stdafx.h
new file mode 100755
index 0000000..d2e5c1a
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DLL.NET/Stdafx.h
@@ -0,0 +1,33 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+    
+// stdafx.h : include file for standard system include files,
+// or project specific include files that are used frequently,
+// but are changed infrequently
+
+#pragma once
+
+#if !defined(_WSPIAPI_COUNTOF)
+#	define _WSPIAPI_COUNTOF(_Array) (sizeof(_Array) / sizeof(_Array[0]))
+#endif
+
+#using <mscorlib.dll>
+#using <System.dll>
+
+struct _DNSServiceRef_t {};
+struct _DNSRecordRef_t {};
+
diff --git a/mdnsresponder/mDNSWindows/DLL.NET/dnssd_NET.cpp b/mdnsresponder/mDNSWindows/DLL.NET/dnssd_NET.cpp
new file mode 100755
index 0000000..3e22146
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DLL.NET/dnssd_NET.cpp
@@ -0,0 +1,1234 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+    
+// This is the main DLL file.
+
+#include "stdafx.h"
+
+#include "dnssd_NET.h"
+#include "DebugServices.h"
+#include "PString.h"
+
+
+using namespace System::Net::Sockets;
+using namespace System::Diagnostics;
+using namespace Apple;
+using namespace Apple::DNSSD;
+
+
+//===========================================================================================================================
+//	Constants
+//===========================================================================================================================
+
+#define	DEBUG_NAME	"[dnssd.NET] "
+
+//
+// ConvertToString
+//
+static String*
+ConvertToString(const char * utf8String)
+{
+	return __gc new String(utf8String, 0, strlen(utf8String), __gc new UTF8Encoding(true, true));
+}
+
+
+//
+// class ServiceRef
+//
+// ServiceRef serves as the base class for all DNSService operations.
+//
+// It manages the DNSServiceRef, and implements processing the
+// result
+//
+ServiceRef::ServiceRef(Object * callback)
+:
+	m_bDisposed(false),
+	m_callback(callback),
+	m_thread(NULL)
+{
+	m_impl = new ServiceRefImpl(this);
+}
+
+
+ServiceRef::~ServiceRef()
+{
+}
+
+
+//
+// StartThread
+//
+// Starts the main processing thread
+//
+void
+ServiceRef::StartThread()
+{
+	check( m_impl != NULL );
+
+	m_impl->SetupEvents();
+
+	m_thread		=	new Thread(new ThreadStart(this, &Apple::DNSSD::ServiceRef::ProcessingThread));
+	m_thread->Name	=	S"DNSService Thread";
+	m_thread->IsBackground = true;
+	
+	m_thread->Start();
+}
+
+
+//
+// ProcessingThread
+//
+// The Thread class can only invoke methods in MC++ types.  So we
+// make a ProcessingThread method that forwards to the impl
+//
+void
+ServiceRef::ProcessingThread()
+{
+	m_impl->ProcessingThread();
+}
+
+
+//
+// Dispose
+//
+// Calls impl-Dispose().  This ultimately will call DNSServiceRefDeallocate()
+//
+void
+ServiceRef::Dispose()
+{
+	check(m_impl != NULL);
+	check(m_bDisposed == false);
+
+	if (!m_bDisposed)
+	{
+		m_bDisposed = true;
+
+		//
+		// Call Dispose.  This won't call DNSServiceRefDeallocate()
+		// necessarily. It depends on what thread this is being
+		// called in.
+		//
+		m_impl->Dispose();
+		m_impl = NULL;
+
+		m_thread = NULL;
+
+		GC::SuppressFinalize(this);  
+	}
+}
+
+
+//
+// EnumerateDomainsDispatch
+//
+// Dispatch a reply to the delegate.
+//
+void
+ServiceRef::EnumerateDomainsDispatch
+						(
+						ServiceFlags	flags,
+						int				interfaceIndex,
+						ErrorCode		errorCode,
+						String		*	replyDomain
+						)
+{
+	if ((m_callback != NULL) && (m_impl != NULL))
+	{
+		DNSService::EnumerateDomainsReply * OnEnumerateDomainsReply = static_cast<DNSService::EnumerateDomainsReply*>(m_callback);
+		OnEnumerateDomainsReply(this, flags, interfaceIndex, errorCode, replyDomain);
+	}
+}
+
+
+//
+// RegisterDispatch
+//
+// Dispatch a reply to the delegate.
+//
+void
+ServiceRef::RegisterDispatch
+				(
+				ServiceFlags	flags,
+				ErrorCode		errorCode,
+ 				String		*	name,
+				String		*	regtype,
+				String		*	domain
+				)
+{
+	if ((m_callback != NULL) && (m_impl != NULL))
+	{
+		DNSService::RegisterReply * OnRegisterReply = static_cast<DNSService::RegisterReply*>(m_callback);
+		OnRegisterReply(this, flags, errorCode, name, regtype, domain);
+	}
+}
+
+
+//
+// BrowseDispatch
+//
+// Dispatch a reply to the delegate.
+//
+void
+ServiceRef::BrowseDispatch
+			(
+			ServiceFlags	flags,
+			int				interfaceIndex,
+			ErrorCode		errorCode,
+			String		*	serviceName,
+			String		*	regtype,
+			String		*	replyDomain
+			)
+{
+	if ((m_callback != NULL) && (m_impl != NULL))
+	{
+		DNSService::BrowseReply * OnBrowseReply = static_cast<DNSService::BrowseReply*>(m_callback);
+		OnBrowseReply(this, flags, interfaceIndex, errorCode, serviceName, regtype, replyDomain);
+	}
+}
+
+
+//
+// ResolveDispatch
+//
+// Dispatch a reply to the delegate.
+//
+void
+ServiceRef::ResolveDispatch
+			(
+			ServiceFlags	flags,
+			int				interfaceIndex,
+			ErrorCode		errorCode,
+			String		*	fullname,
+			String		*	hosttarget,
+			int				port,
+			Byte			txtRecord[]
+			)
+{
+	if ((m_callback != NULL) && (m_impl != NULL))
+	{
+		DNSService::ResolveReply * OnResolveReply = static_cast<DNSService::ResolveReply*>(m_callback);
+		OnResolveReply(this, flags, interfaceIndex, errorCode, fullname, hosttarget, port, txtRecord);
+	}
+}
+
+
+//
+// RegisterRecordDispatch
+//
+// Dispatch a reply to the delegate.
+//
+void
+ServiceRef::RegisterRecordDispatch
+				(
+				ServiceFlags	flags,
+				ErrorCode		errorCode,
+				RecordRef	*	record
+				)
+{
+	if ((m_callback != NULL) && (m_impl != NULL))
+	{
+		DNSService::RegisterRecordReply * OnRegisterRecordReply = static_cast<DNSService::RegisterRecordReply*>(m_callback);
+		OnRegisterRecordReply(this, flags, errorCode, record);
+	}
+}
+
+
+//
+// QueryRecordDispatch
+//
+// Dispatch a reply to the delegate.
+//
+void
+ServiceRef::QueryRecordDispatch
+					(
+					ServiceFlags	flags,
+					int				interfaceIndex,
+					ErrorCode		errorCode,
+					String		*	fullname,
+					int				rrtype,
+					int				rrclass,
+					Byte			rdata[],
+					int				ttl
+					)
+{
+	if ((m_callback != NULL) && (m_impl != NULL))
+	{
+		DNSService::QueryRecordReply * OnQueryRecordReply = static_cast<DNSService::QueryRecordReply*>(m_callback);
+		OnQueryRecordReply(this, flags, interfaceIndex, errorCode, fullname, rrtype, rrclass, rdata, ttl);
+	}
+}
+
+
+//
+// ServiceRefImpl::ServiceRefImpl()
+//
+// Constructs a new ServiceRefImpl.  We save the pointer to our enclosing
+// class in a gcroot handle.  This satisfies the garbage collector as
+// the outer class is a managed type
+//
+ServiceRef::ServiceRefImpl::ServiceRefImpl(ServiceRef * outer)
+:
+	m_socketEvent(NULL),
+	m_stopEvent(NULL),
+	m_disposed(false),
+	m_outer(outer),
+	m_ref(NULL)
+{
+	m_threadId = GetCurrentThreadId();
+}
+
+
+//
+// ServiceRefImpl::~ServiceRefImpl()
+//
+// Deallocate all resources associated with the ServiceRefImpl
+//
+ServiceRef::ServiceRefImpl::~ServiceRefImpl()
+{
+	if (m_socketEvent != NULL)
+	{
+		CloseHandle(m_socketEvent);
+		m_socketEvent = NULL;
+	}
+
+	if (m_stopEvent != NULL)
+	{
+		CloseHandle(m_stopEvent);
+		m_stopEvent = NULL;
+	}
+
+	if (m_ref != NULL)
+	{
+		DNSServiceRefDeallocate(m_ref);
+		m_ref = NULL;
+	}
+}
+
+
+//
+// ServiceRefImpl::SetupEvents()
+//
+// Setup the events necessary to manage multi-threaded dispatch
+// of DNSService Events
+//
+void
+ServiceRef::ServiceRefImpl::SetupEvents()
+{
+	check(m_ref != NULL);
+
+	m_socket		=	(SOCKET) DNSServiceRefSockFD(m_ref);
+	check(m_socket != INVALID_SOCKET);
+
+	m_socketEvent	=	CreateEvent(NULL, 0, 0, NULL);
+
+	if (m_socketEvent == NULL)
+	{
+		throw new DNSServiceException(Unknown);
+	}
+
+	int err = WSAEventSelect(m_socket, m_socketEvent, FD_READ|FD_CLOSE);
+
+	if (err != 0)
+	{
+		throw new DNSServiceException(Unknown);
+	}
+
+	m_stopEvent = CreateEvent(NULL, 0, 0, NULL);
+
+	if (m_stopEvent == NULL)
+	{
+		throw new DNSServiceException(Unknown);
+	}
+}
+
+
+//
+// ServiceRefImpl::ProcessingThread()
+//
+// Wait for socket events on the DNSServiceRefSockFD().  Also wait
+// for stop events
+//
+void
+ServiceRef::ServiceRefImpl::ProcessingThread()
+{
+	check( m_socketEvent != NULL );
+	check( m_stopEvent != NULL );
+	check( m_ref != NULL );
+	
+	HANDLE handles[2];
+
+	handles[0] = m_socketEvent;
+	handles[1] = m_stopEvent;
+
+	while (m_disposed == false)
+	{
+		int ret = WaitForMultipleObjects(2, handles, FALSE, INFINITE);
+
+		//
+		// it's a socket event
+		//
+		if (ret == WAIT_OBJECT_0)
+		{
+			DNSServiceProcessResult(m_ref);
+		}
+		//
+		// else it's a stop event
+		//
+		else if (ret == WAIT_OBJECT_0 + 1)
+		{
+			break;
+		}
+		else
+		{
+			//
+			// unexpected wait result
+			//
+			dlog( kDebugLevelWarning, DEBUG_NAME "%s: unexpected wait result (result=0x%08X)\n", __ROUTINE__, ret );
+		}
+	}
+
+	delete this;
+}
+
+
+//
+// ServiceRefImpl::Dispose()
+//
+// Calls DNSServiceRefDeallocate()
+//
+void
+ServiceRef::ServiceRefImpl::Dispose()
+{
+	OSStatus	err;
+	BOOL		ok;
+
+	check(m_disposed == false);
+
+	m_disposed = true;
+
+	ok = SetEvent(m_stopEvent);
+	err = translate_errno( ok, (OSStatus) GetLastError(), kUnknownErr );
+	require_noerr( err, exit );
+
+exit:
+
+	return;
+}
+
+
+//
+// ServiceRefImpl::EnumerateDomainsCallback()
+//
+// This is the callback from dnssd.dll.  We pass this up to our outer, managed type
+//
+void DNSSD_API
+ServiceRef::ServiceRefImpl::EnumerateDomainsCallback
+											(
+											DNSServiceRef			sdRef,
+											DNSServiceFlags			flags,
+											uint32_t				interfaceIndex,
+											DNSServiceErrorType		errorCode,
+											const char			*	replyDomain,
+											void				*	context
+											)
+{
+	ServiceRef::ServiceRefImpl * self = static_cast<ServiceRef::ServiceRefImpl*>(context);
+
+	check( self != NULL );
+	check( self->m_outer != NULL );
+
+	if (self->m_disposed == false)
+	{
+		self->m_outer->EnumerateDomainsDispatch((ServiceFlags) flags, interfaceIndex, (ErrorCode) errorCode, ConvertToString(replyDomain));
+	}
+}
+
+
+//
+// ServiceRefImpl::RegisterCallback()
+//
+// This is the callback from dnssd.dll.  We pass this up to our outer, managed type
+//
+void DNSSD_API
+ServiceRef::ServiceRefImpl::RegisterCallback
+							(
+							DNSServiceRef			sdRef,
+							DNSServiceFlags			flags,
+							DNSServiceErrorType		errorCode,
+							const char			*	name,
+							const char			*	regtype,
+							const char			*	domain,
+							void				*	context
+							)
+{
+	ServiceRef::ServiceRefImpl * self = static_cast<ServiceRef::ServiceRefImpl*>(context);
+
+	check( self != NULL );
+	check( self->m_outer != NULL );
+	
+	if (self->m_disposed == false)
+	{
+		self->m_outer->RegisterDispatch((ServiceFlags) flags, (ErrorCode) errorCode, ConvertToString(name), ConvertToString(regtype), ConvertToString(domain));
+	}
+}
+
+
+//
+// ServiceRefImpl::BrowseCallback()
+//
+// This is the callback from dnssd.dll.  We pass this up to our outer, managed type
+//
+void DNSSD_API
+ServiceRef::ServiceRefImpl::BrowseCallback
+							(
+							DNSServiceRef			sdRef,
+   							DNSServiceFlags			flags,
+							uint32_t				interfaceIndex,
+							DNSServiceErrorType		errorCode,
+							const char			*	serviceName,
+							const char			*	regtype,
+							const char			*	replyDomain,
+							void				*	context
+							)
+{
+	ServiceRef::ServiceRefImpl * self = static_cast<ServiceRef::ServiceRefImpl*>(context);
+
+	check( self != NULL );
+	check( self->m_outer != NULL );
+	
+	if (self->m_disposed == false)
+	{
+		self->m_outer->BrowseDispatch((ServiceFlags) flags, interfaceIndex, (ErrorCode) errorCode, ConvertToString(serviceName), ConvertToString(regtype), ConvertToString(replyDomain));
+	}
+}
+
+
+//
+// ServiceRefImpl::ResolveCallback()
+//
+// This is the callback from dnssd.dll.  We pass this up to our outer, managed type
+//
+void DNSSD_API
+ServiceRef::ServiceRefImpl::ResolveCallback
+							(
+							DNSServiceRef			sdRef,
+							DNSServiceFlags			flags,
+							uint32_t				interfaceIndex,
+							DNSServiceErrorType		errorCode,
+							const char			*	fullname,
+							const char			*	hosttarget,
+							uint16_t				notAnIntPort,
+							uint16_t				txtLen,
+							const char			*	txtRecord,
+							void				*	context
+							)
+{
+	ServiceRef::ServiceRefImpl * self = static_cast<ServiceRef::ServiceRefImpl*>(context);
+
+	check( self != NULL );
+	check( self->m_outer != NULL );
+	
+	if (self->m_disposed == false)
+	{
+		Byte txtRecordBytes[];
+
+		txtRecordBytes = NULL;
+
+		if (txtLen > 0)
+		{
+			//
+			// copy raw memory into managed byte array
+			//
+			txtRecordBytes		=	new Byte[txtLen];
+			Byte __pin	*	p	=	&txtRecordBytes[0];
+			memcpy(p, txtRecord, txtLen);
+		}
+
+		self->m_outer->ResolveDispatch((ServiceFlags) flags, interfaceIndex, (ErrorCode) errorCode, ConvertToString(fullname), ConvertToString(hosttarget), ntohs(notAnIntPort), txtRecordBytes);
+	}	
+}
+
+
+//
+// ServiceRefImpl::RegisterRecordCallback()
+//
+// This is the callback from dnssd.dll.  We pass this up to our outer, managed type
+//
+void DNSSD_API
+ServiceRef::ServiceRefImpl::RegisterRecordCallback
+								(
+								DNSServiceRef		sdRef,
+								DNSRecordRef		rrRef,
+								DNSServiceFlags		flags,
+								DNSServiceErrorType	errorCode,
+								void			*	context
+								)
+{
+	ServiceRef::ServiceRefImpl * self = static_cast<ServiceRef::ServiceRefImpl*>(context);
+
+	check( self != NULL );
+	check( self->m_outer != NULL );
+	
+	if (self->m_disposed == false)
+	{
+		RecordRef * record = NULL;
+
+		if (errorCode == 0)
+		{
+			record = new RecordRef;
+
+			record->m_impl->m_ref = rrRef;
+		}
+
+		self->m_outer->RegisterRecordDispatch((ServiceFlags) flags, (ErrorCode) errorCode, record);
+	}
+}
+
+
+//
+// ServiceRefImpl::QueryRecordCallback()
+//
+// This is the callback from dnssd.dll.  We pass this up to our outer, managed type
+//
+void DNSSD_API
+ServiceRef::ServiceRefImpl::QueryRecordCallback
+								(
+								DNSServiceRef			DNSServiceRef,
+								DNSServiceFlags			flags,
+								uint32_t				interfaceIndex,
+								DNSServiceErrorType		errorCode,
+								const char			*	fullname,
+								uint16_t				rrtype,
+								uint16_t				rrclass,
+								uint16_t				rdlen,
+								const void			*	rdata,
+								uint32_t				ttl,
+								void				*	context
+								)
+{
+	ServiceRef::ServiceRefImpl * self = static_cast<ServiceRef::ServiceRefImpl*>(context);
+
+	check( self != NULL );
+	check( self->m_outer != NULL );
+	
+	if (self->m_disposed == false)
+	{
+		Byte rdataBytes[];
+
+		if (rdlen)
+		{
+			rdataBytes			=	new Byte[rdlen];
+			Byte __pin * p		=	&rdataBytes[0];
+			memcpy(p, rdata, rdlen);
+		}
+
+		self->m_outer->QueryRecordDispatch((ServiceFlags) flags, (int) interfaceIndex, (ErrorCode) errorCode, ConvertToString(fullname), rrtype, rrclass, rdataBytes, ttl);
+	}
+}
+
+
+/*
+ * EnumerateDomains()
+ *
+ * This maps to DNSServiceEnumerateDomains().  Returns an
+ * initialized ServiceRef on success, throws an exception
+ * on failure.
+ */
+ServiceRef*
+DNSService::EnumerateDomains
+		(
+		int							flags,
+		int							interfaceIndex,
+		EnumerateDomainsReply	*	callback
+		)
+{
+	ServiceRef * sdRef = new ServiceRef(callback);
+	int			 err;
+
+	err = DNSServiceEnumerateDomains(&sdRef->m_impl->m_ref, flags, interfaceIndex, ServiceRef::ServiceRefImpl::EnumerateDomainsCallback, sdRef->m_impl);
+
+	if (err != 0)
+	{
+		throw new DNSServiceException(err);
+	}
+
+	sdRef->StartThread();
+
+	return sdRef;
+}
+
+
+/*
+ * Register()
+ *
+ * This maps to DNSServiceRegister().  Returns an
+ * initialized ServiceRef on success, throws an exception
+ * on failure.
+ */
+ServiceRef*
+DNSService::Register
+				(
+				int					flags,
+				int					interfaceIndex,
+				String			*	name,
+				String			*	regtype,
+				String			*	domain,
+				String			*	host,
+				int					port,
+				Byte				txtRecord[],
+				RegisterReply	*	callback
+				)
+{
+	ServiceRef	*	sdRef	=	new ServiceRef(callback);
+	PString		*	pName	=	new PString(name);
+	PString		*	pType	=	new PString(regtype);
+	PString		*	pDomain =	new PString(domain);
+	PString		*	pHost	=	new PString(host);
+	int				len		=	0;
+	Byte __pin	*	p		=	NULL;
+	void		*	v		=	NULL;
+
+	if ((txtRecord != NULL) && (txtRecord->Length > 0))
+	{
+		len		= txtRecord->Length;
+		p		= &txtRecord[0];
+		v		= (void*) p;
+	}
+
+	int err = DNSServiceRegister(&sdRef->m_impl->m_ref, flags, interfaceIndex, pName->c_str(), pType->c_str(), pDomain->c_str(), pHost->c_str(), htons(port), len, v, ServiceRef::ServiceRefImpl::RegisterCallback, sdRef->m_impl );
+
+	if (err != 0)
+	{
+		throw new DNSServiceException(err);
+	}
+
+	sdRef->StartThread();
+
+	return sdRef;
+}
+
+
+/*
+ * AddRecord()
+ *
+ * This maps to DNSServiceAddRecord().  Returns an
+ * initialized ServiceRef on success, throws an exception
+ * on failure.
+ */
+RecordRef*
+DNSService::AddRecord
+				(
+				ServiceRef	*	sdRef,
+				int				flags,
+				int				rrtype,
+				Byte			rdata[],
+				int				ttl
+				)
+{
+	int				len		=	0;
+	Byte __pin	*	p		=	NULL;
+	void		*	v		=	NULL;
+
+	if ((rdata != NULL) && (rdata->Length > 0))
+	{
+		len = rdata->Length;
+		p	= &rdata[0];
+		v	= (void*) p;
+	}
+
+	RecordRef * record = new RecordRef;
+
+	int err = DNSServiceAddRecord(sdRef->m_impl->m_ref, &record->m_impl->m_ref, flags, rrtype, len, v, ttl);
+
+	if (err != 0)
+	{
+		throw new DNSServiceException(err);
+	}
+
+	return record;
+}
+
+
+/*
+ * UpdateRecord()
+ *
+ * This maps to DNSServiceUpdateRecord().  Returns an
+ * initialized ServiceRef on success, throws an exception
+ * on failure.
+ */
+void
+DNSService::UpdateRecord
+				(
+				ServiceRef	*	sdRef,
+				RecordRef	*	record,
+				int				flags,
+				Byte			rdata[],
+				int				ttl
+				)
+{
+	int				len		=	0;
+	Byte __pin	*	p		=	NULL;
+	void		*	v		=	NULL;
+
+	if ((rdata != NULL) && (rdata->Length > 0))
+	{
+		len	= rdata->Length;
+		p	= &rdata[0];
+		v	= (void*) p;
+	}
+
+	int err = DNSServiceUpdateRecord(sdRef->m_impl->m_ref, record ? record->m_impl->m_ref : NULL, flags, len, v, ttl);
+
+	if (err != 0)
+	{
+		throw new DNSServiceException(err);
+	}
+}
+
+
+/*
+ * RemoveRecord()
+ *
+ * This maps to DNSServiceRemoveRecord().  Returns an
+ * initialized ServiceRef on success, throws an exception
+ * on failure.
+ */
+void
+DNSService::RemoveRecord
+		(
+		ServiceRef	*	sdRef,
+		RecordRef	*	record,
+		int				flags
+		)
+{
+	int err = DNSServiceRemoveRecord(sdRef->m_impl->m_ref, record->m_impl->m_ref, flags);
+
+	if (err != 0)
+	{
+		throw new DNSServiceException(err);
+	}
+}	
+
+
+/*
+ * Browse()
+ *
+ * This maps to DNSServiceBrowse().  Returns an
+ * initialized ServiceRef on success, throws an exception
+ * on failure.
+ */
+ServiceRef*
+DNSService::Browse
+	(
+	int				flags,
+	int				interfaceIndex,
+	String		*	regtype,
+	String		*	domain,
+	BrowseReply	*	callback
+	)
+{
+	ServiceRef	*	sdRef	= new ServiceRef(callback);
+	PString		*	pType	= new PString(regtype);
+	PString		*	pDomain	= new PString(domain);
+
+	int err = DNSServiceBrowse(&sdRef->m_impl->m_ref, flags, interfaceIndex, pType->c_str(), pDomain->c_str(),(DNSServiceBrowseReply) ServiceRef::ServiceRefImpl::BrowseCallback, sdRef->m_impl);
+
+	if (err != 0)
+	{
+		throw new DNSServiceException(err);
+	}
+
+	sdRef->StartThread();
+
+	return sdRef;
+}
+
+
+/*
+ * Resolve()
+ *
+ * This maps to DNSServiceResolve().  Returns an
+ * initialized ServiceRef on success, throws an exception
+ * on failure.
+ */
+ServiceRef*
+DNSService::Resolve
+	(
+	int					flags,
+	int					interfaceIndex,
+	String			*	name,
+	String			*	regtype,
+	String			*	domain,
+	ResolveReply	*	callback	
+	)
+{
+	ServiceRef	*	sdRef	= new ServiceRef(callback);
+	PString		*	pName	= new PString(name);
+	PString		*	pType	= new PString(regtype);
+	PString		*	pDomain	= new PString(domain);
+
+	int err = DNSServiceResolve(&sdRef->m_impl->m_ref, flags, interfaceIndex, pName->c_str(), pType->c_str(), pDomain->c_str(),(DNSServiceResolveReply) ServiceRef::ServiceRefImpl::ResolveCallback, sdRef->m_impl);
+
+	if (err != 0)
+	{
+		throw new DNSServiceException(err);
+	}
+
+	sdRef->StartThread();
+
+	return sdRef;
+}
+
+
+/*
+ * CreateConnection()
+ *
+ * This maps to DNSServiceCreateConnection().  Returns an
+ * initialized ServiceRef on success, throws an exception
+ * on failure.
+ */
+ServiceRef*
+DNSService::CreateConnection
+			(
+			RegisterRecordReply * callback
+			)
+{
+	ServiceRef * sdRef = new ServiceRef(callback);
+
+	int err = DNSServiceCreateConnection(&sdRef->m_impl->m_ref);
+
+	if (err != 0)
+	{
+		throw new DNSServiceException(err);
+	}
+
+	sdRef->StartThread();
+
+	return sdRef;
+}
+
+
+/*
+ * RegisterRecord()
+ *
+ * This maps to DNSServiceRegisterRecord().  Returns an
+ * initialized ServiceRef on success, throws an exception
+ * on failure.
+ */
+
+RecordRef*
+DNSService::RegisterRecord
+			(
+			ServiceRef			*	sdRef,
+			ServiceFlags			flags,
+			int						interfaceIndex,
+			String				*	fullname,
+			int						rrtype,
+			int						rrclass,
+			Byte					rdata[],
+			int						ttl
+			)
+{
+	RecordRef	*	record	= new RecordRef;
+	int				len		= 0;
+	Byte __pin	*	p		= NULL;
+	void		*	v		= NULL;
+
+	PString * pFullname = new PString(fullname);
+
+	if ((rdata != NULL) && (rdata->Length > 0))
+	{
+		len		= rdata->Length;
+		p		= &rdata[0];
+		v		= (void*) p;
+	}
+
+	int err = DNSServiceRegisterRecord(sdRef->m_impl->m_ref, &record->m_impl->m_ref, flags, interfaceIndex, pFullname->c_str(), rrtype, rrclass, len, v, ttl, (DNSServiceRegisterRecordReply) ServiceRef::ServiceRefImpl::RegisterRecordCallback, sdRef->m_impl);
+
+	if (err != 0)
+	{
+		throw new DNSServiceException(err);
+	}
+
+	return record;
+}
+
+/*
+ * QueryRecord()
+ *
+ * This maps to DNSServiceQueryRecord().  Returns an
+ * initialized ServiceRef on success, throws an exception
+ * on failure.
+ */
+ServiceRef*
+DNSService::QueryRecord
+		(
+		ServiceFlags			flags,
+		int						interfaceIndex,
+		String				*	fullname,
+		int						rrtype,
+		int						rrclass,
+		QueryRecordReply	*	callback
+		)
+{
+	ServiceRef	*	sdRef		= new ServiceRef(callback);
+	PString		*	pFullname	= new PString(fullname);
+
+	int err = DNSServiceQueryRecord(&sdRef->m_impl->m_ref, flags, interfaceIndex, pFullname->c_str(), rrtype, rrclass, (DNSServiceQueryRecordReply) ServiceRef::ServiceRefImpl::QueryRecordCallback, sdRef->m_impl);
+
+	if (err != 0)
+	{
+		throw new DNSServiceException(err);
+	}
+
+	sdRef->StartThread();
+
+	return sdRef;
+}
+
+
+/*
+ * ReconfirmRecord()
+ *
+ * This maps to DNSServiceReconfirmRecord().  Returns an
+ * initialized ServiceRef on success, throws an exception
+ * on failure.
+ */
+void
+DNSService::ReconfirmRecord
+		(
+		ServiceFlags	flags,
+		int				interfaceIndex,
+		String		*	fullname,
+		int				rrtype,
+		int				rrclass,
+		Byte			rdata[]
+		)
+{
+	int				len	= 0;
+	Byte __pin	*	p	= NULL;
+	void		*	v	= NULL;
+
+	PString * pFullname = new PString(fullname);
+
+	if ((rdata != NULL) && (rdata->Length > 0))
+	{
+		len	= rdata->Length;
+		p	= &rdata[0];
+		v	= (void*) p;
+	}
+
+	DNSServiceReconfirmRecord(flags, interfaceIndex, pFullname->c_str(), rrtype, rrclass, len, v);
+}
+
+
+void
+TextRecord::SetValue
+		(
+		String	*	key,
+		Byte		value[]            /* may be NULL */
+		)
+{
+	PString			*	pKey = new PString(key);
+	int					len		=	0;
+	Byte __pin		*	p		=	NULL;
+	void			*	v		=	NULL;
+	DNSServiceErrorType	err;
+
+	if (value && (value->Length > 0))
+	{
+		len	=	value->Length;
+		p	=	&value[0];
+		v	=	(void*) p;
+	}
+
+	err = TXTRecordSetValue(&m_impl->m_ref, pKey->c_str(), len, v);
+
+	if (err != 0)
+	{
+		throw new DNSServiceException(err);
+	}
+}
+
+
+void
+TextRecord::RemoveValue
+		(
+		String	*	key
+		)
+{
+	PString			*	pKey = new PString(key);
+	DNSServiceErrorType	err;
+
+	err = TXTRecordRemoveValue(&m_impl->m_ref, pKey->c_str());
+
+	if (err != 0)
+	{
+		throw new DNSServiceException(err);
+	}
+}
+
+
+int
+TextRecord::GetLength
+		(
+		)
+{
+	return TXTRecordGetLength(&m_impl->m_ref);
+}
+
+
+Byte
+TextRecord::GetBytes
+		(
+		) __gc[]
+{
+	const void	*	noGCBytes = NULL;
+	Byte			gcBytes[] = NULL;		
+
+	noGCBytes		=	TXTRecordGetBytesPtr(&m_impl->m_ref);
+	int			len	=	GetLength();
+
+	if (noGCBytes && len)
+	{
+		gcBytes				=	new Byte[len];
+		Byte __pin	*	p	=	&gcBytes[0];
+		memcpy(p, noGCBytes, len);
+	}
+
+	return gcBytes;
+}
+
+
+bool
+TextRecord::ContainsKey
+		(
+		Byte		txtRecord[],
+		String	*	key
+		)
+{
+	PString		*	pKey	= new PString(key);
+	Byte __pin	*	p		= &txtRecord[0];
+	
+	return (TXTRecordContainsKey(txtRecord->Length, p, pKey->c_str()) > 0) ? true : false;
+}
+
+
+Byte
+TextRecord::GetValueBytes
+		(
+		Byte		txtRecord[],
+		String	*	key
+		) __gc[]
+{
+	uint8_t			valueLen;
+	Byte			ret[]	= NULL;
+	PString		*	pKey	= new PString(key);
+	Byte __pin	*	p1		= &txtRecord[0];
+	const void	*	v;
+
+	v = TXTRecordGetValuePtr(txtRecord->Length, p1, pKey->c_str(), &valueLen);
+
+	if (v != NULL)
+	{
+		ret					= new Byte[valueLen];
+		Byte __pin	*	p2	= &ret[0];
+
+		memcpy(p2, v, valueLen);
+	}
+
+	return ret;
+}
+
+
+int
+TextRecord::GetCount
+		(
+		Byte txtRecord[]
+		)
+{
+	Byte __pin	*	p	= &txtRecord[0];
+
+	return TXTRecordGetCount(txtRecord->Length, p);
+}
+
+
+Byte
+TextRecord::GetItemAtIndex
+		(
+		Byte				txtRecord[],
+		int					index,
+		[Out] String	**	key
+		) __gc[]
+{
+	char				keyBuf[255];
+	uint8_t				keyBufLen = 255;
+	uint8_t				valueLen;
+	void			*	value;
+	Byte				ret[]	= NULL;
+	DNSServiceErrorType	err;
+	Byte __pin		*	p1		= &txtRecord[0];
+	
+
+	err = TXTRecordGetItemAtIndex(txtRecord->Length, p1, index, keyBufLen, keyBuf, &valueLen, (const void**) &value);
+
+	if (err != 0)
+	{
+		throw new DNSServiceException(err);
+	}
+
+	*key = ConvertToString(keyBuf);
+
+	if (valueLen)
+	{
+		ret					= new Byte[valueLen];
+		Byte __pin	*	p2	= &ret[0];
+
+		memcpy(p2, value, valueLen);
+	}
+
+	return ret;
+}
+
+
+//
+// DNSServiceException::DNSServiceException()
+//
+// Constructs an exception with an error code
+//
+DNSServiceException::DNSServiceException
+				(
+				int _err
+				)
+:
+	err(_err)
+{
+}
+
+
+//
+// This version of the constructor is useful for instances in which
+// an inner exception is thrown, caught, and then a new exception
+// is thrown in it's place
+//
+DNSServiceException::DNSServiceException
+				(	
+				String				*	message,
+				System::Exception	*	innerException
+				)
+{
+}
diff --git a/mdnsresponder/mDNSWindows/DLL.NET/dnssd_NET.h b/mdnsresponder/mDNSWindows/DLL.NET/dnssd_NET.h
new file mode 100755
index 0000000..3e0196d
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DLL.NET/dnssd_NET.h
@@ -0,0 +1,1392 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+
+ * 
+ * NOTE:
+ * 
+ * These .Net APIs are a work in progress, currently being discussed and refined.
+ * If you plan to build an application based on these APIs, you may wish to
+ * statically link this code into your application, or otherwise distribute
+ * the DLL so that it installs into the same folder as your application
+ * (not into any common system area where it might interfere with other
+ * applications using a future completed version of these APIs).
+ * If you plan to do this, please be sure to inform us by sending email
+ * to bonjour@apple.com to let us know.
+ * You may want to discuss what you're doing on the Bonjour mailing
+ * list to see if others in similar positions have any suggestions for you:
+ * 
+ * <http://lists.apple.com/bonjour-dev/>
+ * 
+ */
+    
+#pragma once
+
+#include <dns_sd.h>
+#include <vcclr.h>
+#include <memory>
+#include <winsock2.h>
+
+using namespace System;
+using namespace System::Net;
+using namespace System::Runtime::InteropServices;
+using namespace System::Threading;
+using namespace System::Collections;
+
+
+namespace Apple
+{
+	namespace DNSSD
+	{
+		public __gc class ServiceRef;
+
+		public __value enum ServiceFlags : int
+		{
+			MoreComing			=	1,
+			/* MoreComing indicates to a callback that at least one more result is
+				* queued and will be delivered following immediately after this one.
+				* Applications should not update their UI to display browse
+				* results when the MoreComing flag is set, because this would
+				* result in a great deal of ugly flickering on the screen.
+				* Applications should instead wait until until MoreComing is not set,
+				* and then update their UI.
+				* When MoreComing is not set, that doesn't mean there will be no more
+				* answers EVER, just that there are no more answers immediately
+				* available right now at this instant. If more answers become available
+				* in the future they will be delivered as usual.
+				*/
+
+			Add					=	2,
+			Default				=	4,
+			/* Flags for domain enumeration and browse/query reply callbacks.
+				* "Default" applies only to enumeration and is only valid in
+				* conjuction with "Add".  An enumeration callback with the "Add"
+				* flag NOT set indicates a "Remove", i.e. the domain is no longer
+				* valid.
+				*/
+
+			NoAutoRename		=	8,
+			/* Flag for specifying renaming behavior on name conflict when registering
+				* non-shared records. By default, name conflicts are automatically handled
+				* by renaming the service.  NoAutoRename overrides this behavior - with this
+				* flag set, name conflicts will result in a callback.  The NoAutorename flag
+				* is only valid if a name is explicitly specified when registering a service
+				* (i.e. the default name is not used.)
+				*/
+
+			Shared				=	16,
+			Unique				=	32,
+			/* Flag for registering individual records on a connected
+				* DNSServiceRef.  Shared indicates that there may be multiple records
+				* with this name on the network (e.g. PTR records).  Unique indicates that
+	the
+				* record's name is to be unique on the network (e.g. SRV records).
+				*/
+
+			BrowseDomains		=	64,
+			RegistrationDomains	=	128,
+			/* Flags for specifying domain enumeration type in DNSServiceEnumerateDomain
+	s.
+				* BrowseDomains enumerates domains recommended for browsing, RegistrationDo
+	mains
+				* enumerates domains recommended for registration.
+				*/
+		};
+
+
+		public __value enum ErrorCode : int
+		{
+			NoError				=	0,
+			Unknown				=	-65537,
+			NoSuchName			=	-65538,
+			NoMemory			=	-65539,
+			BadParam			=	-65540,
+			BadReference		=	-65541,
+			BadState			=	-65542,
+			BadFlags			=	-65543,
+			Unsupported			=	-65544,
+			AlreadyRegistered	=	-65547,
+			NameConflict		=	-65548,
+			Invalid				=	-65549,
+			Incompatible		=	-65551,
+			BadinterfaceIndex	=	-65552
+
+			/*
+				* mDNS Error codes are in the range
+				* FFFE FF00 (-65792) to FFFE FFFF (-65537)
+				*/
+		};
+
+		public __gc class DNSServiceException
+		:
+			public Exception
+		{
+		public:
+
+			DNSServiceException
+				(
+				int err
+				);
+
+			DNSServiceException
+				(	
+				String				*	message,
+				System::Exception	*	innerException
+				);
+
+			int err;
+		};
+
+
+		/*
+		* class RecordRef
+		*
+		* This is a thin MC++ class facade on top of a DNSRecordRef
+		*/
+		public __gc class RecordRef
+		{
+		public:
+
+			RecordRef()
+			{
+				m_impl = new RecordRefImpl;
+				m_impl->m_ref = NULL;
+			}
+
+			~RecordRef()
+			{
+				delete m_impl;
+			}
+
+			__nogc class RecordRefImpl
+			{
+			public:
+
+				DNSRecordRef m_ref;
+			};
+
+			RecordRefImpl * m_impl;
+		};			
+
+
+		/*
+		* class ServiceRef
+		*
+		* This is a thin MC++ class facade on top of a DNSServiceRef
+		*/
+		public __gc class ServiceRef : public IDisposable
+		{
+		public:
+
+			ServiceRef(Object * callback);
+
+			~ServiceRef();
+
+			/*
+			* This does an underlying DNSServiceRefDeallocate().  After
+			* calling Dispose, the ServiceRef is no longer usable.
+			*/
+			void
+			Dispose();
+
+			/*
+			* Internal - Dispatch an EnumerateDomains callback
+			*/
+			void
+			EnumerateDomainsDispatch
+				(
+				ServiceFlags	flags,
+				int				interfaceIndex,
+				ErrorCode		errorCode,
+				String		*	replyDomain
+				);
+
+			/*
+			* Internal - Dispatch a Register callback
+			*/
+			void
+			RegisterDispatch
+				(
+				ServiceFlags	flags,
+				ErrorCode		errorCode,
+ 				String		*	name,
+				String		*	regtype,
+				String		*	domain
+				);
+
+			/*
+			* Internal - Dispatch a Browse callback
+			*/
+			void
+			BrowseDispatch
+				(
+				ServiceFlags	flags,
+				int				interfaceIndex,
+				ErrorCode		errorCode,
+				String		*	serviceName,
+				String		*	regtype,
+				String		*	replyDomain
+				);
+
+			/*
+			* Internal - Dispatch a Resolve callback
+			*/
+			void
+			ResolveDispatch
+				(
+				ServiceFlags	flags,
+				int				interfaceIndex,
+				ErrorCode		errorCode,
+				String		*	fullname,
+				String		*	hosttarget,
+				int				port,
+				Byte			txtRecord[]
+				);
+			
+			/*
+			* Internal - Dispatch a RegisterRecord callback
+			*/
+			void
+			RegisterRecordDispatch
+				(
+				ServiceFlags	flags,
+				ErrorCode		errorCode,
+				RecordRef	*	record
+				);
+
+			/*
+			* Internal - Dispatch a QueryRecord callback
+			*/
+			void
+			QueryRecordDispatch
+				(
+				ServiceFlags	flags,
+				int				interfaceIndex,
+				ErrorCode		errorCode,
+				String		*	fullname,
+				int				rrtype,
+				int				rrclass,
+				Byte			rdata[],
+				int				ttl
+				);
+
+			/*
+			* Internal - A non managed class to wrap a DNSServiceRef
+			*/
+			__nogc class ServiceRefImpl
+			{
+			public:
+
+				ServiceRefImpl
+					(
+					ServiceRef * outer
+					);
+
+				~ServiceRefImpl();
+
+				/*
+				* Sets up events for threaded operation
+				*/
+				void
+				SetupEvents();
+
+				/*
+				* Main processing thread
+				*/
+				void
+				ProcessingThread();
+
+				/*
+				* Calls DNSServiceRefDeallocate()
+				*/
+				void
+				Dispose();
+
+				/*
+				* Called from dnssd.dll
+				*/
+				static void DNSSD_API
+				EnumerateDomainsCallback
+					(
+					DNSServiceRef			sdRef,
+					DNSServiceFlags			flags,
+					uint32_t				interfaceIndex,
+					DNSServiceErrorType		errorCode,
+					const char			*	replyDomain,
+					void				*	context
+					);
+
+				static void DNSSD_API
+				RegisterCallback
+					(
+					DNSServiceRef			ref,
+					DNSServiceFlags			flags,
+					DNSServiceErrorType		errorCode,
+ 					const char			*	name,
+					const char			*	regtype,
+					const char			*	domain,
+					void				*	context
+					);
+
+				static void DNSSD_API
+				BrowseCallback
+					(
+					DNSServiceRef			sdRef,
+   					DNSServiceFlags			flags,
+					uint32_t				interfaceIndex,
+					DNSServiceErrorType		errorCode,
+					const char			*	serviceName,
+					const char			*	regtype,
+					const char			*	replyDomain,
+					void				*	context
+					);
+
+				static void DNSSD_API
+				ResolveCallback
+					(
+					DNSServiceRef			sdRef,
+					DNSServiceFlags			flags,
+					uint32_t				interfaceIndex,
+					DNSServiceErrorType		errorCode,
+					const char			*	fullname,
+					const char			*	hosttarget,
+					uint16_t				notAnIntPort,
+					uint16_t				txtLen,
+					const char			*	txtRecord,
+					void				*	context
+					);
+
+				static void DNSSD_API
+				RegisterRecordCallback
+					( 
+					DNSServiceRef		sdRef,
+					DNSRecordRef		RecordRef,
+					DNSServiceFlags		flags,
+					DNSServiceErrorType	errorCode,
+					void			*	context
+					);
+
+				static void DNSSD_API
+				QueryRecordCallback
+					(
+					DNSServiceRef			DNSServiceRef,
+					DNSServiceFlags			flags,
+					uint32_t				interfaceIndex,
+					DNSServiceErrorType		errorCode,
+					const char			*	fullname,
+					uint16_t				rrtype,
+					uint16_t				rrclass,
+					uint16_t				rdlen,
+					const void			*	rdata,
+					uint32_t				ttl,
+					void				*	context
+					);
+
+				SOCKET				m_socket;
+				HANDLE				m_socketEvent;
+				HANDLE				m_stopEvent;
+				DWORD				m_threadId;
+				bool				m_disposed;
+				DNSServiceRef		m_ref;
+				gcroot<ServiceRef*> m_outer;
+			};
+
+			void
+			StartThread();
+
+			void
+			ProcessingThread();
+
+			bool				m_bDisposed;
+			Object			*	m_callback;
+			Thread			*	m_thread;
+			ServiceRefImpl	*	m_impl;
+		};
+			
+		/*********************************************************************************************
+		*
+		*   TXT Record Construction Functions
+		*
+		*********************************************************************************************/
+
+		/*
+		* A typical calling sequence for TXT record construction is something like:
+		*
+		* DNSService.TextRecord tr = new DNSService.TextRecord(1024);
+		* tr.SetValue();
+		* tr.SetValue();
+		* tr.SetValue();
+		* ...
+		* DNSServiceRegister( ... tr.GetLength(), tr.GetBytes() ... );
+		*/
+
+
+		/* TextRecord
+		*
+		* Opaque internal data type.
+		* Note: Represents a DNS-SD TXT record.
+		*/
+
+
+		/* TextRecord::TextRecord()
+		*
+		* Creates a new empty TextRecord .
+		*
+		*/
+
+		public __gc class TextRecord
+		{
+		public:
+
+			TextRecord()
+			{
+				m_impl = new TextRecordImpl();
+				TXTRecordCreate(&m_impl->m_ref, 0, NULL);
+			}
+
+			~TextRecord()
+			{
+				TXTRecordDeallocate(&m_impl->m_ref);
+				delete m_impl;
+			}
+
+			__nogc class TextRecordImpl
+			{
+			public:
+
+				TXTRecordRef m_ref;
+			};
+
+			TextRecordImpl * m_impl;
+
+
+			/* SetValue()
+			*
+			* Adds a key (optionally with value) to a TextRecord. If the "key" already
+			* exists in the TextRecord, then the current value will be replaced with
+			* the new value.
+			* Keys may exist in four states with respect to a given TXT record:
+			*  - Absent (key does not appear at all)
+			*  - Present with no value ("key" appears alone)
+			*  - Present with empty value ("key=" appears in TXT record)
+			*  - Present with non-empty value ("key=value" appears in TXT record)
+			* For more details refer to "Data Syntax for DNS-SD TXT Records" in
+			* <http://files.dns-sd.org/draft-cheshire-dnsext-dns-sd.txt>
+			*
+			* key:             A null-terminated string which only contains printable ASCII
+			*                  values (0x20-0x7E), excluding '=' (0x3D). Keys should be
+			*                  14 characters or less (not counting the terminating null).
+			*
+			* value:           Any binary value. For values that represent
+			*                  textual data, UTF-8 is STRONGLY recommended.
+			*                  For values that represent textual data, valueSize
+			*                  should NOT include the terminating null (if any)
+			*                  at the end of the string.
+			*                  If NULL, then "key" will be added with no value.
+			*                  If non-NULL but valueSize is zero, then "key=" will be
+			*                  added with empty value.
+			*
+			* exceptions:      Throws kDNSServiceErr_Invalid if the "key" string contains
+			*                  illegal characters.
+			*                  Throws kDNSServiceErr_NoMemory if adding this key would
+			*                  exceed the available storage.
+			*/
+
+			void
+			SetValue
+				(
+				String	*	key,
+				Byte		value[]  /* may be NULL */
+				);
+
+
+			/* RemoveValue()
+			*
+			* Removes a key from a TextRecord.  The "key" must be an
+			* ASCII string which exists in the TextRecord.
+			*
+			* key:             A key name which exists in the TextRecord.
+			*
+			* exceptions:      Throws kDNSServiceErr_NoSuchKey if the "key" does not
+			*                  exist in the TextRecord.
+			*
+			*/
+
+			void
+			RemoveValue
+				(
+				String	*	key
+				);
+
+
+			/* GetLength()
+			*
+			* Allows you to determine the length of the raw bytes within a TextRecord.
+			*
+			* return value :     Returns the size of the raw bytes inside a TextRecord
+			*                  which you can pass directly to DNSServiceRegister() or
+			*                  to DNSServiceUpdateRecord().
+			*                  Returns 0 if the TextRecord is empty.
+			*
+			*/
+
+			int
+			GetLength
+				(
+				);
+
+
+			/* GetBytes()
+			*
+			* Allows you to retrieve a pointer to the raw bytes within a TextRecord.
+			*
+			* return value:    Returns a pointer to the bytes inside the TextRecord
+			*                  which you can pass directly to DNSServiceRegister() or
+			*                  to DNSServiceUpdateRecord().
+			*
+			*/
+
+			Byte
+			GetBytes
+				(
+				) __gc[];
+
+
+			/*********************************************************************************************
+			*
+			*   TXT Record Parsing Functions
+			*
+			*********************************************************************************************/
+
+			/*
+			* A typical calling sequence for TXT record parsing is something like:
+			*
+			* Receive TXT record data in DNSServiceResolve() callback
+			* if (TXTRecordContainsKey(txtLen, txtRecord, "key")) then do something
+			* val1ptr = DNSService.TextService.GetValue(txtRecord, "key1", &len1);
+			* val2ptr = DNSService.TextService.GetValue(txtRecord, "key2", &len2);
+			* ...
+			* return;
+			*
+			*/
+
+			/* ContainsKey()
+			*
+			* Allows you to determine if a given TXT Record contains a specified key.
+			*
+			* txtRecord:       Pointer to the received TXT Record bytes.
+			*
+			* key:             A null-terminated ASCII string containing the key name.
+			*
+			* return value:    Returns 1 if the TXT Record contains the specified key.
+			*                  Otherwise, it returns 0.
+			*
+			*/
+
+			static public bool
+			ContainsKey
+				(
+				Byte		txtRecord[],
+				String	*	key
+				);
+
+
+			/* GetValueBytes()
+			*
+			* Allows you to retrieve the value for a given key from a TXT Record.
+			*
+			* txtRecord:       Pointer to the received TXT Record bytes.
+			*
+			* key:             A null-terminated ASCII string containing the key name.
+			*
+			* return value:    Returns NULL if the key does not exist in this TXT record,
+			*                  or exists with no value (to differentiate between
+			*                  these two cases use ContainsKey()).
+			*                  Returns byte array 
+			*                  if the key exists with empty or non-empty value.
+			*                  For empty value, length of byte array will be zero.
+			*                  For non-empty value, it will be the length of value data.
+			*/
+
+			static public Byte
+			GetValueBytes
+				(
+				Byte		txtRecord[],
+				String	*	key
+				) __gc[];
+
+
+			/* GetCount()
+			*
+			* Returns the number of keys stored in the TXT Record.  The count
+			* can be used with TXTRecordGetItemAtIndex() to iterate through the keys.
+			*
+			* txtRecord:       Pointer to the received TXT Record bytes.
+			*
+			* return value:    Returns the total number of keys in the TXT Record.
+			*
+			*/
+
+			static public int
+			GetCount
+				(
+				Byte	txtRecord[]
+				);
+
+
+			/* GetItemAtIndex()
+			*
+			* Allows you to retrieve a key name and value pointer, given an index into
+			* a TXT Record.  Legal index values range from zero to TXTRecordGetCount()-1.
+			* It's also possible to iterate through keys in a TXT record by simply
+			* calling TXTRecordGetItemAtIndex() repeatedly, beginning with index zero
+			* and increasing until TXTRecordGetItemAtIndex() returns kDNSServiceErr_Invalid.
+			*
+			* On return:
+			* For keys with no value, *value is set to NULL and *valueLen is zero.
+			* For keys with empty value, *value is non-NULL and *valueLen is zero.
+			* For keys with non-empty value, *value is non-NULL and *valueLen is non-zero.
+			*
+			* txtRecord:       Pointer to the received TXT Record bytes.
+			*
+			* index:           An index into the TXT Record.
+			*
+			* key:             A string buffer used to store the key name.
+			*                  On return, the buffer contains a string
+			*                  giving the key name. DNS-SD TXT keys are usually
+			*                  14 characters or less. 
+			*
+			* return value:    Record bytes that holds the value data.
+			*
+			* exceptions:      Throws kDNSServiceErr_Invalid if index is greater than
+			*                  GetCount()-1.
+			*/
+
+			static public Byte
+			GetItemAtIndex
+				(
+				Byte				txtRecord[],
+				int					index,
+				[Out] String	**	key
+				) __gc[];
+		};
+
+
+		public __abstract __gc class DNSService
+		{
+		public:
+
+			/*********************************************************************************************
+			*
+			* Domain Enumeration
+			*
+			*********************************************************************************************/
+
+			/* DNSServiceEnumerateDomains()
+			*
+			* Asynchronously enumerate domains available for browsing and registration.
+			* Currently, the only domain returned is "local.", but other domains will be returned in future.
+			*
+			* The enumeration MUST be cancelled via DNSServiceRefDeallocate() when no more domains
+			* are to be found.
+			*
+			*
+			* EnumerateDomainsReply Delegate
+			*
+			* This Delegate is invoked upon a reply from an EnumerateDomains call.
+			*
+			* sdRef:           The DNSServiceRef initialized by DNSServiceEnumerateDomains().
+			*
+			* flags:           Possible values are:
+			*                  MoreComing
+			*                  Add
+			*                  Default
+			*
+			* interfaceIndex:  Specifies the interface on which the domain exists.  (The index for a given
+			*                  interface is determined via the if_nametoindex() family of calls.)
+			*
+			* errorCode:       Will be NoError (0) on success, otherwise indicates
+			*                  the failure that occurred (other parameters are undefined if errorCode is nonzero).
+			*
+			* replyDomain:     The name of the domain.
+			*
+			*/
+
+			__delegate void
+			EnumerateDomainsReply
+				(
+				ServiceRef	*	sdRef,
+				ServiceFlags	flags,
+				int				interfaceIndex,
+				ErrorCode		errorCode,
+				String		*	replyDomain
+				);
+
+			/* DNSServiceEnumerateDomains() Parameters:
+			*
+			*
+			* flags:           Possible values are:
+			*                  BrowseDomains to enumerate domains recommended for browsing.
+			*                  RegistrationDomains to enumerate domains recommended
+			*                  for registration.
+			*
+			* interfaceIndex:  If non-zero, specifies the interface on which to look for domains.
+			*                  (the index for a given interface is determined via the if_nametoindex()
+			*                  family of calls.)  Most applications will pass 0 to enumerate domains on
+			*                  all interfaces.
+			*
+			* callback:        The delegate to be called when a domain is found or the call asynchronously
+			*                  fails.
+			*
+			*
+			* return value:    Returns initialize ServiceRef on succeses (any subsequent, asynchronous
+			*                  errors are delivered to the delegate), otherwise throws an exception indicating
+			*                  the error that occurred (the callback is not invoked and the ServiceRef
+			*                  is not initialized.)
+			*/
+
+			static public ServiceRef*
+			EnumerateDomains
+				(
+				int							flags,
+				int							interfaceIndex,
+				EnumerateDomainsReply	*	callback
+				);
+
+			/*********************************************************************************************
+			*
+			*  Service Registration
+			*
+			*********************************************************************************************/
+
+			/* Register a service that is discovered via Browse() and Resolve() calls.
+			* 
+			* RegisterReply() Callback Parameters:
+			*
+			* sdRef:           The ServiceRef initialized by Register().
+			*
+			* flags:           Currently unused, reserved for future use.
+			*
+			* errorCode:       Will be NoError on success, otherwise will
+			*                  indicate the failure that occurred (including name conflicts, if the
+			*                  NoAutoRename flag was passed to the
+			*                  callout.)  Other parameters are undefined if errorCode is nonzero.
+			*
+			* name:            The service name registered (if the application did not specify a name in
+			*                  DNSServiceRegister(), this indicates what name was automatically chosen).
+			*
+			* regtype:         The type of service registered, as it was passed to the callout.
+			*
+			* domain:          The domain on which the service was registered (if the application did not
+			*                  specify a domain in Register(), this indicates the default domain
+			*                  on which the service was registered).
+			*
+			*/
+
+			__delegate void
+			RegisterReply
+				(
+				ServiceRef	*	sdRef,
+				ServiceFlags	flags,
+				ErrorCode		errorCode,
+				String		*	name,
+				String		*	regtype,
+				String		*	domain
+				);
+
+			/* Register()  Parameters:
+			*
+			* flags:           Indicates the renaming behavior on name conflict (most applications
+			*                  will pass 0).  See flag definitions above for details.
+			*
+			* interfaceIndex:  If non-zero, specifies the interface on which to register the service
+			*                  (the index for a given interface is determined via the if_nametoindex()
+			*                  family of calls.)  Most applications will pass 0 to register on all
+			*                  available interfaces.  Pass -1 to register a service only on the local
+			*                  machine (service will not be visible to remote hosts.)
+			*
+			* name:            If non-NULL, specifies the service name to be registered.
+			*                  Most applications will not specify a name, in which case the
+			*                  computer name is used (this name is communicated to the client via
+			*                  the callback).
+			*
+			* regtype:         The service type followed by the protocol, separated by a dot
+			*                  (e.g. "_ftp._tcp").  The transport protocol must be "_tcp" or "_udp".
+			*                  New service types should be registered at htp://www.dns-sd.org/ServiceTypes.html.
+			*
+			* domain:          If non-NULL, specifies the domain on which to advertise the service.
+			*                  Most applications will not specify a domain, instead automatically
+			*                  registering in the default domain(s).
+			*
+			* host:            If non-NULL, specifies the SRV target host name.  Most applications
+			*                  will not specify a host, instead automatically using the machine's
+			*                  default host name(s).  Note that specifying a non-NULL host does NOT
+			*                  create an address record for that host - the application is responsible
+			*                  for ensuring that the appropriate address record exists, or creating it
+			*                  via DNSServiceRegisterRecord().
+			*
+			* port:            The port, in host byte order, on which the service accepts connections.
+			*                  Pass 0 for a "placeholder" service (i.e. a service that will not be discovered
+			*                  by browsing, but will cause a name conflict if another client tries to
+			*                  register that same name).  Most clients will not use placeholder services.
+			*
+			* txtRecord:       The txt record rdata.  May be NULL.  Note that a non-NULL txtRecord
+			*                  MUST be a properly formatted DNS TXT record, i.e. <length byte> <data>
+			*                  <length byte> <data> ...
+			*
+			* callback:        The delegate to be called when the registration completes or asynchronously
+			*                  fails.  The client MAY pass NULL for the callback -  The client will NOT be notified
+			*                  of the default values picked on its behalf, and the client will NOT be notified of any
+			*                  asynchronous errors (e.g. out of memory errors, etc.) that may prevent the registration
+			*                  of the service.  The client may NOT pass the NoAutoRename flag if the callback is NULL.
+			*                  The client may still deregister the service at any time via DNSServiceRefDeallocate().
+			*
+			* return value:    Returns initialize ServiceRef (any subsequent, asynchronous
+			*                  errors are delivered to the callback), otherwise throws an exception indicating
+			*                  the error that occurred (the callback is never invoked and the DNSServiceRef
+			*                  is not initialized.)
+			*
+			*/
+			static public ServiceRef*
+			Register
+				(
+				int					flags,
+				int					interfaceIndex,
+				String			*	name,
+				String			*	regtype,
+				String			*	domain,
+				String			*	host,
+				int					port,
+				Byte				txtRecord[],
+				RegisterReply	*	callback
+				);
+
+			/* AddRecord()
+			*
+			* Add a record to a registered service.  The name of the record will be the same as the
+			* registered service's name.
+			* The record can later be updated or deregistered by passing the RecordRef initialized
+			* by this function to UpdateRecord() or RemoveRecord().
+			*
+			*
+			* Parameters;
+			*
+			* sdRef:           A ServiceRef initialized by Register().
+			*
+			* RecordRef:       A pointer to an uninitialized RecordRef.  Upon succesfull completion of this
+			*                  call, this ref may be passed to UpdateRecord() or RemoveRecord().
+			*                  If the above ServiceRef is disposed, RecordRef is also
+			*                  invalidated and may not be used further.
+			*
+			* flags:           Currently ignored, reserved for future use.
+			*
+			* rrtype:          The type of the record (e.g. TXT, SRV, etc), as defined in nameser.h.
+			*
+			* rdata:           The raw rdata to be contained in the added resource record.
+			*
+			* ttl:             The time to live of the resource record, in seconds.
+			*
+			* return value:    Returns initialized RecordRef, otherwise throws
+			*                  an exception indicating the error that occurred (the RecordRef is not initialized).
+			*/
+
+			static public RecordRef*
+			AddRecord
+				(
+				ServiceRef	*	sref,
+				int				flags,
+				int				rrtype,
+				Byte			rdata[],
+				int				ttl
+				);
+
+			/* UpdateRecord
+			*
+			* Update a registered resource record.  The record must either be:
+			*   - The primary txt record of a service registered via Register()
+			*   - A record added to a registered service via AddRecord()
+			*   - An individual record registered by RegisterRecord()
+			*
+			*
+			* Parameters:
+			*
+			* sdRef:           A ServiceRef that was initialized by Register()
+			*                  or CreateConnection().
+			*
+			* RecordRef:       A RecordRef initialized by AddRecord, or NULL to update the
+			*                  service's primary txt record.
+			*
+			* flags:           Currently ignored, reserved for future use.
+			*
+			* rdata:           The new rdata to be contained in the updated resource record.
+			*
+			* ttl:             The time to live of the updated resource record, in seconds.
+			*
+			* return value:    No return value on success, otherwise throws an exception
+			*                  indicating the error that occurred.
+			*/
+			static public void
+			UpdateRecord
+				(
+				ServiceRef	*	sref,
+				RecordRef	*	record,
+				int				flags,
+				Byte			rdata[],
+				int				ttl
+				);
+
+			/* RemoveRecord
+			*
+			* Remove a record previously added to a service record set via AddRecord(), or deregister
+			* an record registered individually via RegisterRecord().
+			*
+			* Parameters:
+			*
+			* sdRef:           A ServiceRef initialized by Register() (if the
+			*                  record being removed was registered via AddRecord()) or by
+			*                  CreateConnection() (if the record being removed was registered via
+			*                  RegisterRecord()).
+			*
+			* recordRef:       A RecordRef initialized by a successful call to AddRecord()
+			*                  or RegisterRecord().
+			*
+			* flags:           Currently ignored, reserved for future use.
+			*
+			* return value:    Nothing on success, otherwise throws an
+			*                  exception indicating the error that occurred.
+			*/
+
+			static public void
+			RemoveRecord
+							(
+							ServiceRef	*	sref,
+							RecordRef	*	record,	
+							int				flags
+							);
+
+			/*********************************************************************************************
+			*
+			*  Service Discovery
+			*
+			*********************************************************************************************/
+
+			/* Browse for instances of a service.
+			*
+			*
+			* BrowseReply() Parameters:
+			*
+			* sdRef:           The DNSServiceRef initialized by Browse().
+			*
+			* flags:           Possible values are MoreComing and Add.
+			*                  See flag definitions for details.
+			*
+			* interfaceIndex:  The interface on which the service is advertised.  This index should
+			*                  be passed to Resolve() when resolving the service.
+			*
+			* errorCode:       Will be NoError (0) on success, otherwise will
+			*                  indicate the failure that occurred.  Other parameters are undefined if
+			*                  the errorCode is nonzero.
+			*
+			* serviceName:     The service name discovered.
+			*
+			* regtype:         The service type, as passed in to Browse().
+			* 
+			* domain:          The domain on which the service was discovered (if the application did not
+			*                  specify a domain in Browse(), this indicates the domain on which the
+			*                  service was discovered.)
+			*
+			*/
+
+			__delegate void
+			BrowseReply
+				(
+				ServiceRef	*	sdRef,
+				ServiceFlags	flags,
+				int				interfaceIndex,
+				ErrorCode		errorCode,
+				String		*	name,
+				String		*	type,
+				String		*	domain
+				);
+
+			/* DNSServiceBrowse() Parameters:
+			*
+			* sdRef:           A pointer to an uninitialized ServiceRef.  Call ServiceRef.Dispose()
+			*                  to terminate the browse.
+			*
+			* flags:           Currently ignored, reserved for future use.
+			*
+			* interfaceIndex:  If non-zero, specifies the interface on which to browse for services
+			*                  (the index for a given interface is determined via the if_nametoindex()
+			*                  family of calls.)  Most applications will pass 0 to browse on all available
+			*                  interfaces.  Pass -1 to only browse for services provided on the local host.
+			*
+			* regtype:         The service type being browsed for followed by the protocol, separated by a
+			*                  dot (e.g. "_ftp._tcp").  The transport protocol must be "_tcp" or "_udp".
+			*
+			* domain:          If non-NULL, specifies the domain on which to browse for services.
+			*                  Most applications will not specify a domain, instead browsing on the
+			*                  default domain(s).
+			*
+			* callback:        The delegate to be called when an instance of the service being browsed for
+			*                  is found, or if the call asynchronously fails.
+			*
+			* return value:    Returns initialized ServiceRef on succeses (any subsequent, asynchronous
+			*                  errors are delivered to the callback), otherwise throws an exception indicating
+			*                  the error that occurred (the callback is not invoked and the ServiceRef
+			*                  is not initialized.)
+			*/
+
+			static public ServiceRef*
+			Browse
+				(
+				int				flags,
+				int				interfaceIndex,
+				String		*	regtype,
+				String		*	domain,
+				BrowseReply	*	callback
+				);
+
+			/* ResolveReply() Parameters:
+			*
+			* Resolve a service name discovered via Browse() to a target host name, port number, and
+			* txt record.
+			*
+			* Note: Applications should NOT use Resolve() solely for txt record monitoring - use
+			* QueryRecord() instead, as it is more efficient for this task.
+			*
+			* Note: When the desired results have been returned, the client MUST terminate the resolve by calling
+			* ServiceRef.Dispose().
+			*
+			* Note: Resolve() behaves correctly for typical services that have a single SRV record and
+			* a single TXT record (the TXT record may be empty.)  To resolve non-standard services with multiple
+			* SRV or TXT records, QueryRecord() should be used.
+			*
+			* ResolveReply Callback Parameters:
+			*
+			* sdRef:           The DNSServiceRef initialized by Resolve().
+			*
+			* flags:           Currently unused, reserved for future use.
+			*
+			* interfaceIndex:  The interface on which the service was resolved.
+			*
+			* errorCode:       Will be NoError (0) on success, otherwise will
+			*                  indicate the failure that occurred.  Other parameters are undefined if
+			*                  the errorCode is nonzero.
+			*
+			* fullname:        The full service domain name, in the form <servicename>.<protocol>.<domain>.
+			*                  (Any literal dots (".") are escaped with a backslash ("\."), and literal
+			*                  backslashes are escaped with a second backslash ("\\"), e.g. a web server
+			*                  named "Dr. Pepper" would have the fullname  "Dr\.\032Pepper._http._tcp.local.").
+			*                  This is the appropriate format to pass to standard system DNS APIs such as
+			*                  res_query(), or to the special-purpose functions included in this API that
+			*                  take fullname parameters.
+			*
+			* hosttarget:      The target hostname of the machine providing the service.  This name can
+			*                  be passed to functions like gethostbyname() to identify the host's IP address.
+			*
+			* port:            The port, in host byte order, on which connections are accepted for this service.
+			*
+			* txtRecord:       The service's primary txt record, in standard txt record format.
+			*
+			*/
+
+			__delegate void
+			ResolveReply
+				(	
+				ServiceRef	*	sdRef,  
+				ServiceFlags	flags,
+				int				interfaceIndex,
+				ErrorCode		errorCode,
+				String		*	fullName,
+				String		*	hostName,
+				int				port,
+				Byte			txtRecord[]
+				);
+
+			/* Resolve() Parameters
+			*
+			* flags:           Currently ignored, reserved for future use.
+			*
+			* interfaceIndex:  The interface on which to resolve the service.  The client should
+			*                  pass the interface on which the servicename was discovered, i.e.
+			*                  the interfaceIndex passed to the DNSServiceBrowseReply callback,
+			*                  or 0 to resolve the named service on all available interfaces.
+			*
+			* name:            The servicename to be resolved.
+			*
+			* regtype:         The service type being resolved followed by the protocol, separated by a
+			*                  dot (e.g. "_ftp._tcp").  The transport protocol must be "_tcp" or "_udp".
+			*
+			* domain:          The domain on which the service is registered, i.e. the domain passed
+			*                  to the DNSServiceBrowseReply callback.
+			*
+			* callback:        The delegate to be called when a result is found, or if the call
+			*                  asynchronously fails.
+			*
+			*
+			* return value:    Returns initialized ServiceRef on succeses (any subsequent, asynchronous
+			*                  errors are delivered to the callback), otherwise throws an exception indicating
+			*                  the error that occurred (the callback is never invoked and the DNSServiceRef
+			*                  is not initialized.)
+			*/
+
+			static public ServiceRef*
+			Resolve
+				(
+				int					flags,
+				int					interfaceIndex,
+				String			*	name,
+				String			*	regtype,
+				String			*	domain,
+				ResolveReply	*	callback
+				);
+
+			/*********************************************************************************************
+			*
+			*  Special Purpose Calls (most applications will not use these)
+			*
+			*********************************************************************************************/
+
+			/* CreateConnection/RegisterRecord
+			*
+			* Register an individual resource record on a connected ServiceRef.
+			*
+			* Note that name conflicts occurring for records registered via this call must be handled
+			* by the client in the callback.
+			*
+			*
+			* RecordReply() parameters:
+			*
+			* sdRef:           The connected ServiceRef initialized by
+			*                  CreateConnection().
+			*
+			* RecordRef:       The RecordRef initialized by RegisterRecord().  If the above
+			*                  ServiceRef.Dispose is called, this RecordRef is
+			*                  invalidated, and may not be used further.
+			*
+			* flags:           Currently unused, reserved for future use.
+			*
+			* errorCode:       Will be NoError on success, otherwise will
+			*                  indicate the failure that occurred (including name conflicts.)
+			*                  Other parameters are undefined if errorCode is nonzero.
+			*
+			*/
+
+			__delegate void
+			RegisterRecordReply
+				(
+				ServiceRef	*	sdRef,
+				ServiceFlags	flags,
+				ErrorCode		errorCode,
+				RecordRef	*	record
+				);
+
+			/* CreateConnection()
+			*
+			* Create a connection to the daemon allowing efficient registration of
+			* multiple individual records.
+			*
+			*
+			* Parameters:
+			*
+			* callback:        The delegate to be called when a result is found, or if the call
+			*                  asynchronously fails (e.g. because of a name conflict.)
+			*
+			* return value:    Returns initialize ServiceRef on success, otherwise throws
+			*                  an exception indicating the specific failure that occurred (in which
+			*                  case the ServiceRef is not initialized).
+			*/
+
+			static public ServiceRef*
+			CreateConnection
+				(
+				RegisterRecordReply * callback
+				);
+
+
+			/* RegisterRecord() Parameters:
+			*
+			* sdRef:           A ServiceRef initialized by CreateConnection().
+			*
+			* RecordRef:       A pointer to an uninitialized RecordRef.  Upon succesfull completion of this
+			*                  call, this ref may be passed to UpdateRecord() or RemoveRecord().
+			*                  (To deregister ALL records registered on a single connected ServiceRef
+			*                  and deallocate each of their corresponding RecordRefs, call
+			*                  ServiceRef.Dispose()).
+			*
+			* flags:           Possible values are Shared or Unique
+			*                  (see flag type definitions for details).
+			*
+			* interfaceIndex:  If non-zero, specifies the interface on which to register the record
+			*                  (the index for a given interface is determined via the if_nametoindex()
+			*                  family of calls.)  Passing 0 causes the record to be registered on all interfaces.
+			*                  Passing -1 causes the record to only be visible on the local host.
+			*
+			* fullname:        The full domain name of the resource record.
+			*
+			* rrtype:          The numerical type of the resource record (e.g. PTR, SRV, etc), as defined
+			*                  in nameser.h.
+			*
+			* rrclass:         The class of the resource record, as defined in nameser.h (usually 1 for the
+			*                  Internet class).
+			*
+			* rdata:           A pointer to the raw rdata, as it is to appear in the DNS record.
+			*
+			* ttl:             The time to live of the resource record, in seconds.
+			*
+			*
+			* return value:    Returns initialize RecordRef on succeses (any subsequent, asynchronous
+			*                  errors are delivered to the callback), otherwise throws an exception indicating
+			*                  the error that occurred (the callback is never invoked and the RecordRef is
+			*                  not initialized.)
+			*/
+			static public RecordRef*
+			RegisterRecord
+				(
+				ServiceRef			*	sdRef,
+				ServiceFlags			flags,
+				int						interfaceIndex,
+				String				*	fullname,
+				int						rrtype,
+				int						rrclass,
+				Byte					rdata[],
+				int						ttl
+				);
+
+
+			/* DNSServiceQueryRecord
+			*
+			* Query for an arbitrary DNS record.
+			*
+			*
+			* QueryRecordReply() Delegate Parameters:
+			*
+			* sdRef:           The ServiceRef initialized by QueryRecord().
+			*
+			* flags:           Possible values are MoreComing and
+			*                  Add.  The Add flag is NOT set for PTR records
+			*                  with a ttl of 0, i.e. "Remove" events.
+			*
+			* interfaceIndex:  The interface on which the query was resolved (the index for a given
+			*                  interface is determined via the if_nametoindex() family of calls).
+			*
+			* errorCode:       Will be kDNSServiceErr_NoError on success, otherwise will
+			*                  indicate the failure that occurred.  Other parameters are undefined if
+			*                  errorCode is nonzero.
+			*
+			* fullname:        The resource record's full domain name.
+			*
+			* rrtype:          The resource record's type (e.g. PTR, SRV, etc) as defined in nameser.h.
+			*
+			* rrclass:         The class of the resource record, as defined in nameser.h (usually 1).
+			*
+			* rdata:           The raw rdata of the resource record.
+			*
+			* ttl:             The resource record's time to live, in seconds.
+			*
+			*/
+
+			__delegate void
+			QueryRecordReply
+				(
+				ServiceRef	*	sdRef,
+				ServiceFlags	flags,
+				int				interfaceIndex,
+				ErrorCode		errorCode,	
+				String		*	fullName,
+				int				rrtype,
+				int				rrclass,
+				Byte			rdata[],
+				int				ttl
+				);
+
+			/* QueryRecord() Parameters:
+			*
+			* flags:           Pass LongLivedQuery to create a "long-lived" unicast
+			*                  query in a non-local domain.  Without setting this flag, unicast queries
+			*                  will be one-shot - that is, only answers available at the time of the call
+			*                  will be returned.  By setting this flag, answers (including Add and Remove
+			*                  events) that become available after the initial call is made will generate
+			*                  callbacks.  This flag has no effect on link-local multicast queries.
+			*
+			* interfaceIndex:  If non-zero, specifies the interface on which to issue the query
+			*                  (the index for a given interface is determined via the if_nametoindex()
+			*                  family of calls.)  Passing 0 causes the name to be queried for on all
+			*                  interfaces.  Passing -1 causes the name to be queried for only on the
+			*                  local host.
+			*
+			* fullname:        The full domain name of the resource record to be queried for.
+			*
+			* rrtype:          The numerical type of the resource record to be queried for (e.g. PTR, SRV, etc)
+			*                  as defined in nameser.h.
+			*
+			* rrclass:         The class of the resource record, as defined in nameser.h
+			*                  (usually 1 for the Internet class).
+			*
+			* callback:        The delegate to be called when a result is found, or if the call
+			*                  asynchronously fails.
+			*
+			*
+			* return value:    Returns initialized ServiceRef on succeses (any subsequent, asynchronous
+			*                  errors are delivered to the callback), otherwise throws an exception indicating
+			*                  the error that occurred (the callback is never invoked and the ServiceRef
+			*                  is not initialized.)
+			*/
+
+			static public ServiceRef*
+			QueryRecord
+				(
+				ServiceFlags			flags,
+				int						interfaceIndex,
+				String				*	fullname,
+				int						rrtype,
+				int						rrclass,
+				QueryRecordReply	*	callback
+				);
+
+			/* ReconfirmRecord
+			*
+			* Instruct the daemon to verify the validity of a resource record that appears to
+			* be out of date (e.g. because tcp connection to a service's target failed.)
+			* Causes the record to be flushed from the daemon's cache (as well as all other
+			* daemons' caches on the network) if the record is determined to be invalid.
+			*
+			* Parameters:
+			*
+			* flags:           Currently unused, reserved for future use.
+			*
+			* fullname:        The resource record's full domain name.
+			*
+			* rrtype:          The resource record's type (e.g. PTR, SRV, etc) as defined in nameser.h.
+			*
+			* rrclass:         The class of the resource record, as defined in nameser.h (usually 1).
+			*
+			* rdata:           The raw rdata of the resource record.
+			*
+			*/
+			static public void
+			ReconfirmRecord
+				(
+				ServiceFlags	flags,
+				int				interfaceIndex,
+				String		*	fullname,
+				int				rrtype,
+				int				rrclass,
+				Byte			rdata[]
+				);
+		};
+	}
+}
diff --git a/mdnsresponder/mDNSWindows/DLL.NET/dnssd_NET.ico b/mdnsresponder/mDNSWindows/DLL.NET/dnssd_NET.ico
new file mode 100755
index 0000000..3a5525f
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DLL.NET/dnssd_NET.ico
Binary files differ
diff --git a/mdnsresponder/mDNSWindows/DLL.NET/dnssd_NET.rc b/mdnsresponder/mDNSWindows/DLL.NET/dnssd_NET.rc
new file mode 100755
index 0000000..95df1cf
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DLL.NET/dnssd_NET.rc
@@ -0,0 +1,113 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+#include "WinVersRes.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+1                       ICON                    "dnssd_NET.ico"
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE 
+BEGIN
+    "resource.h\0"
+    "\0"
+END
+
+2 TEXTINCLUDE 
+BEGIN
+    "#include ""afxres.h""\r\n"
+    "\0"
+END
+
+3 TEXTINCLUDE 
+BEGIN
+    "\r\n"
+    "\0"
+END
+
+#endif    // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION MASTER_PROD_VERS
+ PRODUCTVERSION MASTER_PROD_VERS
+ FILEFLAGSMASK 0x17L
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "040904b0"
+        BEGIN
+            VALUE "CompanyName", MASTER_COMPANY_NAME
+            VALUE "FileDescription", "Bonjour.NET Client Library"
+            VALUE "FileVersion", MASTER_PROD_VERS_STR
+            VALUE "InternalName", "dnssd.NET.dll"
+            VALUE "LegalCopyright", MASTER_LEGAL_COPYRIGHT
+            VALUE "OriginalFilename", "dnssd.NET.dll"
+            VALUE "ProductName", MASTER_PROD_NAME
+            VALUE "ProductVersion", MASTER_PROD_VERS_STR
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x409, 1200
+    END
+END
+
+#endif    // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif    // not APSTUDIO_INVOKED
+
diff --git a/mdnsresponder/mDNSWindows/DLL.NET/dnssd_NET.vcproj b/mdnsresponder/mDNSWindows/DLL.NET/dnssd_NET.vcproj
new file mode 100755
index 0000000..98cc63b
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DLL.NET/dnssd_NET.vcproj
@@ -0,0 +1,446 @@
+<?xml version="1.0" encoding="Windows-1252"?>

+<VisualStudioProject

+	ProjectType="Visual C++"

+	Version="8.00"

+	Name="DLL.NET"

+	ProjectGUID="{2A2FFA97-AF60-494F-9384-BBAA283AA3F2}"

+	RootNamespace="DLL2NET"

+	Keyword="ManagedCProj"

+	>

+	<Platforms>

+		<Platform

+			Name="Win32"

+		/>

+		<Platform

+			Name="x64"

+		/>

+	</Platforms>

+	<ToolFiles>

+	</ToolFiles>

+	<Configurations>

+		<Configuration

+			Name="Debug|Win32"

+			OutputDirectory="$(PlatformName)\$(ConfigurationName)"

+			IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"

+			ConfigurationType="2"

+			CharacterSet="1"

+			ManagedExtensions="4"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+				Description="Generating keypair..."

+				CommandLine="sn -k dnssd_NET.snk"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				Optimization="0"

+				AdditionalIncludeDirectories="../;../../mDNSShared"

+				PreprocessorDefinitions="WIN32;_DEBUG;WIN32_LEAN_AND_MEAN"

+				RuntimeLibrary="3"

+				UsePrecompiledHeader="2"

+				WarningLevel="3"

+				DebugInformationFormat="3"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+				AdditionalIncludeDirectories="../"

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				AdditionalDependencies="../DLL/$(OutDir)/dnssd.lib ws2_32.lib"

+				OutputFile="$(OutDir)\dnssd.NET.dll"

+				LinkIncremental="2"

+				GenerateDebugInformation="true"

+				AssemblyDebug="1"

+				TargetMachine="1"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+				EmbedManifest="false"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+			/>

+		</Configuration>

+		<Configuration

+			Name="Debug|x64"

+			OutputDirectory="$(PlatformName)\$(ConfigurationName)"

+			IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"

+			ConfigurationType="2"

+			CharacterSet="1"

+			ManagedExtensions="4"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+				Description="Generating keypair..."

+				CommandLine="sn -k dnssd_NET.snk"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+				TargetEnvironment="3"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				Optimization="0"

+				AdditionalIncludeDirectories="../;../../mDNSShared"

+				PreprocessorDefinitions="WIN32;_DEBUG;WIN32_LEAN_AND_MEAN"

+				RuntimeLibrary="3"

+				UsePrecompiledHeader="2"

+				WarningLevel="3"

+				DebugInformationFormat="3"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+				AdditionalIncludeDirectories="../"

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				AdditionalDependencies="../DLL/$(OutDir)/dnssd.lib ws2_32.lib"

+				OutputFile="$(OutDir)\dnssd.NET.dll"

+				LinkIncremental="2"

+				GenerateDebugInformation="true"

+				AssemblyDebug="1"

+				TargetMachine="17"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+				EmbedManifest="false"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+			/>

+		</Configuration>

+		<Configuration

+			Name="Release|Win32"

+			OutputDirectory="$(PlatformName)\$(ConfigurationName)"

+			IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"

+			ConfigurationType="2"

+			CharacterSet="1"

+			ManagedExtensions="4"

+			WholeProgramOptimization="1"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+				Description="Generating keypair..."

+				CommandLine="sn -k dnssd_NET.snk"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				AdditionalIncludeDirectories="../;../../mDNSShared"

+				PreprocessorDefinitions="WIN32;NDEBUG;WIN32_LEAN_AND_MEAN"

+				RuntimeLibrary="2"

+				UsePrecompiledHeader="0"

+				WarningLevel="3"

+				DebugInformationFormat="3"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+				AdditionalIncludeDirectories="../"

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				AdditionalDependencies="../DLL/$(OutDir)/dnssd.lib ws2_32.lib"

+				OutputFile="$(OutDir)\dnssd.NET.dll"

+				LinkIncremental="1"

+				GenerateDebugInformation="true"

+				TargetMachine="1"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+				EmbedManifest="false"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+			/>

+		</Configuration>

+		<Configuration

+			Name="Release|x64"

+			OutputDirectory="$(PlatformName)\$(ConfigurationName)"

+			IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"

+			ConfigurationType="2"

+			CharacterSet="1"

+			ManagedExtensions="4"

+			WholeProgramOptimization="1"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+				Description="Generating keypair..."

+				CommandLine="sn -k dnssd_NET.snk"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+				TargetEnvironment="3"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				AdditionalIncludeDirectories="../;../../mDNSShared"

+				PreprocessorDefinitions="WIN32;NDEBUG;WIN32_LEAN_AND_MEAN"

+				RuntimeLibrary="2"

+				UsePrecompiledHeader="0"

+				WarningLevel="3"

+				DebugInformationFormat="3"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+				AdditionalIncludeDirectories="../"

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				AdditionalDependencies="../DLL/$(OutDir)/dnssd.lib ws2_32.lib"

+				OutputFile="$(OutDir)\dnssd.NET.dll"

+				LinkIncremental="1"

+				GenerateDebugInformation="true"

+				TargetMachine="17"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+				EmbedManifest="false"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+			/>

+		</Configuration>

+	</Configurations>

+	<References>

+		<AssemblyReference

+			RelativePath="System.dll"

+			AssemblyName="System, Version=2.0.0.0, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL"

+		/>

+		<AssemblyReference

+			RelativePath="System.Data.dll"

+			AssemblyName="System.Data, Version=2.0.0.0, PublicKeyToken=b77a5c561934e089, processorArchitecture=x86"

+		/>

+		<AssemblyReference

+			RelativePath="System.XML.dll"

+			AssemblyName="System.Xml, Version=2.0.0.0, PublicKeyToken=b77a5c561934e089, processorArchitecture=MSIL"

+		/>

+	</References>

+	<Files>

+		<Filter

+			Name="Source Files"

+			Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"

+			UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"

+			>

+			<File

+				RelativePath=".\AssemblyInfo.cpp"

+				>

+			</File>

+			<File

+				RelativePath=".\dnssd_NET.cpp"

+				>

+			</File>

+			<File

+				RelativePath=".\Stdafx.cpp"

+				>

+				<FileConfiguration

+					Name="Debug|Win32"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						UsePrecompiledHeader="1"

+					/>

+				</FileConfiguration>

+				<FileConfiguration

+					Name="Debug|x64"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						UsePrecompiledHeader="1"

+					/>

+				</FileConfiguration>

+				<FileConfiguration

+					Name="Release|Win32"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						UsePrecompiledHeader="1"

+					/>

+				</FileConfiguration>

+				<FileConfiguration

+					Name="Release|x64"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						UsePrecompiledHeader="1"

+					/>

+				</FileConfiguration>

+			</File>

+		</Filter>

+		<Filter

+			Name="Header Files"

+			Filter="h;hpp;hxx;hm;inl;inc;xsd"

+			UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"

+			>

+			<File

+				RelativePath=".\dnssd_NET.h"

+				>

+			</File>

+			<File

+				RelativePath=".\resource.h"

+				>

+			</File>

+			<File

+				RelativePath=".\Stdafx.h"

+				>

+			</File>

+		</Filter>

+		<Filter

+			Name="Resource Files"

+			Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"

+			UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"

+			>

+			<File

+				RelativePath=".\dnssd_NET.ico"

+				>

+			</File>

+			<File

+				RelativePath=".\dnssd_NET.rc"

+				>

+			</File>

+		</Filter>

+		<File

+			RelativePath=".\ReadMe.txt"

+			>

+		</File>

+	</Files>

+	<Globals>

+	</Globals>

+</VisualStudioProject>

diff --git a/mdnsresponder/mDNSWindows/DLL.NET/resource.h b/mdnsresponder/mDNSWindows/DLL.NET/resource.h
new file mode 100755
index 0000000..29338aa
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DLL.NET/resource.h
@@ -0,0 +1,3 @@
+//{{NO_DEPENDENCIES}}

+// Microsoft Visual C++ generated include file.

+// Used by dnssd_NET.rc

diff --git a/mdnsresponder/mDNSWindows/DLL/dll.aps b/mdnsresponder/mDNSWindows/DLL/dll.aps
new file mode 100644
index 0000000..31b4787
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DLL/dll.aps
Binary files differ
diff --git a/mdnsresponder/mDNSWindows/DLL/dll.rc b/mdnsresponder/mDNSWindows/DLL/dll.rc
new file mode 100644
index 0000000..e76fb30
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DLL/dll.rc
@@ -0,0 +1,102 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+#include "WinVersRes.h"
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE 
+BEGIN
+    "resource.h\0"
+END
+
+2 TEXTINCLUDE 
+BEGIN
+    "#include ""afxres.h""\r\n"
+    "#include ""WinVersRes.h""\0"
+END
+
+3 TEXTINCLUDE 
+BEGIN
+    "\r\n"
+    "\0"
+END
+
+#endif    // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION MASTER_PROD_VERS
+ PRODUCTVERSION MASTER_PROD_VERS
+ FILEFLAGSMASK 0x17L
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "040904b0"
+        BEGIN
+            VALUE "CompanyName", MASTER_COMPANY_NAME
+            VALUE "FileDescription", "Bonjour Client Library"
+            VALUE "FileVersion", MASTER_PROD_VERS_STR
+            VALUE "InternalName", "dnssd.dll"
+            VALUE "LegalCopyright", MASTER_LEGAL_COPYRIGHT
+            VALUE "OriginalFilename", "dnssd.dll"
+            VALUE "ProductName", MASTER_PROD_NAME
+            VALUE "ProductVersion", MASTER_PROD_VERS_STR
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x409, 1200
+    END
+END
+
+#endif    // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif    // not APSTUDIO_INVOKED
+
diff --git a/mdnsresponder/mDNSWindows/DLL/dllmain.c b/mdnsresponder/mDNSWindows/DLL/dllmain.c
new file mode 100644
index 0000000..79f3bb7
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DLL/dllmain.c
@@ -0,0 +1,112 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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 <windows.h>
+#include <DebugServices.h>
+
+BOOL APIENTRY	DllMain( HANDLE inModule, DWORD inReason, LPVOID inReserved )
+{
+	(void) inModule;
+	(void) inReserved;
+	
+	switch( inReason )
+	{
+		case DLL_PROCESS_ATTACH:
+		case DLL_THREAD_ATTACH:
+		case DLL_THREAD_DETACH:
+		case DLL_PROCESS_DETACH:
+			break;
+	}
+    return( TRUE );
+}
+
+
+BOOL
+IsSystemServiceDisabled()
+{
+	ENUM_SERVICE_STATUS	*	lpService = NULL;
+	SC_HANDLE					sc;
+	BOOL							ret = FALSE;
+	BOOL							ok;
+	DWORD							bytesNeeded = 0;
+	DWORD							srvCount;
+	DWORD							resumeHandle = 0;
+	DWORD							srvType;
+	DWORD							srvState;
+	DWORD							dwBytes = 0;
+	DWORD							i;
+	OSStatus						err;
+
+	sc = OpenSCManager( NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE );
+	err = translate_errno( sc, GetLastError(), kUnknownErr );
+	require_noerr( err, exit );
+
+	srvType		=	SERVICE_WIN32;
+	srvState		=	SERVICE_STATE_ALL;
+
+	for ( ;; )
+	{
+		// Call EnumServicesStatus using the handle returned by OpenSCManager
+
+		ok = EnumServicesStatus ( sc, srvType, srvState, lpService, dwBytes, &bytesNeeded, &srvCount, &resumeHandle );
+
+		if ( ok || ( GetLastError() != ERROR_MORE_DATA ) )
+		{
+			break;
+		}
+
+		if ( lpService )
+		{
+			free( lpService );
+		}
+
+		dwBytes = bytesNeeded;
+
+		lpService = ( ENUM_SERVICE_STATUS* ) malloc( dwBytes );
+		require_action( lpService, exit, ret = FALSE );
+	}
+
+	err = translate_errno( ok, GetLastError(), kUnknownErr );
+	require_noerr( err, exit );
+
+	for ( i = 0; i < srvCount; i++ )
+	{
+		if ( strcmp( lpService[i].lpServiceName, "Bonjour Service" ) == 0 )
+		{
+			if ( ( lpService[i].ServiceStatus.dwCurrentState == SERVICE_PAUSED ) || ( lpService[i].ServiceStatus.dwCurrentState == SERVICE_STOPPED ) )
+			{
+				ret = TRUE;
+			}
+
+			break;
+		}
+	}
+
+exit:
+
+	if ( lpService )
+	{
+		free( lpService );
+	}
+
+	if ( sc )
+	{
+		CloseServiceHandle ( sc );
+	}
+
+	return ret;
+}
diff --git a/mdnsresponder/mDNSWindows/DLL/dnssd.def b/mdnsresponder/mDNSWindows/DLL/dnssd.def
new file mode 100644
index 0000000..160510b
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DLL/dnssd.def
@@ -0,0 +1,48 @@
+; -*- tab-width: 4 -*-
+;
+; Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved.
+;
+; Licensed 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.
+;
+
+LIBRARY		dnssd
+
+EXPORTS
+	DNSServiceRefSockFD
+	DNSServiceProcessResult
+	DNSServiceRefDeallocate
+	DNSServiceEnumerateDomains
+	DNSServiceRegister
+	DNSServiceAddRecord
+	DNSServiceUpdateRecord
+	DNSServiceRemoveRecord
+	DNSServiceBrowse
+	DNSServiceResolve
+	DNSServiceConstructFullName
+	DNSServiceCreateConnection
+	DNSServiceRegisterRecord
+	DNSServiceQueryRecord
+	DNSServiceReconfirmRecord
+	DNSServiceNATPortMappingCreate
+	DNSServiceGetAddrInfo
+	DNSServiceGetProperty
+	TXTRecordCreate
+	TXTRecordDeallocate
+	TXTRecordSetValue
+	TXTRecordRemoveValue
+	TXTRecordContainsKey
+	TXTRecordGetCount
+	TXTRecordGetLength
+	TXTRecordGetBytesPtr
+	TXTRecordGetValuePtr
+	TXTRecordGetItemAtIndex
diff --git a/mdnsresponder/mDNSWindows/DLL/dnssd.vcproj b/mdnsresponder/mDNSWindows/DLL/dnssd.vcproj
new file mode 100644
index 0000000..814a322
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DLL/dnssd.vcproj
@@ -0,0 +1,472 @@
+<?xml version="1.0" encoding="Windows-1252"?>

+<VisualStudioProject

+	ProjectType="Visual C++"

+	Version="8.00"

+	Name="DLL"

+	ProjectGUID="{AB581101-18F0-46F6-B56A-83A6B1EA657E}"

+	RootNamespace="DLL"

+	Keyword="Win32Proj"

+	>

+	<Platforms>

+		<Platform

+			Name="Win32"

+		/>

+		<Platform

+			Name="x64"

+		/>

+	</Platforms>

+	<ToolFiles>

+	</ToolFiles>

+	<Configurations>

+		<Configuration

+			Name="Debug|Win32"

+			OutputDirectory="$(PlatformName)\$(ConfigurationName)"

+			IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"

+			ConfigurationType="2"

+			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"

+			CharacterSet="2"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				Optimization="0"

+				AdditionalIncludeDirectories="../../mDNSCore;../../mDNSShared;../"

+				PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;DEBUG=1;NOT_HAVE_SA_LEN;MDNS_DEBUGMSGS=0;WIN32_LEAN_AND_MEAN;USE_TCP_LOOPBACK;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1"

+				StringPooling="true"

+				MinimalRebuild="true"

+				ExceptionHandling="0"

+				BasicRuntimeChecks="3"

+				SmallerTypeCheck="true"

+				RuntimeLibrary="1"

+				BufferSecurityCheck="true"

+				UsePrecompiledHeader="0"

+				AssemblerListingLocation="$(IntDir)\"

+				ProgramDataBaseFileName="$(IntDir)\dnssd.dll.pdb"

+				WarningLevel="4"

+				Detect64BitPortabilityProblems="true"

+				DebugInformationFormat="3"

+				CallingConvention="2"

+				CompileAs="0"

+				DisableSpecificWarnings="4127;4204"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+				AdditionalIncludeDirectories="../"

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				AdditionalOptions="/NXCOMPAT /DYNAMICBASE /SAFESEH"

+				AdditionalDependencies="ws2_32.lib iphlpapi.lib"

+				OutputFile="$(OutDir)/dnssd.dll"

+				LinkIncremental="2"

+				ModuleDefinitionFile="dnssd.def"

+				GenerateDebugInformation="true"

+				ProgramDatabaseFile="$(OutDir)\dnssd.dll.pdb"

+				SubSystem="2"

+				BaseAddress="0x16000000"

+				ImportLibrary="$(OutDir)\dnssd.lib"

+				TargetMachine="1"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+			/>

+		</Configuration>

+		<Configuration

+			Name="Debug|x64"

+			OutputDirectory="$(PlatformName)\$(ConfigurationName)"

+			IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"

+			ConfigurationType="2"

+			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"

+			CharacterSet="2"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+				TargetEnvironment="3"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				Optimization="0"

+				AdditionalIncludeDirectories="../../mDNSCore;../../mDNSShared;../"

+				PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;DEBUG=1;MDNS_DEBUGMSGS=0;WIN32_LEAN_AND_MEAN;USE_TCP_LOOPBACK;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1;NOT_HAVE_SA_LEN"

+				StringPooling="true"

+				MinimalRebuild="true"

+				ExceptionHandling="0"

+				BasicRuntimeChecks="3"

+				SmallerTypeCheck="true"

+				RuntimeLibrary="1"

+				BufferSecurityCheck="true"

+				UsePrecompiledHeader="0"

+				AssemblerListingLocation="$(IntDir)\"

+				ProgramDataBaseFileName="$(IntDir)\dnssd.dll.pdb"

+				WarningLevel="4"

+				Detect64BitPortabilityProblems="true"

+				DebugInformationFormat="3"

+				CallingConvention="2"

+				CompileAs="0"

+				DisableSpecificWarnings="4127;4204"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+				AdditionalIncludeDirectories="../"

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				AdditionalOptions="/NXCOMPAT /DYNAMICBASE"

+				AdditionalDependencies="ws2_32.lib iphlpapi.lib"

+				OutputFile="$(OutDir)/dnssd.dll"

+				LinkIncremental="2"

+				ModuleDefinitionFile="dnssd.def"

+				GenerateDebugInformation="true"

+				ProgramDatabaseFile="$(OutDir)\dnssd.dll.pdb"

+				SubSystem="2"

+				BaseAddress="0x16000000"

+				ImportLibrary="$(OutDir)\dnssd.lib"

+				TargetMachine="17"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+			/>

+		</Configuration>

+		<Configuration

+			Name="Release|Win32"

+			OutputDirectory="$(PlatformName)\$(ConfigurationName)"

+			IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"

+			ConfigurationType="2"

+			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"

+			CharacterSet="2"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				AdditionalIncludeDirectories="../../mDNSCore;../../mDNSShared;../"

+				PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;MDNS_DEBUGMSGS=0;WIN32_LEAN_AND_MEAN;USE_TCP_LOOPBACK;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1;NOT_HAVE_SA_LEN"

+				RuntimeLibrary="0"

+				UsePrecompiledHeader="0"

+				AssemblerListingLocation="$(IntDir)\"

+				ProgramDataBaseFileName="$(IntDir)\dnssd.dll.pdb"

+				WarningLevel="4"

+				Detect64BitPortabilityProblems="true"

+				DebugInformationFormat="3"

+				CallingConvention="2"

+				CompileAs="0"

+				DisableSpecificWarnings="4127;4204"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+				AdditionalIncludeDirectories="../"

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				AdditionalOptions="/NXCOMPAT /DYNAMICBASE /SAFESEH"

+				AdditionalDependencies="ws2_32.lib iphlpapi.lib"

+				OutputFile="$(OutDir)/dnssd.dll"

+				LinkIncremental="1"

+				ModuleDefinitionFile="dnssd.def"

+				GenerateDebugInformation="true"

+				ProgramDatabaseFile="$(OutDir)\dnssd.dll.pdb"

+				SubSystem="2"

+				OptimizeReferences="2"

+				EnableCOMDATFolding="2"

+				BaseAddress="0x16000000"

+				ImportLibrary="$(OutDir)\dnssd.lib"

+				TargetMachine="1"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+				CommandLine="if not &quot;%RC_XBS%&quot; == &quot;YES&quot; goto END&#x0D;&#x0A;if not exist &quot;$(DSTROOT)\WINDOWS\system32\$(PlatformName)&quot;                     mkdir &quot;$(DSTROOT)\WINDOWS\system32\$(PlatformName)&quot;&#x0D;&#x0A;if not exist &quot;$(DSTROOT)\Program Files\Bonjour SDK\lib&quot;                                     mkdir &quot;$(DSTROOT)\Program Files\Bonjour SDK\lib&quot;&#x0D;&#x0A;if not exist &quot;$(DSTROOT)\Program Files\Bonjour SDK\lib\$(PlatformName)&quot;     mkdir &quot;$(DSTROOT)\Program Files\Bonjour SDK\lib\$(PlatformName)&quot;&#x0D;&#x0A;if not exist &quot;$(DSTROOT)\Program Files\Bonjour SDK\include&quot;                            mkdir &quot;$(DSTROOT)\Program Files\Bonjour SDK\include&quot;&#x0D;&#x0A;if not exist &quot;$(DSTROOT)\AppleInternal&quot;                                                                   mkdir &quot;$(DSTROOT)\AppleInternal&quot;&#x0D;&#x0A;if not exist &quot;$(DSTROOT)\AppleInternal\bin&quot;                                                           mkdir &quot;$(DSTROOT)\AppleInternal\bin&quot;&#x0D;&#x0A;if not exist &quot;$(DSTROOT)\AppleInternal\bin\$(PlatformName)&quot;                           mkdir &quot;$(DSTROOT)\AppleInternal\bin\$(PlatformName)&quot;&#x0D;&#x0A;xcopy /I/Y &quot;$(TargetPath)&quot;                                                                                           &quot;$(DSTROOT)\WINDOWS\system32\$(PlatformName)&quot;&#x0D;&#x0A;xcopy /I/Y &quot;$(OutDir)\dnssd.dll.pdb&quot;                                                                          &quot;$(DSTROOT)\AppleInternal\bin\$(PlatformName)&quot;&#x0D;&#x0A;xcopy /I/Y &quot;$(ProjectDir)..\..\mDNSShared\dns_sd.h&quot;                                             &quot;$(DSTROOT)\Program Files\Bonjour SDK\include&quot;&#x0D;&#x0A;:END&#x0D;&#x0A;"

+			/>

+		</Configuration>

+		<Configuration

+			Name="Release|x64"

+			OutputDirectory="$(PlatformName)\$(ConfigurationName)"

+			IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"

+			ConfigurationType="2"

+			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"

+			CharacterSet="2"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+				TargetEnvironment="3"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				AdditionalIncludeDirectories="../../mDNSCore;../../mDNSShared;../"

+				PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS;_USRDLL;MDNS_DEBUGMSGS=0;WIN32_LEAN_AND_MEAN;USE_TCP_LOOPBACK;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1;NOT_HAVE_SA_LEN"

+				RuntimeLibrary="0"

+				UsePrecompiledHeader="0"

+				AssemblerListingLocation="$(IntDir)\"

+				ProgramDataBaseFileName="$(IntDir)\dnssd.dll.pdb"

+				WarningLevel="4"

+				Detect64BitPortabilityProblems="true"

+				DebugInformationFormat="3"

+				CallingConvention="2"

+				CompileAs="0"

+				DisableSpecificWarnings="4127;4204"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+				AdditionalIncludeDirectories="../"

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				AdditionalOptions="/NXCOMPAT /DYNAMICBASE"

+				AdditionalDependencies="ws2_32.lib iphlpapi.lib"

+				OutputFile="$(OutDir)/dnssd.dll"

+				LinkIncremental="1"

+				ModuleDefinitionFile="dnssd.def"

+				GenerateDebugInformation="true"

+				ProgramDatabaseFile="$(OutDir)\dnssd.dll.pdb"

+				SubSystem="2"

+				OptimizeReferences="2"

+				EnableCOMDATFolding="2"

+				BaseAddress="0x16000000"

+				ImportLibrary="$(OutDir)\dnssd.lib"

+				TargetMachine="17"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+				CommandLine="if not &quot;%RC_XBS%&quot; == &quot;YES&quot; goto END&#x0D;&#x0A;if not exist &quot;$(DSTROOT)\WINDOWS\system32\$(PlatformName)&quot;                     mkdir &quot;$(DSTROOT)\WINDOWS\system32\$(PlatformName)&quot;&#x0D;&#x0A;if not exist &quot;$(DSTROOT)\Program Files\Bonjour SDK\lib&quot;                                     mkdir &quot;$(DSTROOT)\Program Files\Bonjour SDK\lib&quot;&#x0D;&#x0A;if not exist &quot;$(DSTROOT)\Program Files\Bonjour SDK\lib\$(PlatformName)&quot;     mkdir &quot;$(DSTROOT)\Program Files\Bonjour SDK\lib\$(PlatformName)&quot;&#x0D;&#x0A;if not exist &quot;$(DSTROOT)\Program Files\Bonjour SDK\include&quot;                            mkdir &quot;$(DSTROOT)\Program Files\Bonjour SDK\include&quot;&#x0D;&#x0A;if not exist &quot;$(DSTROOT)\AppleInternal&quot;                                                                   mkdir &quot;$(DSTROOT)\AppleInternal&quot;&#x0D;&#x0A;if not exist &quot;$(DSTROOT)\AppleInternal\bin&quot;                                                           mkdir &quot;$(DSTROOT)\AppleInternal\bin&quot;&#x0D;&#x0A;if not exist &quot;$(DSTROOT)\AppleInternal\bin\$(PlatformName)&quot;                           mkdir &quot;$(DSTROOT)\AppleInternal\bin\$(PlatformName)&quot;&#x0D;&#x0A;xcopy /I/Y &quot;$(TargetPath)&quot;                                                                                           &quot;$(DSTROOT)\WINDOWS\system32\$(PlatformName)&quot;&#x0D;&#x0A;xcopy /I/Y &quot;$(OutDir)\dnssd.dll.pdb&quot;                                                                         &quot;$(DSTROOT)\AppleInternal\bin\$(PlatformName)&quot;&#x0D;&#x0A;:END&#x0D;&#x0A;"

+			/>

+		</Configuration>

+	</Configurations>

+	<References>

+	</References>

+	<Files>

+		<Filter

+			Name="Source Files"

+			Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"

+			UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"

+			>

+			<File

+				RelativePath="..\..\mDNSShared\DebugServices.c"

+				>

+			</File>

+			<File

+				RelativePath=".\dllmain.c"

+				>

+			</File>

+			<File

+				RelativePath=".\dnssd.def"

+				>

+			</File>

+			<File

+				RelativePath="..\..\mDNSShared\dnssd_clientlib.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\mDNSShared\dnssd_clientstub.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\mDNSShared\dnssd_ipc.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\mDNSShared\GenLinkedList.c"

+				>

+			</File>

+		</Filter>

+		<Filter

+			Name="Header Files"

+			Filter="h;hpp;hxx;hm;inl;inc;xsd"

+			UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"

+			>

+			<File

+				RelativePath="..\..\mDNSShared\CommonServices.h"

+				>

+			</File>

+			<File

+				RelativePath="..\..\mDNSShared\DebugServices.h"

+				>

+			</File>

+			<File

+				RelativePath="..\..\mDNSShared\dns_sd.h"

+				>

+			</File>

+			<File

+				RelativePath="..\..\mDNSShared\dnssd_ipc.h"

+				>

+			</File>

+			<File

+				RelativePath="..\..\mDNSShared\GenLinkedList.h"

+				>

+			</File>

+			<File

+				RelativePath=".\resource.h"

+				>

+			</File>

+		</Filter>

+		<Filter

+			Name="Resource Files"

+			Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"

+			UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"

+			>

+			<File

+				RelativePath=".\dll.rc"

+				>

+			</File>

+		</Filter>

+	</Files>

+	<Globals>

+	</Globals>

+</VisualStudioProject>

diff --git a/mdnsresponder/mDNSWindows/DLL/resource.h b/mdnsresponder/mDNSWindows/DLL/resource.h
new file mode 100644
index 0000000..bdea251
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DLL/resource.h
@@ -0,0 +1,27 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by dll.rc
+//
+#define IDS_PROJNAME                    100
+#define IDR_WMDMLOGGER                  101
+#define IDS_LOG_SEV_INFO                201
+#define IDS_LOG_SEV_WARN                202
+#define IDS_LOG_SEV_ERROR               203
+#define IDS_LOG_DATETIME                204
+#define IDS_LOG_SRCNAME                 205
+#define IDS_DEF_LOGFILE                 301
+#define IDS_DEF_MAXSIZE                 302
+#define IDS_DEF_SHRINKTOSIZE            303
+#define IDS_DEF_LOGENABLED              304
+#define IDS_MUTEX_TIMEOUT               401
+
+// Next default values for new objects
+// 
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE        201
+#define _APS_NEXT_COMMAND_VALUE         32768
+#define _APS_NEXT_CONTROL_VALUE         201
+#define _APS_NEXT_SYMED_VALUE           101
+#endif
+#endif
diff --git a/mdnsresponder/mDNSWindows/DLLStub/DLLStub.cpp b/mdnsresponder/mDNSWindows/DLLStub/DLLStub.cpp
new file mode 100755
index 0000000..c39a747
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DLLStub/DLLStub.cpp
@@ -0,0 +1,693 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2009, Apple Computer, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of its
+ *     contributors may be used to endorse or promote products derived from this
+ *     software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "DLLStub.h"
+
+static int		g_defaultErrorCode = kDNSServiceErr_Unknown;
+static DLLStub	g_glueLayer;
+
+
+// ------------------------------------------
+// DLLStub implementation
+// ------------------------------------------
+DLLStub * DLLStub::m_instance;
+
+DLLStub::DLLStub()
+:
+	m_library( LoadLibrary( TEXT( "dnssd.dll" ) ) )
+{
+	m_instance = this;
+}
+
+
+DLLStub::~DLLStub()
+{
+	if ( m_library != NULL )
+	{
+		FreeLibrary( m_library );
+		m_library = NULL;
+	}
+
+	m_instance = NULL;
+}
+
+
+bool
+DLLStub::GetProcAddress( FARPROC * func, LPCSTR lpProcName )
+{ 
+	if ( m_instance && m_instance->m_library )
+	{
+		// Only call ::GetProcAddress if *func is NULL. This allows
+		// the calling code to cache the funcptr value, and we get
+		// some performance benefit.
+
+		if ( *func == NULL )
+		{
+			*func = ::GetProcAddress( m_instance->m_library, lpProcName );
+		}
+	}
+	else
+	{
+		*func = NULL;
+	}
+
+	return ( *func != NULL );
+}
+
+
+int DNSSD_API
+DNSServiceRefSockFD(DNSServiceRef sdRef)
+{
+	typedef int (DNSSD_API * Func)(DNSServiceRef sdRef);
+	static Func func = NULL;
+	int ret = -1;
+
+	if ( DLLStub::GetProcAddress( ( FARPROC* ) &func, __FUNCTION__ ) )
+	{
+		ret = func( sdRef );
+	}
+	
+	return ret;
+}
+
+
+DNSServiceErrorType DNSSD_API
+DNSServiceProcessResult(DNSServiceRef sdRef)
+{
+	typedef DNSServiceErrorType (DNSSD_API * Func)(DNSServiceRef sdRef);
+	static Func func = NULL;
+	DNSServiceErrorType ret = g_defaultErrorCode;
+
+	if ( DLLStub::GetProcAddress( ( FARPROC* ) &func, __FUNCTION__ ) )
+	{
+		ret = func( sdRef );
+	}
+	
+	return ret;
+}
+
+
+void DNSSD_API
+DNSServiceRefDeallocate(DNSServiceRef sdRef)
+{
+	typedef void (DNSSD_API * Func)(DNSServiceRef sdRef);
+	static Func func = NULL;
+
+	if ( DLLStub::GetProcAddress( ( FARPROC* ) &func, __FUNCTION__ ) )
+	{
+		func( sdRef );
+	}
+}
+
+
+DNSServiceErrorType DNSSD_API
+DNSServiceEnumerateDomains
+		(
+		DNSServiceRef                       *sdRef,
+		DNSServiceFlags                     flags,
+		uint32_t                            interfaceIndex,
+		DNSServiceDomainEnumReply           callBack,
+		void                                *context
+		)
+{
+	typedef DNSServiceErrorType (DNSSD_API * Func)(DNSServiceRef*, DNSServiceFlags, uint32_t, DNSServiceDomainEnumReply, void* );
+	static Func func = NULL;
+	DNSServiceErrorType ret = g_defaultErrorCode;
+
+	if ( DLLStub::GetProcAddress( ( FARPROC* ) &func, __FUNCTION__ ) )
+	{
+		ret = func( sdRef, flags, interfaceIndex, callBack, context );
+	}
+	
+	return ret;
+}
+
+
+DNSServiceErrorType DNSSD_API
+DNSServiceRegister
+		(
+		DNSServiceRef                       *sdRef,
+		DNSServiceFlags                     flags,
+		uint32_t                            interfaceIndex,
+		const char                          *name,
+		const char                          *regtype,
+		const char                          *domain,
+		const char                          *host,
+		uint16_t                            port,
+		uint16_t                            txtLen,
+		const void                          *txtRecord,
+		DNSServiceRegisterReply             callBack,
+		void                                *context
+		)
+{
+	typedef DNSServiceErrorType (DNSSD_API * Func)(DNSServiceRef*, DNSServiceFlags, uint32_t, const char*, const char*, const char*, const char*, uint16_t, uint16_t, const void*, DNSServiceRegisterReply, void* );
+	static Func func = NULL;
+	DNSServiceErrorType ret = g_defaultErrorCode;
+
+	if ( DLLStub::GetProcAddress( ( FARPROC* ) &func, __FUNCTION__ ) )
+	{
+		ret = func( sdRef, flags, interfaceIndex, name, regtype, domain, host, port, txtLen, txtRecord, callBack, context );
+	}
+	
+	return ret;
+}
+
+
+DNSServiceErrorType DNSSD_API
+DNSServiceAddRecord
+		(
+		DNSServiceRef                       sdRef,
+		DNSRecordRef                        *RecordRef,
+		DNSServiceFlags                     flags,
+		uint16_t                            rrtype,
+		uint16_t                            rdlen,
+		const void                          *rdata,
+		uint32_t                            ttl
+		)
+{
+	typedef DNSServiceErrorType (DNSSD_API * Func)(DNSServiceRef, DNSRecordRef*, DNSServiceFlags, uint16_t, uint16_t, const void*, uint32_t );
+	static Func func = NULL;
+	DNSServiceErrorType ret = g_defaultErrorCode;
+
+	if ( DLLStub::GetProcAddress( ( FARPROC* ) &func, __FUNCTION__ ) )
+	{
+		ret = func( sdRef, RecordRef, flags, rrtype, rdlen, rdata, ttl );
+	}
+	
+	return ret;
+}
+
+
+DNSServiceErrorType DNSSD_API
+DNSServiceUpdateRecord
+		(
+		DNSServiceRef                       sdRef,
+		DNSRecordRef                        RecordRef,     /* may be NULL */
+		DNSServiceFlags                     flags,
+		uint16_t                            rdlen,
+		const void                          *rdata,
+		uint32_t                            ttl
+		)
+{
+	typedef DNSServiceErrorType (DNSSD_API * Func)(DNSServiceRef, DNSRecordRef, DNSServiceFlags, uint16_t, const void*, uint32_t );
+	static Func func = NULL;
+	DNSServiceErrorType ret = g_defaultErrorCode;
+
+	if ( DLLStub::GetProcAddress( ( FARPROC* ) &func, __FUNCTION__ ) )
+	{
+		ret = func( sdRef, RecordRef, flags, rdlen, rdata, ttl );
+	}
+	
+	return ret;
+}
+
+
+DNSServiceErrorType DNSSD_API
+DNSServiceRemoveRecord
+		(
+		DNSServiceRef                 sdRef,
+		DNSRecordRef                  RecordRef,
+		DNSServiceFlags               flags
+		)
+{
+	typedef DNSServiceErrorType (DNSSD_API * Func)(DNSServiceRef, DNSRecordRef, DNSServiceFlags );
+	static Func func = NULL;
+	DNSServiceErrorType ret = g_defaultErrorCode;
+
+	if ( DLLStub::GetProcAddress( ( FARPROC* ) &func, __FUNCTION__ ) )
+	{
+		ret = func( sdRef, RecordRef, flags );
+	}
+	
+	return ret;
+}
+
+
+DNSServiceErrorType DNSSD_API
+DNSServiceBrowse
+		(
+		DNSServiceRef                       *sdRef,
+		DNSServiceFlags                     flags,
+		uint32_t                            interfaceIndex,
+		const char                          *regtype,
+		const char                          *domain,    /* may be NULL */
+		DNSServiceBrowseReply               callBack,
+		void                                *context    /* may be NULL */
+		)
+{
+	typedef DNSServiceErrorType (DNSSD_API * Func)(DNSServiceRef*, DNSServiceFlags, uint32_t, const char*, const char*, DNSServiceBrowseReply, void* );
+	static Func func = NULL;
+	DNSServiceErrorType ret = g_defaultErrorCode;
+
+	if ( DLLStub::GetProcAddress( ( FARPROC* ) &func, __FUNCTION__ ) )
+	{
+		ret = func( sdRef, flags, interfaceIndex, regtype, domain, callBack, context );
+	}
+	
+	return ret;
+}
+
+
+DNSServiceErrorType DNSSD_API
+DNSServiceResolve
+		(
+		DNSServiceRef                       *sdRef,
+		DNSServiceFlags                     flags,
+		uint32_t                            interfaceIndex,
+		const char                          *name,
+		const char                          *regtype,
+		const char                          *domain,
+		DNSServiceResolveReply              callBack,
+		void                                *context  /* may be NULL */
+		)
+{
+	typedef DNSServiceErrorType (DNSSD_API * Func)(DNSServiceRef*, DNSServiceFlags, uint32_t, const char*, const char*, const char*, DNSServiceResolveReply, void* );
+	static Func func = NULL;
+	DNSServiceErrorType ret = g_defaultErrorCode;
+
+	if ( DLLStub::GetProcAddress( ( FARPROC* ) &func, __FUNCTION__ ) )
+	{
+		ret = func( sdRef, flags, interfaceIndex, name, regtype, domain, callBack, context );
+	}
+	
+	return ret;
+}
+
+
+DNSServiceErrorType DNSSD_API
+DNSServiceConstructFullName
+		(
+		char                            *fullName,
+		const char                      *service,      /* may be NULL */
+		const char                      *regtype,
+		const char                      *domain
+		)
+{
+	typedef DNSServiceErrorType (DNSSD_API * Func)( char*, const char*, const char*, const char* );
+	static Func func = NULL;
+	DNSServiceErrorType ret = g_defaultErrorCode;
+
+	if ( DLLStub::GetProcAddress( ( FARPROC* ) &func, __FUNCTION__ ) )
+	{
+		ret = func( fullName, service, regtype, domain );
+	}
+	
+	return ret;
+}
+
+
+DNSServiceErrorType DNSSD_API
+DNSServiceCreateConnection(DNSServiceRef *sdRef)
+{
+	typedef DNSServiceErrorType (DNSSD_API * Func)( DNSServiceRef* );
+	static Func func = NULL;
+	DNSServiceErrorType ret = g_defaultErrorCode;
+
+	if ( DLLStub::GetProcAddress( ( FARPROC* ) &func, __FUNCTION__ ) )
+	{
+		ret = func( sdRef );
+	}
+	
+	return ret;
+}
+
+
+DNSServiceErrorType DNSSD_API
+DNSServiceRegisterRecord
+		(
+		DNSServiceRef                       sdRef,
+		DNSRecordRef                        *RecordRef,
+		DNSServiceFlags                     flags,
+		uint32_t                            interfaceIndex,
+		const char                          *fullname,
+		uint16_t                            rrtype,
+		uint16_t                            rrclass,
+		uint16_t                            rdlen,
+		const void                          *rdata,
+		uint32_t                            ttl,
+		DNSServiceRegisterRecordReply       callBack,
+		void                                *context    /* may be NULL */
+		)
+{
+	typedef DNSServiceErrorType (DNSSD_API * Func)(DNSServiceRef, DNSRecordRef*, DNSServiceFlags, uint32_t, const char*, uint16_t, uint16_t, uint16_t, const void*, uint16_t, DNSServiceRegisterRecordReply, void* );
+	static Func func = NULL;
+	DNSServiceErrorType ret = g_defaultErrorCode;
+
+	if ( DLLStub::GetProcAddress( ( FARPROC* ) &func, __FUNCTION__ ) )
+	{
+		ret = func( sdRef, RecordRef, flags, interfaceIndex, fullname, rrtype, rrclass, rdlen, rdata, ttl, callBack, context );
+	}
+	
+	return ret;
+}
+
+
+DNSServiceErrorType DNSSD_API
+DNSServiceQueryRecord
+		(
+		DNSServiceRef                       *sdRef,
+		DNSServiceFlags                     flags,
+		uint32_t                            interfaceIndex,
+		const char                          *fullname,
+		uint16_t                            rrtype,
+		uint16_t                            rrclass,
+		DNSServiceQueryRecordReply          callBack,
+		void                                *context  /* may be NULL */
+		)
+{
+	typedef DNSServiceErrorType (DNSSD_API * Func)(DNSServiceRef*, DNSServiceFlags, uint32_t, const char*, uint16_t, uint16_t, DNSServiceQueryRecordReply, void* );
+	static Func func = NULL;
+	DNSServiceErrorType ret = g_defaultErrorCode;
+
+	if ( DLLStub::GetProcAddress( ( FARPROC* ) &func, __FUNCTION__ ) )
+	{
+		ret = func( sdRef, flags, interfaceIndex, fullname, rrtype, rrclass, callBack, context );
+	}
+	
+	return ret;
+}
+
+
+DNSServiceErrorType DNSSD_API
+DNSServiceReconfirmRecord
+		(
+		DNSServiceFlags                    flags,
+		uint32_t                           interfaceIndex,
+		const char                         *fullname,
+		uint16_t                           rrtype,
+		uint16_t                           rrclass,
+		uint16_t                           rdlen,
+		const void                         *rdata
+		)
+{
+	typedef DNSServiceErrorType (DNSSD_API * Func)( DNSServiceFlags, uint32_t, const char*, uint16_t, uint16_t, uint16_t, const void* );
+	static Func func = NULL;
+	DNSServiceErrorType ret = g_defaultErrorCode;
+
+	if ( DLLStub::GetProcAddress( ( FARPROC* ) &func, __FUNCTION__ ) )
+	{
+		ret = func( flags, interfaceIndex, fullname, rrtype, rrclass, rdlen, rdata );
+	}
+	
+	return ret;
+}
+
+
+DNSServiceErrorType DNSSD_API
+DNSServiceNATPortMappingCreate
+		(
+		DNSServiceRef                    *sdRef,
+		DNSServiceFlags                  flags,
+		uint32_t                         interfaceIndex,
+		DNSServiceProtocol               protocol,          /* TCP and/or UDP          */
+		uint16_t                         internalPort,      /* network byte order      */
+		uint16_t                         externalPort,      /* network byte order      */
+		uint32_t                         ttl,               /* time to live in seconds */
+		DNSServiceNATPortMappingReply    callBack,
+		void                             *context           /* may be NULL             */
+		)
+{
+	typedef DNSServiceErrorType (DNSSD_API * Func)(DNSServiceRef*, DNSServiceFlags, uint32_t, DNSServiceProtocol, uint16_t, uint16_t, uint16_t, DNSServiceNATPortMappingReply, void* );
+	static Func func = NULL;
+	DNSServiceErrorType ret = g_defaultErrorCode;
+
+	if ( DLLStub::GetProcAddress( ( FARPROC* ) &func, __FUNCTION__ ) )
+	{
+		ret = func( sdRef, flags, interfaceIndex, protocol, internalPort, externalPort, ttl, callBack, context );
+	}
+	
+	return ret;
+}
+
+
+DNSServiceErrorType DNSSD_API
+DNSServiceGetAddrInfo
+		(
+		DNSServiceRef                    *sdRef,
+		DNSServiceFlags                  flags,
+		uint32_t                         interfaceIndex,
+		DNSServiceProtocol               protocol,
+		const char                       *hostname,
+		DNSServiceGetAddrInfoReply       callBack,
+		void                             *context          /* may be NULL */
+		)
+{
+	typedef DNSServiceErrorType (DNSSD_API * Func)(DNSServiceRef*, DNSServiceFlags, uint32_t, DNSServiceProtocol, const char*, DNSServiceGetAddrInfoReply, void* );
+	static Func func = NULL;
+	DNSServiceErrorType ret = g_defaultErrorCode;
+
+	if ( DLLStub::GetProcAddress( ( FARPROC* ) &func, __FUNCTION__ ) )
+	{
+		ret = func( sdRef, flags, interfaceIndex, protocol, hostname, callBack, context );
+	}
+	
+	return ret;
+}
+
+
+DNSServiceErrorType DNSSD_API
+DNSServiceGetProperty
+		(
+		const char *property,  /* Requested property (i.e. kDNSServiceProperty_DaemonVersion) */
+		void       *result,    /* Pointer to place to store result */
+		uint32_t   *size       /* size of result location */
+		)
+{
+	typedef DNSServiceErrorType (DNSSD_API * Func)( const char*, void*, uint32_t* );
+	static Func func = NULL;
+	DNSServiceErrorType ret = g_defaultErrorCode;
+
+	if ( DLLStub::GetProcAddress( ( FARPROC* ) &func, __FUNCTION__ ) )
+	{
+		ret = func( property, result, size );
+	}
+	
+	return ret;
+}
+
+
+void DNSSD_API
+TXTRecordCreate
+		(
+		TXTRecordRef     *txtRecord,
+		uint16_t         bufferLen,
+		void             *buffer
+		)
+{
+	typedef void (DNSSD_API * Func)( TXTRecordRef*, uint16_t, void* );
+	static Func func = NULL;
+
+	if ( DLLStub::GetProcAddress( ( FARPROC* ) &func, __FUNCTION__ ) )
+	{
+		func( txtRecord, bufferLen, buffer );
+	}
+}
+
+
+void DNSSD_API
+TXTRecordDeallocate
+		(
+		TXTRecordRef     *txtRecord
+		)
+{
+	typedef void (DNSSD_API * Func)( TXTRecordRef* );
+	static Func func = NULL;
+
+	if ( DLLStub::GetProcAddress( ( FARPROC* ) &func, __FUNCTION__ ) )
+	{
+		func( txtRecord );
+	}
+}
+
+
+DNSServiceErrorType DNSSD_API
+TXTRecordSetValue
+		(
+		TXTRecordRef     *txtRecord,
+		const char       *key,
+		uint8_t          valueSize,        /* may be zero */
+		const void       *value            /* may be NULL */
+		)
+{
+	typedef DNSServiceErrorType (DNSSD_API * Func)( TXTRecordRef*, const char*, uint8_t, const void* );
+	static Func func = NULL;
+	DNSServiceErrorType ret = g_defaultErrorCode;
+
+	if ( DLLStub::GetProcAddress( ( FARPROC* ) &func, __FUNCTION__ ) )
+	{
+		ret = func( txtRecord, key, valueSize, value );
+	}
+	
+	return ret;
+}
+
+
+DNSServiceErrorType DNSSD_API
+TXTRecordRemoveValue
+		(
+		TXTRecordRef     *txtRecord,
+		const char       *key
+		)
+{
+	typedef DNSServiceErrorType (DNSSD_API * Func)( TXTRecordRef*, const char* );
+	static Func func = NULL;
+	DNSServiceErrorType ret = g_defaultErrorCode;
+
+	if ( DLLStub::GetProcAddress( ( FARPROC* ) &func, __FUNCTION__ ) )
+	{
+		ret = func( txtRecord, key );
+	}
+	
+	return ret;
+}
+
+
+int DNSSD_API
+TXTRecordContainsKey
+		(
+		uint16_t         txtLen,
+		const void       *txtRecord,
+		const char       *key
+		)
+{
+	typedef int (DNSSD_API * Func)( uint16_t, const void*, const char* );
+	static Func func = NULL;
+	int ret = 0;
+
+	if ( DLLStub::GetProcAddress( ( FARPROC* ) &func, __FUNCTION__ ) )
+	{
+		ret = func( txtLen, txtRecord, key );
+	}
+	
+	return ret;
+}
+
+
+uint16_t DNSSD_API
+TXTRecordGetCount
+		(
+		uint16_t         txtLen,
+		const void       *txtRecord
+		)
+{
+	typedef uint16_t (DNSSD_API * Func)( uint16_t, const void* );
+	static Func func = NULL;
+	uint16_t ret = 0;
+
+	if ( DLLStub::GetProcAddress( ( FARPROC* ) &func, __FUNCTION__ ) )
+	{
+		ret = func( txtLen, txtRecord );
+	}
+	
+	return ret;
+}
+
+
+uint16_t DNSSD_API
+TXTRecordGetLength
+		(
+		const TXTRecordRef *txtRecord
+		)
+{
+	typedef uint16_t (DNSSD_API * Func)( const TXTRecordRef* );
+	static Func func = NULL;
+	uint16_t ret = 0;
+
+	if ( DLLStub::GetProcAddress( ( FARPROC* ) &func, __FUNCTION__ ) )
+	{
+		ret = func( txtRecord );
+	}
+	
+	return ret;
+}
+
+
+const void * DNSSD_API
+TXTRecordGetBytesPtr
+		(
+		const TXTRecordRef *txtRecord
+		)
+{
+	typedef const void* (DNSSD_API * Func)( const TXTRecordRef* );
+	static Func func = NULL;
+	const void* ret = NULL;
+
+	if ( DLLStub::GetProcAddress( ( FARPROC* ) &func, __FUNCTION__ ) )
+	{
+		ret = func( txtRecord );
+	}
+	
+	return ret;
+}
+
+
+const void * DNSSD_API
+TXTRecordGetValuePtr
+		(
+		uint16_t         txtLen,
+		const void       *txtRecord,
+		const char       *key,
+		uint8_t          *valueLen
+		)
+{
+	typedef const void* (DNSSD_API * Func)( uint16_t, const void*, const char*, uint8_t* );
+	static Func func = NULL;
+	const void* ret = NULL;
+
+	if ( DLLStub::GetProcAddress( ( FARPROC* ) &func, __FUNCTION__ ) )
+	{
+		ret = func( txtLen, txtRecord, key, valueLen );
+	}
+	
+	return ret;
+}
+
+
+DNSServiceErrorType DNSSD_API
+TXTRecordGetItemAtIndex
+		(
+		uint16_t         txtLen,
+		const void       *txtRecord,
+		uint16_t         itemIndex,
+		uint16_t         keyBufLen,
+		char             *key,
+		uint8_t          *valueLen,
+		const void       **value
+		)
+{
+	typedef DNSServiceErrorType (DNSSD_API * Func)( uint16_t, const void*, uint16_t, uint16_t, char*, uint8_t*, const void** );
+	static Func func = NULL;
+	DNSServiceErrorType ret = g_defaultErrorCode;
+
+	if ( DLLStub::GetProcAddress( ( FARPROC* ) &func, __FUNCTION__ ) )
+	{
+		ret = func( txtLen, txtRecord, itemIndex, keyBufLen, key, valueLen, value );
+	}
+	
+	return ret;
+}
\ No newline at end of file
diff --git a/mdnsresponder/mDNSWindows/DLLStub/DLLStub.h b/mdnsresponder/mDNSWindows/DLLStub/DLLStub.h
new file mode 100755
index 0000000..44f57d1
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DLLStub/DLLStub.h
@@ -0,0 +1,52 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2009, Apple Computer, Inc. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are met:
+ *
+ * 1.  Redistributions of source code must retain the above copyright notice,
+ *     this list of conditions and the following disclaimer.
+ * 2.  Redistributions in binary form must reproduce the above copyright notice,
+ *     this list of conditions and the following disclaimer in the documentation
+ *     and/or other materials provided with the distribution.
+ * 3.  Neither the name of Apple Computer, Inc. ("Apple") nor the names of its
+ *     contributors may be used to endorse or promote products derived from this
+ *     software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY APPLE AND ITS CONTRIBUTORS "AS IS" AND ANY
+ * EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
+ * WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
+ * DISCLAIMED. IN NO EVENT SHALL APPLE OR ITS CONTRIBUTORS BE LIABLE FOR ANY
+ * DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
+ * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
+ * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
+ * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef _DLLStub_h
+#define _DLLStub_h
+
+#include <windows.h>
+#include <dns_sd.h>
+
+class DLLStub
+{
+public:
+
+	DLLStub();
+	~DLLStub();
+
+	static bool
+	GetProcAddress( FARPROC * func, LPCSTR lpProcName );
+
+private:
+
+	static DLLStub	*	m_instance;
+	HMODULE				m_library;
+};
+
+
+#endif
\ No newline at end of file
diff --git a/mdnsresponder/mDNSWindows/DLLStub/DLLStub.vcproj b/mdnsresponder/mDNSWindows/DLLStub/DLLStub.vcproj
new file mode 100755
index 0000000..dc68289
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DLLStub/DLLStub.vcproj
@@ -0,0 +1,324 @@
+<?xml version="1.0" encoding="Windows-1252"?>

+<VisualStudioProject

+	ProjectType="Visual C++"

+	Version="8.00"

+	Name="DLLStub"

+	ProjectGUID="{3A2B6325-3053-4236-84BD-AA9BE2E323E5}"

+	RootNamespace="DLLStub"

+	Keyword="Win32Proj"

+	>

+	<Platforms>

+		<Platform

+			Name="Win32"

+		/>

+		<Platform

+			Name="x64"

+		/>

+	</Platforms>

+	<ToolFiles>

+	</ToolFiles>

+	<Configurations>

+		<Configuration

+			Name="Debug|Win32"

+			OutputDirectory="$(PlatformName)\$(ConfigurationName)"

+			IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"

+			ConfigurationType="4"

+			UseOfATL="0"

+			CharacterSet="1"

+			WholeProgramOptimization="0"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				Optimization="0"

+				AdditionalIncludeDirectories="..\..\mDNSShared;..\..\mDNSWindows"

+				PreprocessorDefinitions="WIN32;_DEBUG;_LIB;WIN32_LEAN_AND_MEAN;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1"

+				MinimalRebuild="false"

+				BasicRuntimeChecks="3"

+				RuntimeLibrary="1"

+				UsePrecompiledHeader="0"

+				ProgramDataBaseFileName="$(IntDir)\dnssd.pdb"

+				WarningLevel="3"

+				Detect64BitPortabilityProblems="true"

+				DebugInformationFormat="3"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+			/>

+			<Tool

+				Name="VCLibrarianTool"

+				OutputFile="$(OutDir)\dnssdStatic.lib"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+			/>

+		</Configuration>

+		<Configuration

+			Name="Debug|x64"

+			OutputDirectory="$(PlatformName)\$(ConfigurationName)"

+			IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"

+			ConfigurationType="4"

+			UseOfATL="0"

+			CharacterSet="1"

+			WholeProgramOptimization="0"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+				TargetEnvironment="3"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				Optimization="0"

+				AdditionalIncludeDirectories="..\..\mDNSShared;..\..\mDNSWindows"

+				PreprocessorDefinitions="WIN32;_DEBUG;_LIB;WIN32_LEAN_AND_MEAN;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1"

+				MinimalRebuild="false"

+				BasicRuntimeChecks="3"

+				RuntimeLibrary="1"

+				UsePrecompiledHeader="0"

+				ProgramDataBaseFileName="$(IntDir)\dnssd.pdb"

+				WarningLevel="3"

+				Detect64BitPortabilityProblems="true"

+				DebugInformationFormat="3"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+			/>

+			<Tool

+				Name="VCLibrarianTool"

+				OutputFile="$(OutDir)\dnssdStatic.lib"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+			/>

+		</Configuration>

+		<Configuration

+			Name="Release|Win32"

+			OutputDirectory="$(PlatformName)\$(ConfigurationName)"

+			IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"

+			ConfigurationType="4"

+			UseOfATL="0"

+			CharacterSet="1"

+			WholeProgramOptimization="0"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				AdditionalIncludeDirectories="..\..\mDNSShared;..\..\mDNSWindows"

+				PreprocessorDefinitions="WIN32;NDEBUG;_LIB;WIN32_LEAN_AND_MEAN;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1"

+				RuntimeLibrary="0"

+				UsePrecompiledHeader="0"

+				ProgramDataBaseFileName="$(IntDir)\dnssd.lib.pdb"

+				WarningLevel="3"

+				Detect64BitPortabilityProblems="true"

+				DebugInformationFormat="3"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+			/>

+			<Tool

+				Name="VCLibrarianTool"

+				OutputFile="$(OutDir)\dnssdStatic.lib"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+				CommandLine="if not &quot;%RC_XBS%&quot; == &quot;YES&quot; goto END&#x0D;&#x0A;if not exist &quot;$(DSTROOT)\Program Files\Bonjour SDK\lib\$(PlatformName)&quot; mkdir &quot;$(DSTROOT)\Program Files\Bonjour SDK\lib\$(PlatformName)&quot;&#x0D;&#x0A;echo F | xcopy /Y &quot;$(OutDir)\dnssdStatic.lib&quot;                                                        &quot;$(DSTROOT)\Program Files\Bonjour SDK\lib\$(PlatformName)\dnssd.lib&quot;&#x0D;&#x0A;xcopy /Y &quot;$(OutDir)\dnssd.lib.pdb&quot;                                                                         &quot;$(DSTROOT)\Program Files\Bonjour SDK\lib\$(PlatformName)&quot;&#x0D;&#x0A;:END&#x0D;&#x0A;"

+			/>

+		</Configuration>

+		<Configuration

+			Name="Release|x64"

+			OutputDirectory="$(PlatformName)\$(ConfigurationName)"

+			IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"

+			ConfigurationType="4"

+			UseOfATL="0"

+			CharacterSet="1"

+			WholeProgramOptimization="0"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+				TargetEnvironment="3"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				AdditionalIncludeDirectories="..\..\mDNSShared;..\..\mDNSWindows"

+				PreprocessorDefinitions="WIN32;NDEBUG;_LIB;WIN32_LEAN_AND_MEAN;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1"

+				RuntimeLibrary="0"

+				UsePrecompiledHeader="0"

+				ProgramDataBaseFileName="$(IntDir)\dnssd.lib.pdb"

+				WarningLevel="3"

+				Detect64BitPortabilityProblems="true"

+				DebugInformationFormat="3"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+			/>

+			<Tool

+				Name="VCLibrarianTool"

+				OutputFile="$(OutDir)\dnssdStatic.lib"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+				CommandLine="if not &quot;%RC_XBS%&quot; == &quot;YES&quot; goto END&#x0D;&#x0A;if not exist &quot;$(DSTROOT)\Program Files\Bonjour SDK\lib\$(PlatformName)&quot; mkdir &quot;$(DSTROOT)\Program Files\Bonjour SDK\lib\$(PlatformName)&quot;&#x0D;&#x0A;echo F | xcopy /I/Y &quot;$(OutDir)\dnssdStatic.lib&quot;                                                     &quot;$(DSTROOT)\Program Files\Bonjour SDK\lib\$(PlatformName)\dnssd.lib&quot;&#x0D;&#x0A;xcopy /Y &quot;$(OutDir)\dnssd.lib.pdb&quot;                                                                         &quot;$(DSTROOT)\Program Files\Bonjour SDK\lib\$(PlatformName)&quot;&#x0D;&#x0A;:END&#x0D;&#x0A;"

+			/>

+		</Configuration>

+	</Configurations>

+	<References>

+	</References>

+	<Files>

+		<Filter

+			Name="Source Files"

+			Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"

+			UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"

+			>

+			<File

+				RelativePath=".\DLLStub.cpp"

+				>

+			</File>

+		</Filter>

+		<Filter

+			Name="Header Files"

+			Filter="h;hpp;hxx;hm;inl;inc;xsd"

+			UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"

+			>

+			<File

+				RelativePath=".\DLLStub.h"

+				>

+			</File>

+		</Filter>

+		<Filter

+			Name="Resource Files"

+			Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"

+			UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"

+			>

+		</Filter>

+		<File

+			RelativePath=".\ReadMe.txt"

+			>

+		</File>

+	</Files>

+	<Globals>

+	</Globals>

+</VisualStudioProject>

diff --git a/mdnsresponder/mDNSWindows/DLLX/DLLX.cpp b/mdnsresponder/mDNSWindows/DLLX/DLLX.cpp
new file mode 100755
index 0000000..77883cc
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DLLX/DLLX.cpp
@@ -0,0 +1,208 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2009 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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 "stdafx.h"
+
+#include "resource.h"
+
+#include "DLLX.h"
+
+#include "dlldatax.h"
+
+#include <DebugServices.h>
+
+
+
+
+
+class CDLLComponentModule : public CAtlDllModuleT< CDLLComponentModule >
+
+{
+
+public :
+
+	DECLARE_LIBID(LIBID_Bonjour)
+
+	DECLARE_REGISTRY_APPID_RESOURCEID(IDR_DLLX, "{56608F9C-223B-4CB6-813D-85EDCCADFB4B}")
+
+};
+
+
+
+CDLLComponentModule _AtlModule;
+
+
+
+
+
+#ifdef _MANAGED
+
+#pragma managed(push, off)
+
+#endif
+
+
+
+// DLL Entry Point
+
+extern "C" BOOL WINAPI DllMain(HINSTANCE hInstance, DWORD dwReason, LPVOID lpReserved)
+
+{
+
+	debug_initialize( kDebugOutputTypeWindowsDebugger );
+	debug_set_property( kDebugPropertyTagPrintLevel, kDebugLevelVerbose );
+
+
+
+#ifdef _MERGE_PROXYSTUB
+
+    if (!PrxDllMain(hInstance, dwReason, lpReserved))
+
+        return FALSE;
+
+#endif
+
+	hInstance;
+
+    return _AtlModule.DllMain(dwReason, lpReserved); 
+
+}
+
+
+
+#ifdef _MANAGED
+
+#pragma managed(pop)
+
+#endif
+
+
+
+
+
+
+
+
+
+// Used to determine whether the DLL can be unloaded by OLE
+
+STDAPI DllCanUnloadNow(void)
+
+{
+
+#ifdef _MERGE_PROXYSTUB
+
+    HRESULT hr = PrxDllCanUnloadNow();
+
+    if (hr != S_OK)
+
+        return hr;
+
+#endif
+
+    return _AtlModule.DllCanUnloadNow();
+
+}
+
+
+
+
+
+// Returns a class factory to create an object of the requested type
+
+STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv)
+
+{
+
+#ifdef _MERGE_PROXYSTUB
+
+    if (PrxDllGetClassObject(rclsid, riid, ppv) == S_OK)
+
+        return S_OK;
+
+#endif
+
+    return _AtlModule.DllGetClassObject(rclsid, riid, ppv);
+
+}
+
+
+
+
+
+// DllRegisterServer - Adds entries to the system registry
+
+STDAPI DllRegisterServer(void)
+
+{
+
+    // registers object, typelib and all interfaces in typelib
+
+    HRESULT hr = _AtlModule.DllRegisterServer();
+
+#ifdef _MERGE_PROXYSTUB
+
+    if (FAILED(hr))
+
+        return hr;
+
+    hr = PrxDllRegisterServer();
+
+#endif
+
+	return hr;
+
+}
+
+
+
+
+
+// DllUnregisterServer - Removes entries from the system registry
+
+STDAPI DllUnregisterServer(void)
+
+{
+
+	HRESULT hr = _AtlModule.DllUnregisterServer();
+
+#ifdef _MERGE_PROXYSTUB
+
+    if (FAILED(hr))
+
+        return hr;
+
+    hr = PrxDllRegisterServer();
+
+    if (FAILED(hr))
+
+        return hr;
+
+    hr = PrxDllUnregisterServer();
+
+#endif
+
+	return hr;
+
+}
+
+
+
diff --git a/mdnsresponder/mDNSWindows/DLLX/DLLX.def b/mdnsresponder/mDNSWindows/DLLX/DLLX.def
new file mode 100755
index 0000000..698b172
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DLLX/DLLX.def
@@ -0,0 +1,35 @@
+; -*- Mode: C; tab-width: 4 -*-
+;
+; Copyright (c) 2009 Apple Computer, Inc. All rights reserved.
+;
+; Licensed 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.
+;
+
+
+
+
+
+LIBRARY      "dnssdX.DLL"
+
+
+
+EXPORTS
+
+	DllCanUnloadNow		PRIVATE
+
+	DllGetClassObject	PRIVATE
+
+	DllRegisterServer	PRIVATE
+
+	DllUnregisterServer	PRIVATE
+
diff --git a/mdnsresponder/mDNSWindows/DLLX/DLLX.idl b/mdnsresponder/mDNSWindows/DLLX/DLLX.idl
new file mode 100755
index 0000000..475558e
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DLLX/DLLX.idl
@@ -0,0 +1,491 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2009 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+
+
+// This file will be processed by the MIDL tool to
+
+// produce the type library (DLLComponent.tlb) and marshalling code.
+
+
+
+typedef [ uuid(4085DD59-D0E1-4efe-B6EE-DDBF7631B9C0) ]
+
+enum DNSSDFlags
+
+{
+
+	kDNSSDFlagsMoreComing			= 0x0001,
+
+	kDNSSDFlagsDefault				= 0x0004,
+
+	kDNSSDFlagsNoAutoRename			= 0x0008,
+
+	kDNSSDFlagsShared				= 0x0010,
+
+	kDNSSDFlagsUnique				= 0x0020,
+
+	kDNSSDFlagsBrowseDomains		= 0x0040,
+
+	kDNSSDFlagsRegistrationDomains	= 0x0080,
+
+	kDNSSDFlagsLongLivedQuery		= 0x0100,
+
+	kDNSSDFlagsAllowRemoteQuery		= 0x0200,
+
+	kDNSSDFlagsForceMulticast		= 0x0400,
+
+	kDNSSDFlagsForce				= 0x0800,
+
+	kDNSSDFlagsReturnIntermediates	= 0x1000,
+
+	kDNSSDFlagsNonBrowsable			= 0x2000
+
+} DNSSDFlags;
+
+
+
+
+
+typedef [ uuid(30CDF335-CA52-4b17-AFF2-E83C64C450D4) ]
+
+enum DNSSDAddressFamily
+
+{
+
+	kDNSSDAddressFamily_IPv4 = 0x1,
+
+	kDNSSDAddressFamily_IPv6 = 0x2
+
+} DNSSDAddressFamily;
+
+
+
+
+
+typedef [ uuid(98FB4702-7374-4b16-A8DB-AD35BFB8364D) ]
+
+enum DNSSDProtocol
+
+{
+
+	kDNSSDProtocol_UDP	= 0x10,
+
+	kDNSSDProtocol_TCP	= 0x20
+
+} DNSSDProtocol;
+
+
+
+
+
+typedef [ uuid(72BF3EC3-19BC-47e5-8D95-3B73FF37D893) ]
+
+enum DNSSDRRClass
+
+{
+
+	kDNSSDClass_IN = 1
+
+} DNSSDRRClass;
+
+
+
+
+
+typedef [ uuid(08E362DF-5468-4c9a-AC66-FD4747B917BD) ]
+
+enum DNSSDRRType
+
+{
+
+	kDNSSDType_A         = 1,
+    kDNSSDType_NS        = 2,
+    kDNSSDType_MD        = 3,
+    kDNSSDType_MF        = 4,
+    kDNSSDType_CNAME     = 5,
+    kDNSSDType_SOA       = 6,
+    kDNSSDType_MB        = 7,
+    kDNSSDType_MG        = 8,
+    kDNSSDType_MR        = 9,
+    kDNSSDType_NULL      = 10,
+    kDNSSDType_WKS       = 11,
+    kDNSSDType_PTR       = 12,
+    kDNSSDType_HINFO     = 13,
+    kDNSSDType_MINFO     = 14,
+    kDNSSDType_MX        = 15,
+    kDNSSDType_TXT       = 16,
+    kDNSSDType_RP        = 17,
+    kDNSSDType_AFSDB     = 18,
+    kDNSSDType_X25       = 19,
+    kDNSSDType_ISDN      = 20,
+    kDNSSDType_RT        = 21,
+    kDNSSDType_NSAP      = 22,
+    kDNSSDType_NSAP_PTR  = 23,
+    kDNSSDType_SIG       = 24,
+    kDNSSDType_KEY       = 25,
+    kDNSSDType_PX        = 26,
+    kDNSSDType_GPOS      = 27,
+    kDNSSDType_AAAA      = 28,
+    kDNSSDType_LOC       = 29,
+    kDNSSDType_NXT       = 30,
+    kDNSSDType_EID       = 31,
+    kDNSSDType_NIMLOC    = 32,
+    kDNSSDType_SRV       = 33,
+    kDNSSDType_ATMA      = 34,
+    kDNSSDType_NAPTR     = 35,
+    kDNSSDType_KX        = 36,
+    kDNSSDType_CERT      = 37,
+    kDNSSDType_A6        = 38,
+    kDNSSDType_DNAME     = 39,
+    kDNSSDType_SINK      = 40,
+    kDNSSDType_OPT       = 41,
+    kDNSSDType_APL       = 42,
+    kDNSSDType_DS        = 43,
+    kDNSSDType_SSHFP     = 44,
+    kDNSSDType_IPSECKEY  = 45,
+    kDNSSDType_RRSIG     = 46,
+    kDNSSDType_NSEC      = 47,
+    kDNSSDType_DNSKEY    = 48,
+    kDNSSDType_DHCID     = 49,
+    kDNSSDType_NSEC3     = 50,
+    kDNSSDType_NSEC3PARAM= 51,
+    kDNSSDType_HIP       = 55,
+    kDNSSDType_SPF       = 99,
+    kDNSSDType_UINFO     = 100,
+    kDNSSDType_UID       = 101,
+    kDNSSDType_GID       = 102,
+    kDNSSDType_UNSPEC    = 103,
+    kDNSSDType_TKEY      = 249,
+    kDNSSDType_TSIG      = 250,
+    kDNSSDType_IXFR      = 251,
+    kDNSSDType_AXFR      = 252,
+    kDNSSDType_MAILB     = 253,
+    kDNSSDType_MAILA     = 254,
+    kDNSSDType_ANY       = 255
+
+} DNSSDRRType;
+
+
+
+
+
+typedef [ uuid(3B0059E7-5297-4301-9AAB-1522F31EC8A7) ]
+
+enum DNSSDError
+{
+	kDNSSDError_NoError                   = 0,
+	kDNSSDError_Unknown                   = -65537,
+	kDNSSDError_NoSuchName                = -65538,
+    kDNSSDError_NoMemory                  = -65539,
+    kDNSSDError_BadParam                  = -65540,
+    kDNSSDError_BadReference              = -65541,
+    kDNSSDError_BadState                  = -65542,
+    kDNSSDError_BadFlags                  = -65543,
+    kDNSSDError_Unsupported               = -65544,
+    kDNSSDError_NotInitialized            = -65545,
+    kDNSSDError_AlreadyRegistered         = -65547,
+    kDNSSDError_NameConflict              = -65548,
+    kDNSSDError_Invalid                   = -65549,
+    kDNSSDError_Firewall                  = -65550,
+    kDNSSDError_Incompatible              = -65551,
+    kDNSSDError_BadInterfaceIndex         = -65552,
+    kDNSSDError_Refused                   = -65553,
+    kDNSSDError_NoSuchRecord              = -65554,
+    kDNSSDError_NoAuth                    = -65555,
+    kDNSSDError_NoSuchKey                 = -65556,
+    kDNSSDError_NATTraversal              = -65557,
+    kDNSSDError_DoubleNAT                 = -65558,
+    kDNSSDError_BadTime                   = -65559,
+    kDNSSDError_BadSig                    = -65560,
+    kDNSSDError_BadKey                    = -65561,
+    kDNSSDError_Transient                 = -65562,
+    kDNSSDError_ServiceNotRunning         = -65563,  /* Background daemon not running */
+    kDNSSDError_NATPortMappingUnsupported = -65564,  /* NAT doesn't support NAT-PMP or UPnP */
+    kDNSSDError_NATPortMappingDisabled    = -65565,  /* NAT supports NAT-PMP or UPnP but it's disabled by the administrator */
+    kDNSSDError_NoRouter                  = -65566,  /* No router currently configured (probably no network connectivity) */
+    kDNSSDError_PollingMode               = -65567
+} DNSSDError;
+
+
+
+import "oaidl.idl";
+
+import "ocidl.idl";
+
+
+
+
+
+[
+
+	object,
+
+	uuid(8FA0889C-5973-4FC9-970B-EC15C925D0CE),
+
+	dual,
+
+	nonextensible,
+
+	helpstring("ITXTRecord Interface"),
+
+	pointer_default(unique)
+
+]
+
+interface ITXTRecord : IDispatch{
+
+	[id(1), helpstring("method SetValue")] HRESULT SetValue([in] BSTR key, [in] VARIANT value);
+
+	[id(2), helpstring("method RemoveValue")] HRESULT RemoveValue([in] BSTR key);
+
+	[id(3), helpstring("method ContainsKey")] HRESULT ContainsKey([in] BSTR key, [out,retval] VARIANT_BOOL* retval);
+
+	[id(4), helpstring("method GetValueForKey")] HRESULT GetValueForKey([in] BSTR key, [out,retval] VARIANT* value);
+
+	[id(5), helpstring("method GetCount")] HRESULT GetCount([out,retval] ULONG* count);
+
+	[id(6), helpstring("method GetKeyAtIndex")] HRESULT GetKeyAtIndex([in] ULONG index, [out,retval] BSTR* retval);
+
+	[id(7), helpstring("method GetValueAtIndex")] HRESULT GetValueAtIndex([in] ULONG index, [out,retval] VARIANT* retval);
+
+};
+
+[
+
+	object,
+
+	uuid(9CE603A0-3365-4DA0-86D1-3F780ECBA110),
+
+	dual,
+
+	nonextensible,
+
+	helpstring("IDNSSDRecord Interface"),
+
+	pointer_default(unique)
+
+]
+
+interface IDNSSDRecord : IDispatch{
+
+	[id(1), helpstring("method Update")] HRESULT Update([in] DNSSDFlags flags, [in] VARIANT rdata, [in] ULONG ttl);
+
+	[id(2), helpstring("method Remove")] HRESULT Remove([in] DNSSDFlags flags);
+
+};
+
+[
+
+	object,
+
+	uuid(7FD72324-63E1-45AD-B337-4D525BD98DAD),
+
+	dual,
+
+	nonextensible,
+
+	helpstring("IDNSSDEventManager Interface"),
+
+	pointer_default(unique)
+
+]
+
+interface IDNSSDEventManager : IDispatch{
+
+};
+
+[
+
+	object,
+
+	uuid(29DE265F-8402-474F-833A-D4653B23458F),
+
+	dual,
+
+	nonextensible,
+
+	helpstring("IDNSSDService Interface"),
+
+	pointer_default(unique)
+
+]
+
+interface IDNSSDService : IDispatch{
+
+	[id(1), helpstring("method EnumerateDomains")] HRESULT EnumerateDomains([in] DNSSDFlags flags, [in] ULONG ifIndex, [in] IDNSSDEventManager* eventManager, [out,retval] IDNSSDService** service);
+
+	[id(2), helpstring("method Browse"), local] HRESULT Browse([in] DNSSDFlags flags, [in] ULONG interfaceIndex, [in] BSTR regtype, [in] BSTR domain, [in] IDNSSDEventManager* eventManager, [out,retval] IDNSSDService** sdref);
+
+	[id(3), helpstring("method Resolve")] HRESULT Resolve([in] DNSSDFlags flags, [in] ULONG ifIndex, [in] BSTR serviceName, [in] BSTR regType, [in] BSTR domain, [in] IDNSSDEventManager* eventManager, [out,retval] IDNSSDService** service);
+
+	[id(4), helpstring("method Register")] HRESULT Register([in] DNSSDFlags flags, [in] ULONG ifIndex, [in] BSTR name, [in] BSTR regType, [in] BSTR domain, [in] BSTR host, [in] USHORT port, [in] ITXTRecord* record, [in] IDNSSDEventManager* eventManager, [out,retval] IDNSSDService** service);
+
+	[id(5), helpstring("method QueryRecord")] HRESULT QueryRecord([in] DNSSDFlags flags, [in] ULONG ifIndex, [in] BSTR fullname, [in] DNSSDRRType rrtype, [in] DNSSDRRClass rrclass, [in] IDNSSDEventManager* eventManager, [out,retval] IDNSSDService** service);
+
+	[id(6), helpstring("method RegisterRecord")] HRESULT RegisterRecord([in] DNSSDFlags flags, [in] ULONG ifIndex, [in] BSTR fullname, [in] DNSSDRRType rrtype, [in] DNSSDRRClass rrclass, [in] VARIANT rdata, [in] ULONG ttl, [in] IDNSSDEventManager* eventManager, [out,retval] IDNSSDRecord** record);
+
+	[id(7), helpstring("method AddRecord")] HRESULT AddRecord([in] DNSSDFlags flags, [in] DNSSDRRType rrtype, [in] VARIANT rdata, [in] ULONG ttl, [out,retval] IDNSSDRecord** record);
+
+	[id(8), helpstring("method ReconfirmRecord")] HRESULT ReconfirmRecord([in] DNSSDFlags flags, [in] ULONG ifIndex, [in] BSTR fullname, [in] DNSSDRRType rrtype, [in] DNSSDRRClass rrclass, [in] VARIANT rdata);
+
+	[id(9), helpstring("method GetProperty")] HRESULT GetProperty([in] BSTR prop, [in,out] VARIANT * value );	
+
+	[id(10), helpstring("method GetAddrInfo")] HRESULT GetAddrInfo([in] DNSSDFlags flags, [in] ULONG ifIndex, [in] DNSSDAddressFamily addressFamily, [in] BSTR hostname, [in] IDNSSDEventManager* eventManager, [out,retval] IDNSSDService** service);
+
+	[id(11), helpstring("method NATPortMappingCreate")] HRESULT NATPortMappingCreate([in] DNSSDFlags flags, [in] ULONG ifIndex, [in] DNSSDAddressFamily addressFamily, [in] DNSSDProtocol protocol, [in] USHORT internalPort, [in] USHORT externalPort, [in] ULONG ttl, [in] IDNSSDEventManager* eventManager, [out,retval] IDNSSDService** service);
+
+	[id(12), helpstring("method Stop"), local] HRESULT Stop(void);
+
+};
+
+[
+
+	uuid(18FBED6D-F2B7-4EC8-A4A4-46282E635308),
+
+	version(1.0),
+
+	helpstring("Apple Bonjour Library 1.0")
+
+]
+
+library Bonjour
+
+{
+
+	importlib("stdole2.tlb");
+
+	[
+
+		uuid(21AE8D7F-D5FE-45cf-B632-CFA2C2C6B498),
+
+		helpstring("_IDNSSDEvents Interface")
+
+	]
+
+	dispinterface _IDNSSDEvents
+
+	{
+
+		properties:
+
+		methods:
+
+		[id(1), helpstring("method DomainFound")] void DomainFound([in] IDNSSDService* service, [in] DNSSDFlags flags, [in] ULONG ifIndex, [in] BSTR domain);
+
+		[id(2), helpstring("method DomainLost")] void DomainLost([in] IDNSSDService* service, [in] DNSSDFlags flags, [in] ULONG ifIndex, [in] BSTR domain);
+
+		[id(3), helpstring("method ServiceFound")] void ServiceFound([in] IDNSSDService* browser, [in] DNSSDFlags flags, [in] ULONG ifIndex, [in] BSTR serviceName, [in] BSTR regType, [in] BSTR domain);
+
+		[id(4), helpstring("method ServiceLost")] void ServiceLost([in] IDNSSDService* browser, [in] DNSSDFlags flags, [in] ULONG ifIndex, [in] BSTR serviceName, [in] BSTR regType, [in] BSTR domain);
+
+		[id(5), helpstring("method ServiceResolved")] void ServiceResolved([in] IDNSSDService* service, [in] DNSSDFlags flags, [in] ULONG ifIndex, [in] BSTR fullName, [in] BSTR hostName, [in] USHORT port, [in] ITXTRecord* record);
+
+		[id(6), helpstring("method ServiceRegistered")] void ServiceRegistered([in] IDNSSDService* service, [in] DNSSDFlags flags, [in] BSTR name, [in] BSTR regType, [in] BSTR domain);
+
+		[id(7), helpstring("method QueryRecordAnswered")] void QueryRecordAnswered([in] IDNSSDService* service, [in] DNSSDFlags flags, [in] ULONG ifIndex, [in] BSTR fullName, [in] DNSSDRRType rrtype, [in] DNSSDRRClass rrclass, [in] VARIANT rdata, [in] ULONG ttl);
+
+		[id(8), helpstring("method RecordRegistered")] void RecordRegistered([in] IDNSSDRecord* record, [in] DNSSDFlags flags);
+
+		[id(9), helpstring("method AddressFound")] void AddressFound([in] IDNSSDService* service, [in] DNSSDFlags flags, [in] ULONG ifIndex, [in] BSTR hostname, [in] DNSSDAddressFamily addressFamily, [in] BSTR address, [in] ULONG ttl);
+
+		[id(10), helpstring("method MappingCreated")] void MappingCreated([in] IDNSSDService* service, [in] DNSSDFlags flags, [in] ULONG ifIndex, [in] ULONG externalAddress, [in] DNSSDAddressFamily addressFamily, [in] DNSSDProtocol protocol, [in] USHORT internalPort, [in] USHORT externalPort, [in] ULONG ttl);
+
+		[id(11), helpstring("method OperationFailed")] void OperationFailed([in] IDNSSDService* service, [in] DNSSDError error);
+
+	};
+
+	[
+
+		uuid(24CD4DE9-FF84-4701-9DC1-9B69E0D1090A),
+
+		helpstring("DNSSDService Class")
+
+	]
+
+	coclass DNSSDService
+
+	{
+
+		[default] interface IDNSSDService;
+
+	};
+
+	[
+
+		uuid(AFEE063C-05BA-4248-A26E-168477F49734),
+
+		helpstring("TXTRecord Class")
+
+	]
+
+	coclass TXTRecord
+
+	{
+
+		[default] interface ITXTRecord;
+
+	};
+
+	[
+
+		uuid(5E93C5A9-7516-4259-A67B-41A656F6E01C),
+
+		helpstring("DNSSDRecord Class")
+
+	]
+
+	coclass DNSSDRecord
+
+	{
+
+		[default] interface IDNSSDRecord;
+
+	};
+
+	[
+
+		uuid(BEEB932A-8D4A-4619-AEFE-A836F988B221),
+
+		helpstring("DNSSDEventManager Class")
+
+	]
+
+	coclass DNSSDEventManager
+
+	{
+
+		[default] interface IDNSSDEventManager;
+
+		[default, source] dispinterface _IDNSSDEvents;
+
+	};
+
+	enum DNSSDFlags;
+
+	enum DNSSDAddressFamily;
+
+	enum DNSSDProtocol;
+
+	enum DNSSDRRClass;
+
+	enum DNSSDRRType;
+
+	enum DNSSDError;
+
+};
+
diff --git a/mdnsresponder/mDNSWindows/DLLX/DLLX.rc b/mdnsresponder/mDNSWindows/DLLX/DLLX.rc
new file mode 100755
index 0000000..4299b5a
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DLLX/DLLX.rc
@@ -0,0 +1,126 @@
+// Microsoft Visual C++ generated resource script.

+//

+#include "resource.h"

+

+#define APSTUDIO_READONLY_SYMBOLS

+/////////////////////////////////////////////////////////////////////////////

+//

+// Generated from the TEXTINCLUDE 2 resource.

+//

+#include "winres.h"

+#include "WinVersRes.h"

+

+/////////////////////////////////////////////////////////////////////////////

+#undef APSTUDIO_READONLY_SYMBOLS

+

+/////////////////////////////////////////////////////////////////////////////

+// English (U.S.) resources

+

+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)

+#ifdef _WIN32

+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US

+#pragma code_page(1252)

+#endif //_WIN32

+

+#ifdef APSTUDIO_INVOKED

+/////////////////////////////////////////////////////////////////////////////

+//

+// TEXTINCLUDE

+//

+

+1 TEXTINCLUDE 

+BEGIN

+    "resource.h\0"

+END

+

+2 TEXTINCLUDE 

+BEGIN

+    "#include ""winres.h""\r\n"

+    "#include ""WinVersRes.h""\r\n"

+    "\0"

+END

+

+3 TEXTINCLUDE 

+BEGIN

+    "1 TYPELIB ""BonjourLib.tlb""\r\n"

+    "\0"

+END

+

+#endif    // APSTUDIO_INVOKED

+

+

+/////////////////////////////////////////////////////////////////////////////

+//

+// Version

+//

+

+VS_VERSION_INFO VERSIONINFO

+ FILEVERSION MASTER_PROD_VERS

+ PRODUCTVERSION MASTER_PROD_VERS

+ FILEFLAGSMASK 0x3fL

+#ifdef _DEBUG

+ FILEFLAGS 0x1L

+#else

+ FILEFLAGS 0x0L

+#endif

+ FILEOS 0x4L

+ FILETYPE 0x2L

+ FILESUBTYPE 0x0L

+BEGIN

+    BLOCK "StringFileInfo"

+    BEGIN

+        BLOCK "040904e4"

+        BEGIN

+            VALUE "CompanyName", MASTER_COMPANY_NAME

+            VALUE "FileDescription", "Bonjour COM Component Library"

+            VALUE "FileVersion", MASTER_PROD_VERS_STR

+            VALUE "LegalCopyright", MASTER_LEGAL_COPYRIGHT

+            VALUE "InternalName", "dnssdX.dll"

+            VALUE "OriginalFilename", "dnssdX.dll"

+            VALUE "ProductName", MASTER_PROD_NAME

+            VALUE "ProductVersion", MASTER_PROD_VERS_STR

+        END

+    END

+    BLOCK "VarFileInfo"

+    BEGIN

+        VALUE "Translation", 0x409, 1252

+    END

+END

+

+

+/////////////////////////////////////////////////////////////////////////////

+//

+// REGISTRY

+//

+

+IDR_DLLX		        REGISTRY                "DLLX.rgs"

+IDR_DNSSDSERVICE        REGISTRY                "DNSSDService.rgs"

+IDR_TXTRECORD           REGISTRY                "TXTRecord.rgs"

+IDR_DNSSDRECORD         REGISTRY                "DNSSDRecord.rgs"

+IDR_DNSSDEVENTMANAGER   REGISTRY                "DNSSDEventManager.rgs"

+

+/////////////////////////////////////////////////////////////////////////////

+//

+// String Table

+//

+

+STRINGTABLE 

+BEGIN

+    IDS_PROJNAME            "BonjourLib"

+END

+

+#endif    // English (U.S.) resources

+/////////////////////////////////////////////////////////////////////////////

+

+

+

+#ifndef APSTUDIO_INVOKED

+/////////////////////////////////////////////////////////////////////////////

+//

+// Generated from the TEXTINCLUDE 3 resource.

+//

+1 TYPELIB "dnssdX.tlb"

+

+/////////////////////////////////////////////////////////////////////////////

+#endif    // not APSTUDIO_INVOKED

+

diff --git a/mdnsresponder/mDNSWindows/DLLX/DLLX.rgs b/mdnsresponder/mDNSWindows/DLLX/DLLX.rgs
new file mode 100755
index 0000000..c55ee8d
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DLLX/DLLX.rgs
@@ -0,0 +1,11 @@
+HKCR

+{

+	NoRemove AppID

+	{

+		'%APPID%' = s 'Bonjour'

+		'Bonjour.DLL'

+		{

+			val AppID = s '%APPID%'

+		}

+	}

+}

diff --git a/mdnsresponder/mDNSWindows/DLLX/DLLX.vcproj b/mdnsresponder/mDNSWindows/DLLX/DLLX.vcproj
new file mode 100755
index 0000000..7c58b0a
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DLLX/DLLX.vcproj
@@ -0,0 +1,625 @@
+<?xml version="1.0" encoding="Windows-1252"?>

+<VisualStudioProject

+	ProjectType="Visual C++"

+	Version="8.00"

+	Name="DLLX"

+	ProjectGUID="{78FBFCC5-2873-4AE2-9114-A08082F71124}"

+	RootNamespace="DLLX"

+	Keyword="AtlProj"

+	>

+	<Platforms>

+		<Platform

+			Name="Win32"

+		/>

+		<Platform

+			Name="x64"

+		/>

+	</Platforms>

+	<ToolFiles>

+	</ToolFiles>

+	<Configurations>

+		<Configuration

+			Name="Debug|Win32"

+			OutputDirectory="$(PlatformName)\$(ConfigurationName)"

+			IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"

+			ConfigurationType="2"

+			UseOfMFC="0"

+			UseOfATL="1"

+			ATLMinimizesCRunTimeLibraryUsage="false"

+			CharacterSet="1"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+				PreprocessorDefinitions="_DEBUG"

+				MkTypLibCompatible="false"

+				TargetEnvironment="1"

+				GenerateStublessProxies="true"

+				TypeLibraryName="$(IntDir)/dnssdX.tlb"

+				HeaderFileName="DLLX.h"

+				DLLDataFileName=""

+				InterfaceIdentifierFileName="DLLX_i.c"

+				ProxyFileName="DLLX_p.c"

+				ValidateParameters="false"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				Optimization="0"

+				AdditionalIncludeDirectories="..\..\mDNSShared;..\..\mDNSWindows"

+				PreprocessorDefinitions="WIN32;_WINDOWS;_DEBUG;_USRDLL;_MERGE_PROXYSTUB;DEBUG=1;WIN32_LEAN_AND_MEAN;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1"

+				MinimalRebuild="true"

+				BasicRuntimeChecks="3"

+				RuntimeLibrary="1"

+				UsePrecompiledHeader="0"

+				WarningLevel="3"

+				Detect64BitPortabilityProblems="true"

+				DebugInformationFormat="3"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+				PreprocessorDefinitions="_DEBUG"

+				Culture="1033"

+				AdditionalIncludeDirectories="..;&quot;$(IntDir)&quot;"

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				RegisterOutput="true"

+				IgnoreImportLibrary="true"

+				AdditionalOptions="/NXCOMPAT /DYNAMICBASE /SAFESEH"

+				AdditionalDependencies="ws2_32.lib ../../mDNSWindows/DLLStub/$(PlatformName)/$(ConfigurationName)/dnssdStatic.lib"

+				OutputFile="$(OutDir)\dnssdX.dll"

+				LinkIncremental="2"

+				ModuleDefinitionFile=".\DLLX.def"

+				GenerateDebugInformation="true"

+				SubSystem="2"

+				TargetMachine="1"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+			/>

+		</Configuration>

+		<Configuration

+			Name="Debug|x64"

+			OutputDirectory="$(PlatformName)\$(ConfigurationName)"

+			IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"

+			ConfigurationType="2"

+			UseOfMFC="0"

+			UseOfATL="1"

+			ATLMinimizesCRunTimeLibraryUsage="false"

+			CharacterSet="1"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+				PreprocessorDefinitions="_DEBUG"

+				MkTypLibCompatible="false"

+				TargetEnvironment="3"

+				GenerateStublessProxies="true"

+				TypeLibraryName="$(IntDir)/dnssdX.tlb"

+				HeaderFileName="DLLX.h"

+				DLLDataFileName=""

+				InterfaceIdentifierFileName="DLLX_i.c"

+				ProxyFileName="DLLX_p.c"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				Optimization="0"

+				AdditionalIncludeDirectories="..\..\mDNSShared;..\..\mDNSWindows"

+				PreprocessorDefinitions="WIN32;_WINDOWS;_DEBUG;_USRDLL;_MERGE_PROXYSTUB;DEBUG=1;WIN32_LEAN_AND_MEAN;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1"

+				MinimalRebuild="true"

+				BasicRuntimeChecks="3"

+				RuntimeLibrary="1"

+				UsePrecompiledHeader="0"

+				WarningLevel="3"

+				Detect64BitPortabilityProblems="true"

+				DebugInformationFormat="3"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+				PreprocessorDefinitions="_DEBUG"

+				Culture="1033"

+				AdditionalIncludeDirectories="..;&quot;$(IntDir)&quot;"

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				RegisterOutput="true"

+				IgnoreImportLibrary="true"

+				AdditionalOptions="/NXCOMPAT /DYNAMICBASE"

+				AdditionalDependencies="ws2_32.lib ../../mDNSWindows/DLLStub/$(PlatformName)/$(ConfigurationName)/dnssdStatic.lib"

+				OutputFile="$(OutDir)\dnssdX.dll"

+				LinkIncremental="2"

+				ModuleDefinitionFile=".\DLLX.def"

+				GenerateDebugInformation="true"

+				SubSystem="2"

+				TargetMachine="17"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+			/>

+		</Configuration>

+		<Configuration

+			Name="Release|Win32"

+			OutputDirectory="$(PlatformName)\$(ConfigurationName)"

+			IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"

+			ConfigurationType="2"

+			UseOfATL="1"

+			ATLMinimizesCRunTimeLibraryUsage="false"

+			CharacterSet="1"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+				PreprocessorDefinitions="NDEBUG"

+				MkTypLibCompatible="false"

+				TargetEnvironment="1"

+				GenerateStublessProxies="true"

+				TypeLibraryName="$(IntDir)/dnssdX.tlb"

+				HeaderFileName="DLLX.h"

+				DLLDataFileName=""

+				InterfaceIdentifierFileName="DLLX_i.c"

+				ProxyFileName="DLLX_p.c"

+				ValidateParameters="false"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				Optimization="2"

+				AdditionalIncludeDirectories="..\..\mDNSShared;..\..\mDNSWindows"

+				PreprocessorDefinitions="WIN32;_WINDOWS;NDEBUG;_USRDLL;_MERGE_PROXYSTUB;WIN32_LEAN_AND_MEAN;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1"

+				RuntimeLibrary="0"

+				UsePrecompiledHeader="0"

+				WarningLevel="3"

+				Detect64BitPortabilityProblems="true"

+				DebugInformationFormat="3"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+				PreprocessorDefinitions="NDEBUG"

+				Culture="1033"

+				AdditionalIncludeDirectories="..;&quot;$(IntDir)&quot;"

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				RegisterOutput="false"

+				IgnoreImportLibrary="true"

+				AdditionalOptions="/NXCOMPAT /DYNAMICBASE /SAFESEH"

+				AdditionalDependencies="ws2_32.lib ../../mDNSWindows/DLLStub/$(PlatformName)/$(ConfigurationName)/dnssdStatic.lib"

+				OutputFile="$(OutDir)\dnssdX.dll"

+				LinkIncremental="1"

+				ModuleDefinitionFile=".\DLLX.def"

+				GenerateDebugInformation="true"

+				SubSystem="2"

+				OptimizeReferences="2"

+				EnableCOMDATFolding="2"

+				TargetMachine="1"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+				CommandLine="if not &quot;%RC_XBS%&quot; == &quot;YES&quot; goto END&#x0D;&#x0A;if not exist &quot;$(DSTROOT)\WINDOWS\system32\$(PlatformName)&quot;             mkdir &quot;$(DSTROOT)\WINDOWS\system32\$(PlatformName)&quot;&#x0D;&#x0A;xcopy /I/Y &quot;$(TargetPath)&quot;                                                                            &quot;$(DSTROOT)\WINDOWS\system32\$(PlatformName)&quot;&#x0D;&#x0A;:END&#x0D;&#x0A;"

+			/>

+		</Configuration>

+		<Configuration

+			Name="Release|x64"

+			OutputDirectory="$(PlatformName)\$(ConfigurationName)"

+			IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"

+			ConfigurationType="2"

+			UseOfATL="1"

+			ATLMinimizesCRunTimeLibraryUsage="false"

+			CharacterSet="1"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+				PreprocessorDefinitions="NDEBUG"

+				MkTypLibCompatible="false"

+				TargetEnvironment="3"

+				GenerateStublessProxies="true"

+				TypeLibraryName="$(IntDir)/dnssdX.tlb"

+				HeaderFileName="DLLX.h"

+				DLLDataFileName=""

+				InterfaceIdentifierFileName="DLLX_i.c"

+				ProxyFileName="DLLX_p.c"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				Optimization="2"

+				AdditionalIncludeDirectories="..\..\mDNSShared;..\..\mDNSWindows"

+				PreprocessorDefinitions="WIN32;_WINDOWS;NDEBUG;_USRDLL;_MERGE_PROXYSTUB;WIN32_LEAN_AND_MEAN;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1"

+				RuntimeLibrary="0"

+				UsePrecompiledHeader="0"

+				WarningLevel="3"

+				Detect64BitPortabilityProblems="true"

+				DebugInformationFormat="3"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+				PreprocessorDefinitions="NDEBUG"

+				Culture="1033"

+				AdditionalIncludeDirectories="..;&quot;$(IntDir)&quot;"

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				RegisterOutput="false"

+				IgnoreImportLibrary="true"

+				AdditionalOptions="/NXCOMPAT /DYNAMICBASE"

+				AdditionalDependencies="ws2_32.lib ../../mDNSWindows/DLLStub/$(PlatformName)/$(ConfigurationName)/dnssdStatic.lib"

+				OutputFile="$(OutDir)\dnssdX.dll"

+				LinkIncremental="1"

+				ModuleDefinitionFile=".\DLLX.def"

+				GenerateDebugInformation="true"

+				SubSystem="2"

+				OptimizeReferences="2"

+				EnableCOMDATFolding="2"

+				TargetMachine="17"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+				CommandLine="if not &quot;%RC_XBS%&quot; == &quot;YES&quot; goto END&#x0D;&#x0A;if not exist &quot;$(DSTROOT)\WINDOWS\system32\$(PlatformName)&quot;             mkdir &quot;$(DSTROOT)\WINDOWS\system32\$(PlatformName)&quot;&#x0D;&#x0A;xcopy /I/Y &quot;$(TargetPath)&quot;                                                                            &quot;$(DSTROOT)\WINDOWS\system32\$(PlatformName)&quot;&#x0D;&#x0A;:END&#x0D;&#x0A;"

+			/>

+		</Configuration>

+	</Configurations>

+	<References>

+	</References>

+	<Files>

+		<Filter

+			Name="Source Files"

+			Filter="cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx"

+			UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"

+			>

+			<File

+				RelativePath=".\dlldatax.c"

+				>

+				<FileConfiguration

+					Name="Debug|Win32"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						UsePrecompiledHeader="0"

+					/>

+				</FileConfiguration>

+				<FileConfiguration

+					Name="Debug|x64"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						UsePrecompiledHeader="0"

+					/>

+				</FileConfiguration>

+				<FileConfiguration

+					Name="Release|Win32"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						UsePrecompiledHeader="0"

+					/>

+				</FileConfiguration>

+				<FileConfiguration

+					Name="Release|x64"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						UsePrecompiledHeader="0"

+					/>

+				</FileConfiguration>

+			</File>

+			<File

+				RelativePath=".\DLLX.cpp"

+				>

+			</File>

+			<File

+				RelativePath=".\DLLX.def"

+				>

+			</File>

+			<File

+				RelativePath=".\DLLX.idl"

+				>

+			</File>

+			<File

+				RelativePath=".\DNSSDEventManager.cpp"

+				>

+			</File>

+			<File

+				RelativePath=".\DNSSDRecord.cpp"

+				>

+			</File>

+			<File

+				RelativePath=".\DNSSDService.cpp"

+				>

+			</File>

+			<File

+				RelativePath=".\TXTRecord.cpp"

+				>

+			</File>

+		</Filter>

+		<Filter

+			Name="Header Files"

+			Filter="h;hpp;hxx;hm;inl;inc;xsd"

+			UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"

+			>

+			<File

+				RelativePath=".\_IDNSSDEvents_CP.H"

+				>

+			</File>

+			<File

+				RelativePath=".\dlldatax.h"

+				>

+			</File>

+			<File

+				RelativePath=".\DNSSDEventManager.h"

+				>

+			</File>

+			<File

+				RelativePath=".\DNSSDRecord.h"

+				>

+			</File>

+			<File

+				RelativePath=".\DNSSDService.h"

+				>

+			</File>

+			<File

+				RelativePath=".\Resource.h"

+				>

+			</File>

+			<File

+				RelativePath=".\stdafx.h"

+				>

+			</File>

+			<File

+				RelativePath=".\TXTRecord.h"

+				>

+			</File>

+		</Filter>

+		<Filter

+			Name="Resource Files"

+			Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav"

+			UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"

+			>

+			<File

+				RelativePath=".\DLLX.rc"

+				>

+			</File>

+			<File

+				RelativePath=".\DLLX.rgs"

+				>

+			</File>

+			<File

+				RelativePath=".\DNSSDEventManager.rgs"

+				>

+			</File>

+			<File

+				RelativePath=".\DNSSDRecord.rgs"

+				>

+			</File>

+			<File

+				RelativePath=".\DNSSDService.rgs"

+				>

+			</File>

+			<File

+				RelativePath=".\TXTRecord.rgs"

+				>

+			</File>

+		</Filter>

+		<Filter

+			Name="Generated Files"

+			SourceControlFiles="false"

+			>

+			<File

+				RelativePath=".\DLLX.h"

+				>

+			</File>

+			<File

+				RelativePath=".\DLLX_i.c"

+				>

+				<FileConfiguration

+					Name="Debug|Win32"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						UsePrecompiledHeader="0"

+					/>

+				</FileConfiguration>

+				<FileConfiguration

+					Name="Debug|x64"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						UsePrecompiledHeader="0"

+					/>

+				</FileConfiguration>

+				<FileConfiguration

+					Name="Release|Win32"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						UsePrecompiledHeader="0"

+					/>

+				</FileConfiguration>

+				<FileConfiguration

+					Name="Release|x64"

+					>

+					<Tool

+						Name="VCCLCompilerTool"

+						UsePrecompiledHeader="0"

+					/>

+				</FileConfiguration>

+			</File>

+		</Filter>

+		<Filter

+			Name="Support Files"

+			>

+			<File

+				RelativePath="..\..\mDNSShared\CommonServices.h"

+				>

+			</File>

+			<File

+				RelativePath="..\..\mDNSShared\DebugServices.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\mDNSShared\DebugServices.h"

+				>

+			</File>

+			<File

+				RelativePath=".\StringServices.cpp"

+				>

+			</File>

+			<File

+				RelativePath=".\StringServices.h"

+				>

+			</File>

+		</Filter>

+	</Files>

+	<Globals>

+	</Globals>

+</VisualStudioProject>

diff --git a/mdnsresponder/mDNSWindows/DLLX/DNSSD.cpp b/mdnsresponder/mDNSWindows/DLLX/DNSSD.cpp
new file mode 100755
index 0000000..84a8206
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DLLX/DNSSD.cpp
@@ -0,0 +1,892 @@
+// DNSSD.cpp : Implementation of CDNSSD

+

+#include "stdafx.h"

+#include "DNSSD.h"

+#include "DNSSDService.h"

+#include "TXTRecord.h"

+#include <dns_sd.h>

+#include <CommonServices.h>

+#include <DebugServices.h>

+#include "StringServices.h"

+

+

+// CDNSSD

+

+STDMETHODIMP CDNSSD::Browse(DNSSDFlags flags, ULONG ifIndex, BSTR regtype, BSTR domain, IBrowseListener* listener, IDNSSDService** browser )

+{

+	CComObject<CDNSSDService>	*	object		= NULL;

+	std::string						regtypeUTF8;

+	std::string						domainUTF8;

+	DNSServiceRef					sref		= NULL;

+	DNSServiceErrorType				err			= 0;

+	HRESULT							hr			= 0;

+	BOOL							ok;

+

+	// Initialize

+	*browser = NULL;

+

+	// Convert BSTR params to utf8

+	ok = BSTRToUTF8( regtype, regtypeUTF8 );

+	require_action( ok, exit, err = kDNSServiceErr_BadParam );

+	ok = BSTRToUTF8( domain, domainUTF8 );

+	require_action( ok, exit, err = kDNSServiceErr_BadParam );

+

+	try

+	{

+		object = new CComObject<CDNSSDService>();

+	}

+	catch ( ... )

+	{

+		object = NULL;

+	}

+

+	require_action( object != NULL, exit, err = kDNSServiceErr_NoMemory );

+	hr = object->FinalConstruct();

+	require_action( hr == S_OK, exit, err = kDNSServiceErr_Unknown );

+	object->AddRef();

+

+	err = DNSServiceBrowse( &sref, flags, ifIndex, regtypeUTF8.c_str(), domainUTF8.c_str(), ( DNSServiceBrowseReply ) &BrowseReply, object );

+	require_noerr( err, exit );

+

+	object->SetServiceRef( sref );

+	object->SetListener( listener );

+

+	err = object->Run();

+	require_noerr( err, exit );

+

+	*browser = object;

+

+exit:

+

+	if ( err && object )

+	{

+		object->Release();

+	}

+

+	return err;

+}

+

+

+STDMETHODIMP CDNSSD::Resolve(DNSSDFlags flags, ULONG ifIndex, BSTR serviceName, BSTR regType, BSTR domain, IResolveListener* listener, IDNSSDService** service)

+{

+	CComObject<CDNSSDService>	*	object			= NULL;

+	std::string						serviceNameUTF8;

+	std::string						regTypeUTF8;

+	std::string						domainUTF8;

+	DNSServiceRef					sref			= NULL;

+	DNSServiceErrorType				err				= 0;

+	HRESULT							hr				= 0;

+	BOOL							ok;

+

+	// Initialize

+	*service = NULL;

+

+	// Convert BSTR params to utf8

+	ok = BSTRToUTF8( serviceName, serviceNameUTF8 );

+	require_action( ok, exit, err = kDNSServiceErr_BadParam );

+	ok = BSTRToUTF8( regType, regTypeUTF8 );

+	require_action( ok, exit, err = kDNSServiceErr_BadParam );

+	ok = BSTRToUTF8( domain, domainUTF8 );

+	require_action( ok, exit, err = kDNSServiceErr_BadParam );

+

+	try

+	{

+		object = new CComObject<CDNSSDService>();

+	}

+	catch ( ... )

+	{

+		object = NULL;

+	}

+

+	require_action( object != NULL, exit, err = kDNSServiceErr_NoMemory );

+	hr = object->FinalConstruct();

+	require_action( hr == S_OK, exit, err = kDNSServiceErr_Unknown );

+	object->AddRef();

+

+	err = DNSServiceResolve( &sref, flags, ifIndex, serviceNameUTF8.c_str(), regTypeUTF8.c_str(), domainUTF8.c_str(), ( DNSServiceResolveReply ) &ResolveReply, object );

+	require_noerr( err, exit );

+

+	object->SetServiceRef( sref );

+	object->SetListener( listener );

+

+	err = object->Run();

+	require_noerr( err, exit );

+

+	*service = object;

+

+exit:

+

+	if ( err && object )

+	{

+		object->Release();

+	}

+

+	return err;

+}

+

+

+STDMETHODIMP CDNSSD::EnumerateDomains(DNSSDFlags flags, ULONG ifIndex, IDomainListener *listener, IDNSSDService **service)

+{

+	CComObject<CDNSSDService>	*	object			= NULL;

+	DNSServiceRef					sref			= NULL;

+	DNSServiceErrorType				err				= 0;

+	HRESULT							hr				= 0;

+

+	// Initialize

+	*service = NULL;

+

+	try

+	{

+		object = new CComObject<CDNSSDService>();

+	}

+	catch ( ... )

+	{

+		object = NULL;

+	}

+

+	require_action( object != NULL, exit, err = kDNSServiceErr_NoMemory );

+	hr = object->FinalConstruct();

+	require_action( hr == S_OK, exit, err = kDNSServiceErr_Unknown );

+	object->AddRef();

+

+	err = DNSServiceEnumerateDomains( &sref, flags, ifIndex, ( DNSServiceDomainEnumReply ) &DomainEnumReply, object );

+	require_noerr( err, exit );

+

+	object->SetServiceRef( sref );

+	object->SetListener( listener );

+

+	err = object->Run();

+	require_noerr( err, exit );

+

+	*service = object;

+

+exit:

+

+	if ( err && object )

+	{

+		object->Release();

+	}

+

+	return err;

+}

+

+

+STDMETHODIMP CDNSSD::Register(DNSSDFlags flags, ULONG ifIndex, BSTR serviceName, BSTR regType, BSTR domain, BSTR host, USHORT port, ITXTRecord *record, IRegisterListener *listener, IDNSSDService **service)

+{

+	CComObject<CDNSSDService>	*	object			= NULL;

+	std::string						serviceNameUTF8;

+	std::string						regTypeUTF8;

+	std::string						domainUTF8;

+	std::string						hostUTF8;

+	const void					*	txtRecord		= NULL;

+	uint16_t						txtLen			= 0;

+	DNSServiceRef					sref			= NULL;

+	DNSServiceErrorType				err				= 0;

+	HRESULT							hr				= 0;

+	BOOL							ok;

+

+	// Initialize

+	*service = NULL;

+

+	// Convert BSTR params to utf8

+	ok = BSTRToUTF8( serviceName, serviceNameUTF8 );

+	require_action( ok, exit, err = kDNSServiceErr_BadParam );

+	ok = BSTRToUTF8( regType, regTypeUTF8 );

+	require_action( ok, exit, err = kDNSServiceErr_BadParam );

+	ok = BSTRToUTF8( domain, domainUTF8 );

+	require_action( ok, exit, err = kDNSServiceErr_BadParam );

+	ok = BSTRToUTF8( host, hostUTF8 );

+	require_action( ok, exit, err = kDNSServiceErr_BadParam );

+

+	try

+	{

+		object = new CComObject<CDNSSDService>();

+	}

+	catch ( ... )

+	{

+		object = NULL;

+	}

+

+	require_action( object != NULL, exit, err = kDNSServiceErr_NoMemory );

+	hr = object->FinalConstruct();

+	require_action( hr == S_OK, exit, err = kDNSServiceErr_Unknown );

+	object->AddRef();

+

+	if ( record )

+	{

+		CComObject< CTXTRecord > * realTXTRecord;

+

+		realTXTRecord = ( CComObject< CTXTRecord >* ) record;

+

+		txtRecord	= realTXTRecord->GetBytes();

+		txtLen		= realTXTRecord->GetLen();

+	}

+

+	err = DNSServiceRegister( &sref, flags, ifIndex, serviceNameUTF8.c_str(), regTypeUTF8.c_str(), domainUTF8.c_str(), hostUTF8.c_str(), port, txtLen, txtRecord, ( DNSServiceRegisterReply ) &RegisterReply, object );

+	require_noerr( err, exit );

+

+	object->SetServiceRef( sref );

+	object->SetListener( listener );

+

+	err = object->Run();

+	require_noerr( err, exit );

+

+	*service = object;

+

+exit:

+

+	if ( err && object )

+	{

+		object->Release();

+	}

+

+	return err;

+}

+

+

+STDMETHODIMP CDNSSD::QueryRecord(DNSSDFlags flags, ULONG ifIndex, BSTR fullname, DNSSDRRType rrtype, DNSSDRRClass rrclass, IQueryRecordListener *listener, IDNSSDService **service)

+{

+	CComObject<CDNSSDService>	*	object			= NULL;

+	DNSServiceRef					sref			= NULL;

+	std::string						fullNameUTF8;

+	DNSServiceErrorType				err				= 0;

+	HRESULT							hr				= 0;

+	BOOL							ok;

+

+	// Initialize

+	*service = NULL;

+

+	// Convert BSTR params to utf8

+	ok = BSTRToUTF8( fullname, fullNameUTF8 );

+	require_action( ok, exit, err = kDNSServiceErr_BadParam );

+

+	try

+	{

+		object = new CComObject<CDNSSDService>();

+	}

+	catch ( ... )

+	{

+		object = NULL;

+	}

+

+	require_action( object != NULL, exit, err = kDNSServiceErr_NoMemory );

+	hr = object->FinalConstruct();

+	require_action( hr == S_OK, exit, err = kDNSServiceErr_Unknown );

+	object->AddRef();

+

+	err = DNSServiceQueryRecord( &sref, flags, ifIndex, fullNameUTF8.c_str(), ( uint16_t ) rrtype, ( uint16_t ) rrclass, ( DNSServiceQueryRecordReply ) &QueryRecordReply, object );

+	require_noerr( err, exit );

+

+	object->SetServiceRef( sref );

+	object->SetListener( listener );

+

+	err = object->Run();

+	require_noerr( err, exit );

+

+	*service = object;

+

+exit:

+

+	if ( err && object )

+	{

+		object->Release();

+	}

+

+	return err;

+}

+

+

+STDMETHODIMP CDNSSD::GetAddrInfo(DNSSDFlags flags, ULONG ifIndex, DNSSDAddressFamily addressFamily, BSTR hostName, IGetAddrInfoListener *listener, IDNSSDService **service)

+{

+	CComObject<CDNSSDService>	*	object			= NULL;

+	DNSServiceRef					sref			= NULL;

+	std::string						hostNameUTF8;

+	DNSServiceErrorType				err				= 0;

+	HRESULT							hr				= 0;

+	BOOL							ok;

+

+	// Initialize

+	*service = NULL;

+

+	// Convert BSTR params to utf8

+	ok = BSTRToUTF8( hostName, hostNameUTF8 );

+	require_action( ok, exit, err = kDNSServiceErr_BadParam );

+

+	try

+	{

+		object = new CComObject<CDNSSDService>();

+	}

+	catch ( ... )

+	{

+		object = NULL;

+	}

+

+	require_action( object != NULL, exit, err = kDNSServiceErr_NoMemory );

+	hr = object->FinalConstruct();

+	require_action( hr == S_OK, exit, err = kDNSServiceErr_Unknown );

+	object->AddRef();

+

+	err = DNSServiceGetAddrInfo( &sref, flags, ifIndex, addressFamily, hostNameUTF8.c_str(), ( DNSServiceGetAddrInfoReply ) &GetAddrInfoReply, object );

+	require_noerr( err, exit );

+

+	object->SetServiceRef( sref );

+	object->SetListener( listener );

+

+	err = object->Run();

+	require_noerr( err, exit );

+

+	*service = object;

+

+exit:

+

+	if ( err && object )

+	{

+		object->Release();

+	}

+

+	return err;

+}

+

+

+STDMETHODIMP CDNSSD::CreateConnection(IDNSSDService **service)

+{

+	CComObject<CDNSSDService>	*	object	= NULL;

+	DNSServiceRef					sref	= NULL;

+	DNSServiceErrorType				err		= 0;

+	HRESULT							hr		= 0;

+

+	// Initialize

+	*service = NULL;

+

+	try

+	{

+		object = new CComObject<CDNSSDService>();

+	}

+	catch ( ... )

+	{

+		object = NULL;

+	}

+

+	require_action( object != NULL, exit, err = kDNSServiceErr_NoMemory );

+	hr = object->FinalConstruct();

+	require_action( hr == S_OK, exit, err = kDNSServiceErr_Unknown );

+	object->AddRef();

+

+	err = DNSServiceCreateConnection( &sref );

+	require_noerr( err, exit );

+

+	object->SetServiceRef( sref );

+

+	*service = object;

+

+exit:

+

+	if ( err && object )

+	{

+		object->Release();

+	}

+

+	return err;

+}

+

+

+STDMETHODIMP CDNSSD::NATPortMappingCreate(DNSSDFlags flags, ULONG ifIndex, DNSSDAddressFamily addressFamily, DNSSDProtocol protocol, USHORT internalPort, USHORT externalPort, ULONG ttl, INATPortMappingListener *listener, IDNSSDService **service)

+{

+	CComObject<CDNSSDService>	*	object			= NULL;

+	DNSServiceRef					sref			= NULL;

+	DNSServiceProtocol				prot			= 0;

+	DNSServiceErrorType				err				= 0;

+	HRESULT							hr				= 0;

+

+	// Initialize

+	*service = NULL;

+

+	try

+	{

+		object = new CComObject<CDNSSDService>();

+	}

+	catch ( ... )

+	{

+		object = NULL;

+	}

+

+	require_action( object != NULL, exit, err = kDNSServiceErr_NoMemory );

+	hr = object->FinalConstruct();

+	require_action( hr == S_OK, exit, err = kDNSServiceErr_Unknown );

+	object->AddRef();

+

+	prot = ( addressFamily | protocol );

+

+	err = DNSServiceNATPortMappingCreate( &sref, flags, ifIndex, prot, internalPort, externalPort, ttl, ( DNSServiceNATPortMappingReply ) &NATPortMappingReply, object );

+	require_noerr( err, exit );

+

+	object->SetServiceRef( sref );

+	object->SetListener( listener );

+

+	err = object->Run();

+	require_noerr( err, exit );

+

+	*service = object;

+

+exit:

+

+	if ( err && object )

+	{

+		object->Release();

+	}

+

+	return err;

+}

+

+

+STDMETHODIMP CDNSSD::GetProperty(BSTR prop, VARIANT * value )

+{

+	std::string			propUTF8;

+	std::vector< BYTE >	byteArray;

+	SAFEARRAY		*	psa			= NULL;

+	BYTE			*	pData		= NULL;

+	uint32_t			elems		= 0;

+	DNSServiceErrorType	err			= 0;

+	BOOL				ok = TRUE;

+

+	// Convert BSTR params to utf8

+	ok = BSTRToUTF8( prop, propUTF8 );

+	require_action( ok, exit, err = kDNSServiceErr_BadParam );

+

+	// Setup the byte array

+	require_action( V_VT( value ) == ( VT_ARRAY|VT_UI1 ), exit, err = kDNSServiceErr_Unknown );

+	psa = V_ARRAY( value );

+	require_action( psa, exit, err = kDNSServiceErr_Unknown );

+	require_action( SafeArrayGetDim( psa ) == 1, exit, err = kDNSServiceErr_Unknown );

+	byteArray.reserve( psa->rgsabound[0].cElements );

+	byteArray.assign( byteArray.capacity(), 0 );

+	elems = ( uint32_t ) byteArray.capacity();

+

+	// Call the function and package the return value in the Variant

+	err = DNSServiceGetProperty( propUTF8.c_str(), &byteArray[ 0 ], &elems );

+	require_noerr( err, exit );

+	ok = ByteArrayToVariant( &byteArray[ 0 ], elems, value );

+	require_action( ok, exit, err = kDNSSDError_Unknown );

+

+exit:

+

+	if ( psa )

+	{

+		SafeArrayUnaccessData( psa );

+		psa = NULL;

+	}

+

+	return err;

+}

+

+

+void DNSSD_API
+CDNSSD::DomainEnumReply
+    (
+    DNSServiceRef                       sdRef,
+    DNSServiceFlags                     flags,
+    uint32_t                            ifIndex,
+    DNSServiceErrorType                 errorCode,
+    const char                          *replyDomainUTF8,
+    void                                *context
+    )

+{

+	CComObject<CDNSSDService> * service;

+	int err;

+	

+	service = ( CComObject< CDNSSDService>* ) context;

+	require_action( service, exit, err = kDNSServiceErr_Unknown );

+

+	if ( !service->Stopped() )

+	{

+		IDomainListener	* listener;

+

+		listener = ( IDomainListener* ) service->GetListener();

+		require_action( listener, exit, err = kDNSServiceErr_Unknown );

+

+		if ( !errorCode )

+		{

+			CComBSTR replyDomain;

+		

+			UTF8ToBSTR( replyDomainUTF8, replyDomain );

+

+			if ( flags & kDNSServiceFlagsAdd )

+			{

+				listener->DomainFound( service, ( DNSSDFlags ) flags, ifIndex, replyDomain );

+			}

+			else

+			{

+				listener->DomainLost( service, ( DNSSDFlags ) flags, ifIndex, replyDomain );

+			}

+		}

+		else

+		{

+			listener->EnumDomainsFailed( service, ( DNSSDError ) errorCode );

+		}

+	}

+

+exit:

+

+	return;

+}

+

+

+void DNSSD_API
+CDNSSD::BrowseReply
+		(
+		DNSServiceRef                       sdRef,
+		DNSServiceFlags                     flags,
+		uint32_t                            ifIndex,
+		DNSServiceErrorType                 errorCode,
+		const char                          *serviceNameUTF8,
+		const char                          *regTypeUTF8,
+		const char                          *replyDomainUTF8,
+		void                                *context
+		)

+{

+	CComObject<CDNSSDService> * service;

+	int err;

+	

+	service = ( CComObject< CDNSSDService>* ) context;

+	require_action( service, exit, err = kDNSServiceErr_Unknown );

+

+	if ( !service->Stopped() )

+	{

+		IBrowseListener	* listener;

+

+		listener = ( IBrowseListener* ) service->GetListener();

+		require_action( listener, exit, err = kDNSServiceErr_Unknown );

+

+		if ( !errorCode )

+		{

+			CComBSTR	serviceName;

+			CComBSTR	regType;

+			CComBSTR	replyDomain;

+		

+			UTF8ToBSTR( serviceNameUTF8, serviceName );

+			UTF8ToBSTR( regTypeUTF8, regType );

+			UTF8ToBSTR( replyDomainUTF8, replyDomain );

+

+			if ( flags & kDNSServiceFlagsAdd )

+			{

+				listener->ServiceFound( service, ( DNSSDFlags ) flags, ifIndex, serviceName, regType, replyDomain );

+			}

+			else

+			{

+				listener->ServiceLost( service, ( DNSSDFlags ) flags, ifIndex, serviceName, regType, replyDomain );

+			}

+		}

+		else

+		{

+			listener->BrowseFailed( service, ( DNSSDError ) errorCode );

+		}

+	}

+

+exit:

+

+	return;

+}

+

+

+void DNSSD_API

+CDNSSD::ResolveReply

+		(
+		DNSServiceRef                       sdRef,
+		DNSServiceFlags                     flags,
+		uint32_t                            ifIndex,
+		DNSServiceErrorType                 errorCode,
+		const char                          *fullNameUTF8,
+		const char                          *hostNameUTF8,
+		uint16_t                            port,
+		uint16_t                            txtLen,
+		const unsigned char                 *txtRecord,
+		void                                *context

+		)

+{

+	CComObject<CDNSSDService> * service;

+	int err;

+	

+	service = ( CComObject< CDNSSDService>* ) context;

+	require_action( service, exit, err = kDNSServiceErr_Unknown );

+

+	if ( !service->Stopped() )

+	{

+		IResolveListener * listener;

+

+		listener = ( IResolveListener* ) service->GetListener();

+		require_action( listener, exit, err = kDNSServiceErr_Unknown );

+

+		if ( !errorCode )

+		{

+			CComBSTR					fullName;

+			CComBSTR					hostName;

+			CComBSTR					regType;

+			CComBSTR					replyDomain;

+			CComObject< CTXTRecord >*	record;

+			BOOL						ok;

+

+			ok = UTF8ToBSTR( fullNameUTF8, fullName );

+			require_action( ok, exit, err = kDNSServiceErr_Unknown );

+			ok = UTF8ToBSTR( hostNameUTF8, hostName );

+			require_action( ok, exit, err = kDNSServiceErr_Unknown );

+

+			try

+			{

+				record = new CComObject<CTXTRecord>();

+			}

+			catch ( ... )

+			{

+				record = NULL;

+			}

+

+			require_action( record, exit, err = kDNSServiceErr_NoMemory );

+			record->AddRef();

+

+			char buf[ 64 ];

+			sprintf( buf, "txtLen = %d", txtLen );

+			OutputDebugStringA( buf );

+

+			if ( txtLen > 0 )

+			{

+				record->SetBytes( txtRecord, txtLen );

+			}

+

+			listener->ServiceResolved( service, ( DNSSDFlags ) flags, ifIndex, fullName, hostName, port, record );

+		}

+		else

+		{

+			listener->ResolveFailed( service, ( DNSSDError ) errorCode );

+		}

+	}

+

+exit:

+

+	return;

+}

+

+

+void DNSSD_API
+CDNSSD::RegisterReply
+		(
+		DNSServiceRef                       sdRef,
+		DNSServiceFlags                     flags,
+		DNSServiceErrorType                 errorCode,
+		const char                          *serviceNameUTF8,
+		const char                          *regTypeUTF8,
+		const char                          *domainUTF8,
+		void                                *context
+		)

+{

+	CComObject<CDNSSDService> * service;

+	int err;

+	

+	service = ( CComObject< CDNSSDService>* ) context;

+	require_action( service, exit, err = kDNSServiceErr_Unknown );

+

+	if ( !service->Stopped() )

+	{

+		IRegisterListener * listener;

+

+		listener = ( IRegisterListener* ) service->GetListener();

+		require_action( listener, exit, err = kDNSServiceErr_Unknown );

+

+		if ( !errorCode )

+		{

+			CComBSTR					serviceName;

+			CComBSTR					regType;

+			CComBSTR					domain;

+			BOOL						ok;

+

+			ok = UTF8ToBSTR( serviceNameUTF8, serviceName );

+			require_action( ok, exit, err = kDNSServiceErr_Unknown );

+			ok = UTF8ToBSTR( regTypeUTF8, regType );

+			require_action( ok, exit, err = kDNSServiceErr_Unknown );

+			ok = UTF8ToBSTR( domainUTF8, domain );

+			require_action( ok, exit, err = kDNSServiceErr_Unknown );

+

+			listener->ServiceRegistered( service, ( DNSSDFlags ) flags, serviceName, regType, domain );

+		}

+		else

+		{

+			listener->ServiceRegisterFailed( service, ( DNSSDError ) errorCode );

+		}

+	}

+

+exit:

+

+	return;

+}

+

+

+void DNSSD_API
+CDNSSD::QueryRecordReply
+		(
+		DNSServiceRef                       sdRef,
+		DNSServiceFlags                     flags,
+		uint32_t                            ifIndex,
+		DNSServiceErrorType                 errorCode,
+		const char                          *fullNameUTF8,
+		uint16_t                            rrtype,
+		uint16_t                            rrclass,
+		uint16_t                            rdlen,
+		const void                          *rdata,
+		uint32_t                            ttl,
+		void                                *context
+		)

+{

+	CComObject<CDNSSDService> * service;

+	int err;

+	

+	service = ( CComObject< CDNSSDService>* ) context;

+	require_action( service, exit, err = kDNSServiceErr_Unknown );

+

+	if ( !service->Stopped() )

+	{

+		IQueryRecordListener * listener;

+

+		listener = ( IQueryRecordListener* ) service->GetListener();

+		require_action( listener, exit, err = kDNSServiceErr_Unknown );

+

+		if ( !errorCode )

+		{

+			CComBSTR	fullName;

+			VARIANT		var;

+			BOOL		ok;

+

+			ok = UTF8ToBSTR( fullNameUTF8, fullName );

+			require_action( ok, exit, err = kDNSServiceErr_Unknown );

+			ok = ByteArrayToVariant( rdata, rdlen, &var );

+			require_action( ok, exit, err = kDNSServiceErr_Unknown );

+

+			listener->QueryRecordAnswered( service, ( DNSSDFlags ) flags, ifIndex, fullName, ( DNSSDRRType ) rrtype, ( DNSSDRRClass ) rrclass, var, ttl );

+		}

+		else

+		{

+			listener->QueryRecordFailed( service, ( DNSSDError ) errorCode );

+		}

+	}

+

+exit:

+

+	return;

+}

+

+

+void DNSSD_API
+CDNSSD::GetAddrInfoReply
+		(
+		DNSServiceRef                    sdRef,
+		DNSServiceFlags                  flags,
+		uint32_t                         ifIndex,
+		DNSServiceErrorType              errorCode,
+		const char                       *hostNameUTF8,
+		const struct sockaddr            *rawAddress,
+		uint32_t                         ttl,
+		void                             *context
+		)

+{

+	CComObject<CDNSSDService> * service;

+	int err;

+	

+	service = ( CComObject< CDNSSDService>* ) context;

+	require_action( service, exit, err = kDNSServiceErr_Unknown );

+

+	if ( !service->Stopped() )

+	{

+		IGetAddrInfoListener * listener;

+

+		listener = ( IGetAddrInfoListener* ) service->GetListener();

+		require_action( listener, exit, err = kDNSServiceErr_Unknown );

+

+		if ( !errorCode )

+		{

+			CComBSTR			hostName;

+			DWORD				sockaddrLen;

+			DNSSDAddressFamily	addressFamily;

+			char				addressUTF8[INET6_ADDRSTRLEN];

+			DWORD				addressLen = sizeof( addressUTF8 );

+			CComBSTR			address;

+			BOOL				ok;

+

+			ok = UTF8ToBSTR( hostNameUTF8, hostName );

+			require_action( ok, exit, err = kDNSServiceErr_Unknown );

+

+			switch ( rawAddress->sa_family )

+			{

+				case AF_INET:

+				{

+					addressFamily	= kDNSSDAddressFamily_IPv4;

+					sockaddrLen		= sizeof( sockaddr_in );

+				}

+				break;

+

+				case AF_INET6:

+				{

+					addressFamily	= kDNSSDAddressFamily_IPv6;

+					sockaddrLen		= sizeof( sockaddr_in6 );

+				}

+				break;

+			}

+

+			err = WSAAddressToStringA( ( LPSOCKADDR ) rawAddress, sockaddrLen, NULL, addressUTF8, &addressLen );

+			require_noerr( err, exit );

+			ok = UTF8ToBSTR( addressUTF8, address );

+			require_action( ok, exit, err = kDNSServiceErr_Unknown );

+

+			listener->GetAddrInfoReply( service, ( DNSSDFlags ) flags, ifIndex, hostName, addressFamily, address, ttl );

+		}

+		else

+		{

+			listener->GetAddrInfoFailed( service, ( DNSSDError ) errorCode );

+		}

+	}

+

+exit:

+

+	return;

+}

+

+

+void DNSSD_API
+CDNSSD::NATPortMappingReply
+    (
+    DNSServiceRef                    sdRef,
+    DNSServiceFlags                  flags,
+    uint32_t                         ifIndex,
+    DNSServiceErrorType              errorCode,
+    uint32_t                         externalAddress,   /* four byte IPv4 address in network byte order */
+    DNSServiceProtocol               protocol,
+    uint16_t                         internalPort,
+    uint16_t                         externalPort,      /* may be different than the requested port     */
+    uint32_t                         ttl,               /* may be different than the requested ttl      */
+    void                             *context
+    )

+{

+	CComObject<CDNSSDService> * service;

+	int err;

+	

+	service = ( CComObject< CDNSSDService>* ) context;

+	require_action( service, exit, err = kDNSServiceErr_Unknown );

+

+	if ( !service->Stopped() )

+	{

+		INATPortMappingListener * listener;

+

+		listener = ( INATPortMappingListener* ) service->GetListener();

+		require_action( listener, exit, err = kDNSServiceErr_Unknown );

+

+		if ( !errorCode )

+		{

+			listener->MappingCreated( service, ( DNSSDFlags ) flags, ifIndex, externalAddress, ( DNSSDAddressFamily ) ( protocol & 0x8 ), ( DNSSDProtocol ) ( protocol & 0x80 ), internalPort, externalPort, ttl  );

+		}

+		else

+		{

+			listener->MappingFailed( service, ( DNSSDError ) errorCode );

+		}

+	}

+

+exit:

+

+	return;

+}

+

diff --git a/mdnsresponder/mDNSWindows/DLLX/DNSSDEventManager.cpp b/mdnsresponder/mDNSWindows/DLLX/DNSSDEventManager.cpp
new file mode 100755
index 0000000..310ced0
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DLLX/DNSSDEventManager.cpp
@@ -0,0 +1,31 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2009 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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 "stdafx.h"
+
+#include "DNSSDEventManager.h"
+
+
+
+
+
+// CDNSSDEventManager
+
+
+
diff --git a/mdnsresponder/mDNSWindows/DLLX/DNSSDEventManager.h b/mdnsresponder/mDNSWindows/DLLX/DNSSDEventManager.h
new file mode 100755
index 0000000..70aedfc
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DLLX/DNSSDEventManager.h
@@ -0,0 +1,133 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2009 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+
+
+#pragma once
+
+#include "resource.h"       // main symbols
+
+
+
+#include "DLLX.h"
+
+#include "_IDNSSDEvents_CP.H"
+
+
+
+
+
+#if defined(_WIN32_WCE) && !defined(_CE_DCOM) && !defined(_CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA)
+
+#error "Single-threaded COM objects are not properly supported on Windows CE platform, such as the Windows Mobile platforms that do not include full DCOM support. Define _CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA to force ATL to support creating single-thread COM object's and allow use of it's single-threaded COM object implementations. The threading model in your rgs file was set to 'Free' as that is the only threading model supported in non DCOM Windows CE platforms."
+
+#endif
+
+
+
+
+
+
+
+// CDNSSDEventManager
+
+
+
+class ATL_NO_VTABLE CDNSSDEventManager :
+
+	public CComObjectRootEx<CComSingleThreadModel>,
+
+	public CComCoClass<CDNSSDEventManager, &CLSID_DNSSDEventManager>,
+
+	public IConnectionPointContainerImpl<CDNSSDEventManager>,
+
+	public CProxy_IDNSSDEvents<CDNSSDEventManager>,
+
+	public IDispatchImpl<IDNSSDEventManager, &IID_IDNSSDEventManager, &LIBID_Bonjour, /*wMajor =*/ 1, /*wMinor =*/ 0>
+
+{
+
+public:
+
+	CDNSSDEventManager()
+
+	{
+
+	}
+
+
+
+DECLARE_REGISTRY_RESOURCEID(IDR_DNSSDEVENTMANAGER)
+
+
+
+
+
+BEGIN_COM_MAP(CDNSSDEventManager)
+
+	COM_INTERFACE_ENTRY(IDNSSDEventManager)
+
+	COM_INTERFACE_ENTRY(IDispatch)
+
+	COM_INTERFACE_ENTRY(IConnectionPointContainer)
+
+END_COM_MAP()
+
+
+
+BEGIN_CONNECTION_POINT_MAP(CDNSSDEventManager)
+
+	CONNECTION_POINT_ENTRY(__uuidof(_IDNSSDEvents))
+
+END_CONNECTION_POINT_MAP()
+
+
+
+
+
+	DECLARE_PROTECT_FINAL_CONSTRUCT()
+
+
+
+	HRESULT FinalConstruct()
+
+	{
+
+		return S_OK;
+
+	}
+
+
+
+	void FinalRelease()
+
+	{
+
+	}
+
+
+
+public:
+
+
+
+};
+
+
+
+OBJECT_ENTRY_AUTO(__uuidof(DNSSDEventManager), CDNSSDEventManager)
+
diff --git a/mdnsresponder/mDNSWindows/DLLX/DNSSDEventManager.rgs b/mdnsresponder/mDNSWindows/DLLX/DNSSDEventManager.rgs
new file mode 100755
index 0000000..9103512
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DLLX/DNSSDEventManager.rgs
@@ -0,0 +1,27 @@
+HKCR

+{

+	Bonjour.DNSSDEventManager.1 = s 'DNSSDEventManager Class'

+	{

+		CLSID = s '{BEEB932A-8D4A-4619-AEFE-A836F988B221}'

+	}

+	Bonjour.DNSSDEventManager = s 'DNSSDEventManager Class'

+	{

+		CLSID = s '{BEEB932A-8D4A-4619-AEFE-A836F988B221}'

+		CurVer = s 'Bonjour.DNSSDEventManager.1'

+	}

+	NoRemove CLSID

+	{

+		ForceRemove {BEEB932A-8D4A-4619-AEFE-A836F988B221} = s 'DNSSDEventManager Class'

+		{

+			ProgID = s 'Bonjour.DNSSDEventManager.1'

+			VersionIndependentProgID = s 'Bonjour.DNSSDEventManager'

+			ForceRemove 'Programmable'

+			InprocServer32 = s '%MODULE%'

+			{

+				val ThreadingModel = s 'Apartment'

+			}

+			val AppID = s '%APPID%'

+			'TypeLib' = s '{18FBED6D-F2B7-4EC8-A4A4-46282E635308}'

+		}

+	}

+}

diff --git a/mdnsresponder/mDNSWindows/DLLX/DNSSDRecord.cpp b/mdnsresponder/mDNSWindows/DLLX/DNSSDRecord.cpp
new file mode 100755
index 0000000..a272720
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DLLX/DNSSDRecord.cpp
@@ -0,0 +1,101 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2009 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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 "stdafx.h"
+
+#include "DNSSDRecord.h"
+
+#include "StringServices.h"
+
+#include <DebugServices.h>
+
+
+
+
+
+// CDNSSDRecord
+
+
+
+STDMETHODIMP CDNSSDRecord::Update(DNSSDFlags flags, VARIANT rdata, ULONG ttl)
+
+{
+
+	std::vector< BYTE >	byteArray;
+
+	const void		*	byteArrayPtr	= NULL;
+
+	DNSServiceErrorType	err				= 0;
+
+	HRESULT				hr				= 0;
+
+	BOOL				ok;
+
+
+
+	// Convert the VARIANT
+
+	ok = VariantToByteArray( &rdata, byteArray );
+
+	require_action( ok, exit, err = kDNSServiceErr_Unknown );
+
+
+
+	err = DNSServiceUpdateRecord( m_serviceObject->GetSubordRef(), m_rref, flags, ( uint16_t ) byteArray.size(), byteArray.size() > 0 ? &byteArray[ 0 ] : NULL, ttl );
+
+	require_noerr( err, exit );
+
+
+
+exit:
+
+
+
+	return err;
+
+}
+
+
+
+
+
+STDMETHODIMP CDNSSDRecord::Remove(DNSSDFlags flags)
+
+{
+
+	DNSServiceErrorType	err = 0;
+
+
+
+	err = DNSServiceRemoveRecord( m_serviceObject->GetSubordRef(), m_rref, flags );
+
+	require_noerr( err, exit );
+
+
+
+exit:
+
+
+
+	return err;
+
+}
+
+
+
diff --git a/mdnsresponder/mDNSWindows/DLLX/DNSSDRecord.h b/mdnsresponder/mDNSWindows/DLLX/DNSSDRecord.h
new file mode 100755
index 0000000..bdedda5
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DLLX/DNSSDRecord.h
@@ -0,0 +1,185 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2009 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+
+
+#pragma once
+
+#include "resource.h"       // main symbols
+
+
+
+#include "DLLX.h"
+
+#include "DNSSDService.h"
+
+#include <dns_sd.h>
+
+
+
+
+
+#if defined(_WIN32_WCE) && !defined(_CE_DCOM) && !defined(_CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA)
+
+#error "Single-threaded COM objects are not properly supported on Windows CE platform, such as the Windows Mobile platforms that do not include full DCOM support. Define _CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA to force ATL to support creating single-thread COM object's and allow use of it's single-threaded COM object implementations. The threading model in your rgs file was set to 'Free' as that is the only threading model supported in non DCOM Windows CE platforms."
+
+#endif
+
+
+
+
+
+
+
+// CDNSSDRecord
+
+
+
+class ATL_NO_VTABLE CDNSSDRecord :
+
+	public CComObjectRootEx<CComSingleThreadModel>,
+
+	public CComCoClass<CDNSSDRecord, &CLSID_DNSSDRecord>,
+
+	public IDispatchImpl<IDNSSDRecord, &IID_IDNSSDRecord, &LIBID_Bonjour, /*wMajor =*/ 1, /*wMinor =*/ 0>
+
+{
+
+public:
+
+	CDNSSDRecord()
+
+	{
+
+	}
+
+
+
+DECLARE_REGISTRY_RESOURCEID(IDR_DNSSDRECORD)
+
+
+
+
+
+BEGIN_COM_MAP(CDNSSDRecord)
+
+	COM_INTERFACE_ENTRY(IDNSSDRecord)
+
+	COM_INTERFACE_ENTRY(IDispatch)
+
+END_COM_MAP()
+
+
+
+
+
+
+
+	DECLARE_PROTECT_FINAL_CONSTRUCT()
+
+
+
+	HRESULT FinalConstruct()
+
+	{
+
+		return S_OK;
+
+	}
+
+
+
+	void FinalRelease()
+
+	{
+
+	}
+
+
+
+	inline CDNSSDService*
+
+	GetServiceObject()
+
+	{
+
+		return m_serviceObject;
+
+	}
+
+
+
+	inline void
+
+	SetServiceObject( CDNSSDService * serviceObject )
+
+	{
+
+		m_serviceObject = serviceObject;
+
+	}
+
+
+
+	inline DNSRecordRef
+
+	GetRecordRef()
+
+	{
+
+		return m_rref;
+
+	}
+
+
+
+	inline void
+
+	SetRecordRef( DNSRecordRef rref )
+
+	{
+
+		m_rref = rref;
+
+	}
+
+
+
+public:
+
+
+
+	STDMETHOD(Update)(DNSSDFlags flags, VARIANT rdata, ULONG ttl);
+
+	STDMETHOD(Remove)(DNSSDFlags flags);
+
+
+
+private:
+
+
+
+	CDNSSDService *	m_serviceObject;
+
+	DNSRecordRef	m_rref;
+
+};
+
+
+
+OBJECT_ENTRY_AUTO(__uuidof(DNSSDRecord), CDNSSDRecord)
+
diff --git a/mdnsresponder/mDNSWindows/DLLX/DNSSDRecord.rgs b/mdnsresponder/mDNSWindows/DLLX/DNSSDRecord.rgs
new file mode 100755
index 0000000..ee0a5a2
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DLLX/DNSSDRecord.rgs
@@ -0,0 +1,27 @@
+HKCR

+{

+	Bonjour.DNSSDRecord.1 = s 'DNSSDRecord Class'

+	{

+		CLSID = s '{5E93C5A9-7516-4259-A67B-41A656F6E01C}'

+	}

+	Bonjour.DNSSDRecord = s 'DNSSDRecord Class'

+	{

+		CLSID = s '{5E93C5A9-7516-4259-A67B-41A656F6E01C}'

+		CurVer = s 'Bonjour.DNSSDRecord.1'

+	}

+	NoRemove CLSID

+	{

+		ForceRemove {5E93C5A9-7516-4259-A67B-41A656F6E01C} = s 'DNSSDRecord Class'

+		{

+			ProgID = s 'Bonjour.DNSSDRecord.1'

+			VersionIndependentProgID = s 'Bonjour.DNSSDRecord'

+			ForceRemove 'Programmable'

+			InprocServer32 = s '%MODULE%'

+			{

+				val ThreadingModel = s 'Apartment'

+			}

+			val AppID = s '%APPID%'

+			'TypeLib' = s '{18FBED6D-F2B7-4EC8-A4A4-46282E635308}'

+		}

+	}

+}

diff --git a/mdnsresponder/mDNSWindows/DLLX/DNSSDService.cpp b/mdnsresponder/mDNSWindows/DLLX/DNSSDService.cpp
new file mode 100755
index 0000000..b8ce49b
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DLLX/DNSSDService.cpp
@@ -0,0 +1,2095 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2009 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+
+
+#pragma warning(disable:4995)
+
+
+
+#include "stdafx.h"
+
+#include <strsafe.h>
+
+#include "DNSSDService.h"
+
+#include "DNSSDEventManager.h"
+
+#include "DNSSDRecord.h"
+
+#include "TXTRecord.h"
+
+#include "StringServices.h"
+
+#include <DebugServices.h>
+
+
+
+
+
+#define WM_SOCKET (WM_APP + 100)
+
+
+
+
+
+// CDNSSDService
+
+
+
+BOOL						CDNSSDService::m_registeredWindowClass	= FALSE;
+
+HWND						CDNSSDService::m_hiddenWindow			= NULL;
+
+CDNSSDService::SocketMap	CDNSSDService::m_socketMap;
+
+
+
+
+
+HRESULT CDNSSDService::FinalConstruct()
+
+{
+
+	DNSServiceErrorType	err	= 0;
+
+	HRESULT				hr	= S_OK;
+
+
+
+	m_isPrimary = TRUE;
+
+	err = DNSServiceCreateConnection( &m_primary );
+
+	require_action( !err, exit, hr = E_FAIL );
+
+
+
+	if ( !m_hiddenWindow )
+
+	{
+
+		TCHAR windowClassName[ 256 ];
+
+
+
+		StringCchPrintf( windowClassName, sizeof( windowClassName ) / sizeof( TCHAR ), TEXT( "Bonjour Hidden Window %d" ), GetProcessId( NULL ) );
+
+
+
+		if ( !m_registeredWindowClass )
+
+		{
+
+			WNDCLASS	wc;
+
+			ATOM		atom;
+
+
+
+			wc.style			= 0;
+
+			wc.lpfnWndProc		= WndProc;
+
+			wc.cbClsExtra		= 0;
+
+			wc.cbWndExtra		= 0;
+
+			wc.hInstance		= NULL;
+
+			wc.hIcon			= NULL;
+
+			wc.hCursor			= NULL;
+
+			wc.hbrBackground	= NULL;
+
+			wc.lpszMenuName		= NULL;
+
+			wc.lpszClassName	= windowClassName;
+
+
+
+			atom = RegisterClass(&wc);
+
+			require_action( atom != NULL, exit, hr = E_FAIL );
+
+
+
+			m_registeredWindowClass = TRUE;
+
+		}
+
+
+
+		m_hiddenWindow = CreateWindow( windowClassName, windowClassName, WS_OVERLAPPED, 0, 0, 0, 0, NULL, NULL, GetModuleHandle( NULL ), NULL );
+
+		require_action( m_hiddenWindow != NULL, exit, hr = E_FAIL );
+
+	}
+
+
+
+	err = WSAAsyncSelect( DNSServiceRefSockFD( m_primary ), m_hiddenWindow, WM_SOCKET, FD_READ );
+
+	require_action( !err, exit, hr = E_FAIL );
+
+
+
+	m_socketMap[ DNSServiceRefSockFD( m_primary ) ] = this;
+
+
+
+exit:
+
+
+
+	return hr;
+
+}
+
+
+
+
+
+void CDNSSDService::FinalRelease()
+
+{
+
+	dlog( kDebugLevelTrace, "FinalRelease()\n" ); 
+
+	Stop();
+
+}
+
+
+
+
+
+STDMETHODIMP CDNSSDService::EnumerateDomains(DNSSDFlags flags, ULONG ifIndex, IDNSSDEventManager *eventManager, IDNSSDService **service)
+
+{
+
+	CComObject<CDNSSDService>	*	object	= NULL;
+
+	DNSServiceRef					subord	= NULL;
+
+	DNSServiceErrorType				err		= 0;
+
+	HRESULT							hr		= 0;
+
+
+
+	check( m_primary );
+
+
+
+	// Initialize
+
+	*service = NULL;
+
+
+
+	try
+
+	{
+
+		object = new CComObject<CDNSSDService>();
+
+	}
+
+	catch ( ... )
+
+	{
+
+		object = NULL;
+
+	}
+
+
+
+	require_action( object != NULL, exit, err = kDNSServiceErr_NoMemory );
+
+	object->AddRef();
+
+
+
+	subord = m_primary;
+
+	err = DNSServiceEnumerateDomains( &subord, flags | kDNSServiceFlagsShareConnection, ifIndex, ( DNSServiceDomainEnumReply ) &DomainEnumReply, object );
+
+	require_noerr( err, exit );
+
+
+
+	object->SetPrimaryRef( m_primary );
+
+	object->SetSubordRef( subord );
+
+	object->SetEventManager( eventManager );
+
+
+
+	*service = object;
+
+
+
+exit:
+
+
+
+	if ( err && object )
+
+	{
+
+		object->Release();
+
+	}
+
+
+
+	return err;
+
+}
+
+
+
+
+
+STDMETHODIMP CDNSSDService::Browse(DNSSDFlags flags, ULONG ifIndex, BSTR regtype, BSTR domain, IDNSSDEventManager* eventManager, IDNSSDService** service )
+
+{
+
+	CComObject<CDNSSDService>	*	object		= NULL;
+
+	std::string						regtypeUTF8;
+
+	std::string						domainUTF8;
+
+	DNSServiceRef					subord		= NULL;
+
+	DNSServiceErrorType				err			= 0;
+
+	HRESULT							hr			= 0;
+
+	BOOL							ok;
+
+
+
+	check( m_primary );
+
+
+
+	// Initialize
+
+	*service = NULL;
+
+
+
+	// Convert BSTR params to utf8
+
+	ok = BSTRToUTF8( regtype, regtypeUTF8 );
+
+	require_action( ok, exit, err = kDNSServiceErr_BadParam );
+
+	ok = BSTRToUTF8( domain, domainUTF8 );
+
+	require_action( ok, exit, err = kDNSServiceErr_BadParam );
+
+
+
+	try
+
+	{
+
+		object = new CComObject<CDNSSDService>();
+
+	}
+
+	catch ( ... )
+
+	{
+
+		object = NULL;
+
+	}
+
+
+
+	require_action( object != NULL, exit, err = kDNSServiceErr_NoMemory );
+
+	object->AddRef();
+
+
+
+	subord = m_primary;
+
+	err = DNSServiceBrowse( &subord, flags | kDNSServiceFlagsShareConnection, ifIndex, regtypeUTF8.c_str(), ( domainUTF8.size() > 0 ) ? domainUTF8.c_str() : NULL, ( DNSServiceBrowseReply ) &BrowseReply, object );
+
+	require_noerr( err, exit );
+
+
+
+	object->SetPrimaryRef( m_primary );
+
+	object->SetSubordRef( subord );
+
+	object->SetEventManager( eventManager );
+
+
+
+	*service = object;
+
+
+
+exit:
+
+
+
+	if ( err && object )
+
+	{
+
+		object->Release();
+
+	}
+
+
+
+	return err;
+
+}
+
+
+
+
+
+STDMETHODIMP CDNSSDService::Resolve(DNSSDFlags flags, ULONG ifIndex, BSTR serviceName, BSTR regType, BSTR domain, IDNSSDEventManager* eventManager, IDNSSDService** service)
+
+{
+
+	CComObject<CDNSSDService>	*	object			= NULL;
+
+	std::string						serviceNameUTF8;
+
+	std::string						regTypeUTF8;
+
+	std::string						domainUTF8;
+
+	DNSServiceRef					subord			= NULL;
+
+	DNSServiceErrorType				err				= 0;
+
+	HRESULT							hr				= 0;
+
+	BOOL							ok;
+
+
+
+	check( m_primary );
+
+
+
+	// Initialize
+
+	*service = NULL;
+
+
+
+	// Convert BSTR params to utf8
+
+	ok = BSTRToUTF8( serviceName, serviceNameUTF8 );
+
+	require_action( ok, exit, err = kDNSServiceErr_BadParam );
+
+	ok = BSTRToUTF8( regType, regTypeUTF8 );
+
+	require_action( ok, exit, err = kDNSServiceErr_BadParam );
+
+	ok = BSTRToUTF8( domain, domainUTF8 );
+
+	require_action( ok, exit, err = kDNSServiceErr_BadParam );
+
+
+
+	try
+
+	{
+
+		object = new CComObject<CDNSSDService>();
+
+	}
+
+	catch ( ... )
+
+	{
+
+		object = NULL;
+
+	}
+
+
+
+	require_action( object != NULL, exit, err = kDNSServiceErr_NoMemory );
+
+	object->AddRef();
+
+
+
+	subord = m_primary;
+
+	err = DNSServiceResolve( &subord, flags | kDNSServiceFlagsShareConnection, ifIndex, serviceNameUTF8.c_str(), regTypeUTF8.c_str(), domainUTF8.c_str(), ( DNSServiceResolveReply ) &ResolveReply, object );
+
+	require_noerr( err, exit );
+
+
+
+	object->SetPrimaryRef( m_primary );
+
+	object->SetSubordRef( subord );
+
+	object->SetEventManager( eventManager );
+
+
+
+	*service = object;
+
+
+
+exit:
+
+
+
+	if ( err && object )
+
+	{
+
+		object->Release();
+
+	}
+
+
+
+	return err;
+
+}
+
+
+
+
+
+STDMETHODIMP CDNSSDService::Register(DNSSDFlags flags, ULONG ifIndex, BSTR serviceName, BSTR regType, BSTR domain, BSTR host, USHORT port, ITXTRecord *record, IDNSSDEventManager *eventManager, IDNSSDService **service)
+
+{
+
+	CComObject<CDNSSDService>	*	object			= NULL;
+
+	std::string						serviceNameUTF8;
+
+	std::string						regTypeUTF8;
+
+	std::string						domainUTF8;
+
+	std::string						hostUTF8;
+
+	const void					*	txtRecord		= NULL;
+
+	uint16_t						txtLen			= 0;
+
+	DNSServiceRef					subord			= NULL;
+
+	DNSServiceErrorType				err				= 0;
+
+	HRESULT							hr				= 0;
+
+	BOOL							ok;
+
+
+
+	check( m_primary );
+
+
+
+	// Initialize
+
+	*service = NULL;
+
+
+
+	// Convert BSTR params to utf8
+
+	ok = BSTRToUTF8( serviceName, serviceNameUTF8 );
+
+	require_action( ok, exit, err = kDNSServiceErr_BadParam );
+
+	ok = BSTRToUTF8( regType, regTypeUTF8 );
+
+	require_action( ok, exit, err = kDNSServiceErr_BadParam );
+
+	ok = BSTRToUTF8( domain, domainUTF8 );
+
+	require_action( ok, exit, err = kDNSServiceErr_BadParam );
+
+	ok = BSTRToUTF8( host, hostUTF8 );
+
+	require_action( ok, exit, err = kDNSServiceErr_BadParam );
+
+
+
+	try
+
+	{
+
+		object = new CComObject<CDNSSDService>();
+
+	}
+
+	catch ( ... )
+
+	{
+
+		object = NULL;
+
+	}
+
+
+
+	require_action( object != NULL, exit, err = kDNSServiceErr_NoMemory );
+
+	object->AddRef();
+
+
+
+	if ( record )
+
+	{
+
+		CComObject< CTXTRecord > * realTXTRecord;
+
+
+
+		realTXTRecord = ( CComObject< CTXTRecord >* ) record;
+
+
+
+		txtRecord	= realTXTRecord->GetBytes();
+
+		txtLen		= realTXTRecord->GetLen();
+
+	}
+
+
+
+	subord = m_primary;
+
+	err = DNSServiceRegister( &subord, flags | kDNSServiceFlagsShareConnection, ifIndex, serviceNameUTF8.c_str(), regTypeUTF8.c_str(), ( domainUTF8.size() > 0 ) ? domainUTF8.c_str() : NULL, hostUTF8.c_str(), htons( port ), txtLen, txtRecord, ( DNSServiceRegisterReply ) &RegisterReply, object );
+
+	require_noerr( err, exit );
+
+
+
+	object->SetPrimaryRef( m_primary );
+
+	object->SetSubordRef( subord );
+
+	object->SetEventManager( eventManager );
+
+
+
+	*service = object;
+
+
+
+exit:
+
+
+
+	if ( err && object )
+
+	{
+
+		object->Release();
+
+	}
+
+
+
+	return err;
+
+}
+
+
+
+
+
+STDMETHODIMP CDNSSDService::QueryRecord(DNSSDFlags flags, ULONG ifIndex, BSTR fullname, DNSSDRRType rrtype, DNSSDRRClass rrclass, IDNSSDEventManager *eventManager, IDNSSDService **service)
+
+{
+
+	CComObject<CDNSSDService>	*	object			= NULL;
+
+	DNSServiceRef					subord			= NULL;
+
+	std::string						fullNameUTF8;
+
+	DNSServiceErrorType				err				= 0;
+
+	HRESULT							hr				= 0;
+
+	BOOL							ok;
+
+
+
+	check( m_primary );
+
+
+
+	// Initialize
+
+	*service = NULL;
+
+
+
+	// Convert BSTR params to utf8
+
+	ok = BSTRToUTF8( fullname, fullNameUTF8 );
+
+	require_action( ok, exit, err = kDNSServiceErr_BadParam );
+
+
+
+	try
+
+	{
+
+		object = new CComObject<CDNSSDService>();
+
+	}
+
+	catch ( ... )
+
+	{
+
+		object = NULL;
+
+	}
+
+
+
+	require_action( object != NULL, exit, err = kDNSServiceErr_NoMemory );
+
+	object->AddRef();
+
+
+
+	subord = m_primary;
+
+	err = DNSServiceQueryRecord( &subord, flags | kDNSServiceFlagsShareConnection, ifIndex, fullNameUTF8.c_str(), ( uint16_t ) rrtype, ( uint16_t ) rrclass, ( DNSServiceQueryRecordReply ) &QueryRecordReply, object );
+
+	require_noerr( err, exit );
+
+
+
+	object->SetPrimaryRef( m_primary );
+
+	object->SetSubordRef( subord );
+
+	object->SetEventManager( eventManager );
+
+
+
+	*service = object;
+
+
+
+exit:
+
+
+
+	if ( err && object )
+
+	{
+
+		object->Release();
+
+	}
+
+
+
+	return err;
+
+}
+
+
+
+
+
+STDMETHODIMP CDNSSDService::RegisterRecord(DNSSDFlags flags, ULONG ifIndex, BSTR fullName, DNSSDRRType rrtype, DNSSDRRClass rrclass, VARIANT rdata, ULONG ttl, IDNSSDEventManager* eventManager, IDNSSDRecord** record)
+
+{
+
+	CComObject<CDNSSDRecord>	*	object			= NULL;
+
+	DNSRecordRef					rref			= NULL;
+
+	std::string						fullNameUTF8;
+
+	std::vector< BYTE >				byteArray;
+
+	const void					*	byteArrayPtr	= NULL;
+
+	DNSServiceErrorType				err				= 0;
+
+	HRESULT							hr				= 0;
+
+	BOOL							ok;
+
+
+
+	check( m_primary );
+
+
+
+	// Initialize
+
+	*object = NULL;
+
+
+
+	// Convert BSTR params to utf8
+
+	ok = BSTRToUTF8( fullName, fullNameUTF8 );
+
+	require_action( ok, exit, err = kDNSServiceErr_BadParam );
+
+
+
+	// Convert the VARIANT
+
+	ok = VariantToByteArray( &rdata, byteArray );
+
+	require_action( ok, exit, err = kDNSServiceErr_Unknown );
+
+
+
+	try
+
+	{
+
+		object = new CComObject<CDNSSDRecord>();
+
+	}
+
+	catch ( ... )
+
+	{
+
+		object = NULL;
+
+	}
+
+
+
+	require_action( object != NULL, exit, err = kDNSServiceErr_NoMemory );
+
+	object->AddRef();
+
+
+
+	err = DNSServiceRegisterRecord( m_primary, &rref, flags, ifIndex, fullNameUTF8.c_str(), rrtype, rrclass, ( uint16_t ) byteArray.size(), byteArray.size() > 0 ? &byteArray[ 0 ] : NULL, ttl, &RegisterRecordReply, object );
+
+	require_noerr( err, exit );
+
+
+
+	object->SetServiceObject( this );
+
+	object->SetRecordRef( rref );
+
+	this->SetEventManager( eventManager );
+
+
+
+	*record = object;
+
+
+
+exit:
+
+
+
+	if ( err && object )
+
+	{
+
+		object->Release();
+
+	}
+
+
+
+	return err;
+
+}
+
+
+
+
+
+STDMETHODIMP CDNSSDService::AddRecord(DNSSDFlags flags, DNSSDRRType rrtype, VARIANT rdata, ULONG ttl, IDNSSDRecord ** record)
+
+{
+
+	CComObject<CDNSSDRecord>	*	object			= NULL;
+
+	DNSRecordRef					rref			= NULL;
+
+	std::vector< BYTE >				byteArray;
+
+	const void					*	byteArrayPtr	= NULL;
+
+	DNSServiceErrorType				err				= 0;
+
+	HRESULT							hr				= 0;
+
+	BOOL							ok;
+
+
+
+	check( m_primary );
+
+
+
+	// Initialize
+
+	*object = NULL;
+
+
+
+	// Convert the VARIANT
+
+	ok = VariantToByteArray( &rdata, byteArray );
+
+	require_action( ok, exit, err = kDNSServiceErr_Unknown );
+
+
+
+	try
+
+	{
+
+		object = new CComObject<CDNSSDRecord>();
+
+	}
+
+	catch ( ... )
+
+	{
+
+		object = NULL;
+
+	}
+
+
+
+	require_action( object != NULL, exit, err = kDNSServiceErr_NoMemory );
+
+	object->AddRef();
+
+
+
+	err = DNSServiceAddRecord( m_primary, &rref, flags, rrtype, ( uint16_t ) byteArray.size(), byteArray.size() > 0 ? &byteArray[ 0 ] : NULL, ttl );
+
+	require_noerr( err, exit );
+
+
+
+	object->SetServiceObject( this );
+
+	object->SetRecordRef( rref );
+
+
+
+	*record = object;
+
+
+
+exit:
+
+
+
+	if ( err && object )
+
+	{
+
+		object->Release();
+
+	}
+
+
+
+	return err;
+
+}
+
+
+
+STDMETHODIMP CDNSSDService::ReconfirmRecord(DNSSDFlags flags, ULONG ifIndex, BSTR fullName, DNSSDRRType rrtype, DNSSDRRClass rrclass, VARIANT rdata)
+
+{
+
+	std::string						fullNameUTF8;
+
+	std::vector< BYTE >				byteArray;
+
+	const void					*	byteArrayPtr	= NULL;
+
+	DNSServiceErrorType				err				= 0;
+
+	HRESULT							hr				= 0;
+
+	BOOL							ok;
+
+
+
+	// Convert BSTR params to utf8
+
+	ok = BSTRToUTF8( fullName, fullNameUTF8 );
+
+	require_action( ok, exit, err = kDNSServiceErr_BadParam );
+
+
+
+	// Convert the VARIANT
+
+	ok = VariantToByteArray( &rdata, byteArray );
+
+	require_action( ok, exit, err = kDNSServiceErr_Unknown );
+
+
+
+	err = DNSServiceReconfirmRecord( flags, ifIndex, fullNameUTF8.c_str(), rrtype, rrclass, ( uint16_t ) byteArray.size(), byteArray.size() > 0 ? &byteArray[ 0 ] : NULL );
+
+	require_noerr( err, exit );
+
+
+
+exit:
+
+
+
+	return err;
+
+}
+
+
+
+
+
+STDMETHODIMP CDNSSDService::GetProperty(BSTR prop, VARIANT * value )
+
+{
+
+	std::string			propUTF8;
+
+	std::vector< BYTE >	byteArray;
+
+	SAFEARRAY		*	psa			= NULL;
+
+	BYTE			*	pData		= NULL;
+
+	uint32_t			elems		= 0;
+
+	DNSServiceErrorType	err			= 0;
+
+	BOOL				ok = TRUE;
+
+
+
+	// Convert BSTR params to utf8
+
+	ok = BSTRToUTF8( prop, propUTF8 );
+
+	require_action( ok, exit, err = kDNSServiceErr_BadParam );
+
+
+
+	// Setup the byte array
+
+	require_action( V_VT( value ) == ( VT_ARRAY|VT_UI1 ), exit, err = kDNSServiceErr_Unknown );
+
+	psa = V_ARRAY( value );
+
+	require_action( psa, exit, err = kDNSServiceErr_Unknown );
+
+	require_action( SafeArrayGetDim( psa ) == 1, exit, err = kDNSServiceErr_Unknown );
+
+	byteArray.reserve( psa->rgsabound[0].cElements );
+
+	byteArray.assign( byteArray.capacity(), 0 );
+
+	elems = ( uint32_t ) byteArray.capacity();
+
+
+
+	// Call the function and package the return value in the Variant
+
+	err = DNSServiceGetProperty( propUTF8.c_str(), &byteArray[ 0 ], &elems );
+
+	require_noerr( err, exit );
+
+	ok = ByteArrayToVariant( &byteArray[ 0 ], elems, value );
+
+	require_action( ok, exit, err = kDNSSDError_Unknown );
+
+
+
+exit:
+
+
+
+	if ( psa )
+
+	{
+
+		SafeArrayUnaccessData( psa );
+
+		psa = NULL;
+
+	}
+
+
+
+	return err;
+
+}
+
+
+
+STDMETHODIMP CDNSSDService::GetAddrInfo(DNSSDFlags flags, ULONG ifIndex, DNSSDAddressFamily addressFamily, BSTR hostName, IDNSSDEventManager *eventManager, IDNSSDService **service)
+
+{
+
+	CComObject<CDNSSDService>	*	object			= NULL;
+
+	DNSServiceRef					subord			= NULL;
+
+	std::string						hostNameUTF8;
+
+	DNSServiceErrorType				err				= 0;
+
+	HRESULT							hr				= 0;
+
+	BOOL							ok;
+
+
+
+	check( m_primary );
+
+
+
+	// Initialize
+
+	*service = NULL;
+
+
+
+	// Convert BSTR params to utf8
+
+	ok = BSTRToUTF8( hostName, hostNameUTF8 );
+
+	require_action( ok, exit, err = kDNSServiceErr_BadParam );
+
+
+
+	try
+
+	{
+
+		object = new CComObject<CDNSSDService>();
+
+	}
+
+	catch ( ... )
+
+	{
+
+		object = NULL;
+
+	}
+
+
+
+	require_action( object != NULL, exit, err = kDNSServiceErr_NoMemory );
+
+	object->AddRef();
+
+
+
+	subord = m_primary;
+
+	err = DNSServiceGetAddrInfo( &subord, flags | kDNSServiceFlagsShareConnection, ifIndex, addressFamily, hostNameUTF8.c_str(), ( DNSServiceGetAddrInfoReply ) &GetAddrInfoReply, object );
+
+	require_noerr( err, exit );
+
+
+
+	object->SetPrimaryRef( m_primary );
+
+	object->SetSubordRef( subord );
+
+	object->SetEventManager( eventManager );
+
+
+
+	*service = object;
+
+
+
+exit:
+
+
+
+	if ( err && object )
+
+	{
+
+		object->Release();
+
+	}
+
+
+
+	return err;
+
+}
+
+
+
+
+
+STDMETHODIMP CDNSSDService::NATPortMappingCreate(DNSSDFlags flags, ULONG ifIndex, DNSSDAddressFamily addressFamily, DNSSDProtocol protocol, USHORT internalPort, USHORT externalPort, ULONG ttl, IDNSSDEventManager *eventManager, IDNSSDService **service)
+
+{
+
+	CComObject<CDNSSDService>	*	object			= NULL;
+
+	DNSServiceRef					subord			= NULL;
+
+	DNSServiceProtocol				prot			= 0;
+
+	DNSServiceErrorType				err				= 0;
+
+	HRESULT							hr				= 0;
+
+
+
+	check( m_primary );
+
+
+
+	// Initialize
+
+	*service = NULL;
+
+
+
+	try
+
+	{
+
+		object = new CComObject<CDNSSDService>();
+
+	}
+
+	catch ( ... )
+
+	{
+
+		object = NULL;
+
+	}
+
+
+
+	require_action( object != NULL, exit, err = kDNSServiceErr_NoMemory );
+
+	object->AddRef();
+
+
+
+	prot = ( addressFamily | protocol );
+
+
+
+	subord = m_primary;
+
+	err = DNSServiceNATPortMappingCreate( &subord, flags | kDNSServiceFlagsShareConnection, ifIndex, prot, htons( internalPort ), htons( externalPort ), ttl, ( DNSServiceNATPortMappingReply ) &NATPortMappingReply, object );
+
+	require_noerr( err, exit );
+
+
+
+	object->SetPrimaryRef( m_primary );
+
+	object->SetSubordRef( subord );
+
+	object->SetEventManager( eventManager );
+
+
+
+	*service = object;
+
+
+
+exit:
+
+
+
+	if ( err && object )
+
+	{
+
+		object->Release();
+
+	}
+
+
+
+	return err;
+
+}
+
+
+
+
+
+STDMETHODIMP CDNSSDService::Stop(void)
+
+{
+
+	if ( !m_stopped )
+
+	{
+
+		m_stopped = TRUE;
+
+
+
+		dlog( kDebugLevelTrace, "Stop()\n" );
+
+
+
+		if ( m_isPrimary && m_primary )
+
+		{
+
+			SocketMap::iterator it;
+
+
+
+			if ( m_hiddenWindow )
+
+			{
+
+				WSAAsyncSelect( DNSServiceRefSockFD( m_primary ), m_hiddenWindow, 0, 0 );
+
+			}
+
+
+
+			it = m_socketMap.find( DNSServiceRefSockFD( m_primary ) );
+
+
+
+			if ( it != m_socketMap.end() )
+
+			{
+
+				m_socketMap.erase( it );
+
+			}
+
+
+
+			DNSServiceRefDeallocate( m_primary );
+
+			m_primary = NULL;
+
+		}
+
+		else if ( m_subord )
+
+		{
+
+			DNSServiceRefDeallocate( m_subord );
+
+			m_subord = NULL;
+
+		}
+
+
+
+		if ( m_eventManager != NULL )
+
+		{
+
+			m_eventManager->Release();
+
+			m_eventManager = NULL;
+
+		}
+
+	}
+
+
+
+	return S_OK;
+
+}
+
+
+
+
+
+void DNSSD_API
+CDNSSDService::DomainEnumReply
+    (
+    DNSServiceRef                       sdRef,
+    DNSServiceFlags                     flags,
+    uint32_t                            ifIndex,
+    DNSServiceErrorType                 errorCode,
+    const char                          *replyDomainUTF8,
+    void                                *context
+    )
+
+{
+
+	CComObject<CDNSSDService>	* service		= NULL;
+
+	CDNSSDEventManager			* eventManager	= NULL;
+
+	int err = 0;
+
+	
+
+	service = ( CComObject< CDNSSDService>* ) context;
+
+	require_action( service, exit, err = kDNSServiceErr_Unknown );
+
+
+
+	if ( service->ShouldHandleReply( errorCode, eventManager ) )
+
+	{
+
+		CComBSTR replyDomain;
+
+		BOOL ok;
+
+		
+
+		ok = UTF8ToBSTR( replyDomainUTF8, replyDomain );
+
+		require_action( ok, exit, err = kDNSServiceErr_Unknown );
+
+
+
+		if ( flags & kDNSServiceFlagsAdd )
+
+		{
+
+			eventManager->Fire_DomainFound( service, ( DNSSDFlags ) flags, ifIndex, replyDomain );
+
+		}
+
+		else
+
+		{
+
+			eventManager->Fire_DomainLost( service, ( DNSSDFlags ) flags, ifIndex, replyDomain );
+
+		}
+
+	}
+
+
+
+exit:
+
+
+
+	return;
+
+}
+
+
+
+
+
+void DNSSD_API
+CDNSSDService::BrowseReply
+		(
+		DNSServiceRef                       sdRef,
+		DNSServiceFlags                     flags,
+		uint32_t                            ifIndex,
+		DNSServiceErrorType                 errorCode,
+		const char                          *serviceNameUTF8,
+		const char                          *regTypeUTF8,
+		const char                          *replyDomainUTF8,
+		void                                *context
+		)
+
+{
+
+	CComObject<CDNSSDService>	* service		= NULL;
+
+	CDNSSDEventManager			* eventManager	= NULL;
+
+	int err = 0;
+
+	
+
+	service = ( CComObject< CDNSSDService>* ) context;
+
+	require_action( service, exit, err = kDNSServiceErr_Unknown );
+
+
+
+	if ( service->ShouldHandleReply( errorCode, eventManager ) )
+
+	{
+
+		CComBSTR	serviceName;
+
+		CComBSTR	regType;
+
+		CComBSTR	replyDomain;
+
+	
+
+		UTF8ToBSTR( serviceNameUTF8, serviceName );
+
+		UTF8ToBSTR( regTypeUTF8, regType );
+
+		UTF8ToBSTR( replyDomainUTF8, replyDomain );
+
+
+
+		if ( flags & kDNSServiceFlagsAdd )
+
+		{
+
+			eventManager->Fire_ServiceFound( service, ( DNSSDFlags ) flags, ifIndex, serviceName, regType, replyDomain );
+
+		}
+
+		else
+
+		{
+
+			eventManager->Fire_ServiceLost( service, ( DNSSDFlags ) flags, ifIndex, serviceName, regType, replyDomain );
+
+		}
+
+	}
+
+
+
+exit:
+
+
+
+	return;
+
+}
+
+
+
+
+
+void DNSSD_API
+
+CDNSSDService::ResolveReply
+
+		(
+		DNSServiceRef                       sdRef,
+		DNSServiceFlags                     flags,
+		uint32_t                            ifIndex,
+		DNSServiceErrorType                 errorCode,
+		const char                          *fullNameUTF8,
+		const char                          *hostNameUTF8,
+		uint16_t                            port,
+		uint16_t                            txtLen,
+		const unsigned char                 *txtRecord,
+		void                                *context
+
+		)
+
+{
+
+	CComObject<CDNSSDService>	* service		= NULL;
+
+	CDNSSDEventManager			* eventManager	= NULL;
+
+	int err = 0;
+
+	
+
+	service = ( CComObject< CDNSSDService>* ) context;
+
+	require_action( service, exit, err = kDNSServiceErr_Unknown );
+
+
+
+	if ( service->ShouldHandleReply( errorCode, eventManager ) )
+
+	{
+
+		CComBSTR					fullName;
+
+		CComBSTR					hostName;
+
+		CComBSTR					regType;
+
+		CComBSTR					replyDomain;
+
+		CComObject< CTXTRecord >*	record;
+
+		BOOL						ok;
+
+
+
+		ok = UTF8ToBSTR( fullNameUTF8, fullName );
+
+		require_action( ok, exit, err = kDNSServiceErr_Unknown );
+
+		ok = UTF8ToBSTR( hostNameUTF8, hostName );
+
+		require_action( ok, exit, err = kDNSServiceErr_Unknown );
+
+
+
+		try
+
+		{
+
+			record = new CComObject<CTXTRecord>();
+
+		}
+
+		catch ( ... )
+
+		{
+
+			record = NULL;
+
+		}
+
+
+
+		require_action( record, exit, err = kDNSServiceErr_NoMemory );
+
+		record->AddRef();
+
+
+
+		if ( txtLen > 0 )
+
+		{
+
+			record->SetBytes( txtRecord, txtLen );
+
+		}
+
+
+
+		eventManager->Fire_ServiceResolved( service, ( DNSSDFlags ) flags, ifIndex, fullName, hostName, ntohs( port ), record );
+
+	}
+
+
+
+exit:
+
+
+
+	return;
+
+}
+
+
+
+
+
+void DNSSD_API
+CDNSSDService::RegisterReply
+		(
+		DNSServiceRef                       sdRef,
+		DNSServiceFlags                     flags,
+		DNSServiceErrorType                 errorCode,
+		const char                          *serviceNameUTF8,
+		const char                          *regTypeUTF8,
+		const char                          *domainUTF8,
+		void                                *context
+		)
+
+{
+
+	CComObject<CDNSSDService>	* service		= NULL;
+
+	CDNSSDEventManager			* eventManager	= NULL;
+
+	int err = 0;
+
+	
+
+	service = ( CComObject< CDNSSDService>* ) context;
+
+	require_action( service, exit, err = kDNSServiceErr_Unknown );
+
+
+
+	if ( service->ShouldHandleReply( errorCode, eventManager ) )
+
+	{
+
+		CComBSTR					serviceName;
+
+		CComBSTR					regType;
+
+		CComBSTR					domain;
+
+		BOOL						ok;
+
+
+
+		ok = UTF8ToBSTR( serviceNameUTF8, serviceName );
+
+		require_action( ok, exit, err = kDNSServiceErr_Unknown );
+
+		ok = UTF8ToBSTR( regTypeUTF8, regType );
+
+		require_action( ok, exit, err = kDNSServiceErr_Unknown );
+
+		ok = UTF8ToBSTR( domainUTF8, domain );
+
+		require_action( ok, exit, err = kDNSServiceErr_Unknown );
+
+
+
+		eventManager->Fire_ServiceRegistered( service, ( DNSSDFlags ) flags, serviceName, regType, domain );
+
+	}
+
+
+
+exit:
+
+
+
+	return;
+
+}
+
+
+
+
+
+void DNSSD_API
+CDNSSDService::QueryRecordReply
+		(
+		DNSServiceRef                       sdRef,
+		DNSServiceFlags                     flags,
+		uint32_t                            ifIndex,
+		DNSServiceErrorType                 errorCode,
+		const char                          *fullNameUTF8,
+		uint16_t                            rrtype,
+		uint16_t                            rrclass,
+		uint16_t                            rdlen,
+		const void                          *rdata,
+		uint32_t                            ttl,
+		void                                *context
+		)
+
+{
+
+	CComObject<CDNSSDService>	* service		= NULL;
+
+	CDNSSDEventManager			* eventManager	= NULL;
+
+	int err = 0;
+
+	
+
+	service = ( CComObject< CDNSSDService>* ) context;
+
+	require_action( service, exit, err = kDNSServiceErr_Unknown );
+
+
+
+	if ( service->ShouldHandleReply( errorCode, eventManager ) )
+
+	{
+
+		CComBSTR	fullName;
+
+		VARIANT		var;
+
+		BOOL		ok;
+
+
+
+		ok = UTF8ToBSTR( fullNameUTF8, fullName );
+
+		require_action( ok, exit, err = kDNSServiceErr_Unknown );
+
+		ok = ByteArrayToVariant( rdata, rdlen, &var );
+
+		require_action( ok, exit, err = kDNSServiceErr_Unknown );
+
+
+
+		eventManager->Fire_QueryRecordAnswered( service, ( DNSSDFlags ) flags, ifIndex, fullName, ( DNSSDRRType ) rrtype, ( DNSSDRRClass ) rrclass, var, ttl );
+
+	}
+
+
+
+exit:
+
+
+
+	return;
+
+}
+
+
+
+
+
+void DNSSD_API
+CDNSSDService::GetAddrInfoReply
+		(
+		DNSServiceRef                    sdRef,
+		DNSServiceFlags                  flags,
+		uint32_t                         ifIndex,
+		DNSServiceErrorType              errorCode,
+		const char                       *hostNameUTF8,
+		const struct sockaddr            *rawAddress,
+		uint32_t                         ttl,
+		void                             *context
+		)
+
+{
+
+	CComObject<CDNSSDService>	* service		= NULL;
+
+	CDNSSDEventManager			* eventManager	= NULL;
+
+	int err = 0;
+
+	
+
+	service = ( CComObject< CDNSSDService>* ) context;
+
+	require_action( service, exit, err = kDNSServiceErr_Unknown );
+
+
+
+	if ( service->ShouldHandleReply( errorCode, eventManager ) )
+
+	{
+
+		CComBSTR			hostName;
+
+		DWORD				sockaddrLen;
+
+		DNSSDAddressFamily	addressFamily;
+
+		char				addressUTF8[INET6_ADDRSTRLEN];
+
+		DWORD				addressLen = sizeof( addressUTF8 );
+
+		CComBSTR			address;
+
+		BOOL				ok;
+
+
+
+		ok = UTF8ToBSTR( hostNameUTF8, hostName );
+
+		require_action( ok, exit, err = kDNSServiceErr_Unknown );
+
+
+
+		switch ( rawAddress->sa_family )
+
+		{
+
+			case AF_INET:
+
+			{
+
+				addressFamily	= kDNSSDAddressFamily_IPv4;
+
+				sockaddrLen		= sizeof( sockaddr_in );
+
+			}
+
+			break;
+
+
+
+			case AF_INET6:
+
+			{
+
+				addressFamily	= kDNSSDAddressFamily_IPv6;
+
+				sockaddrLen		= sizeof( sockaddr_in6 );
+
+			}
+
+			break;
+
+		}
+
+
+
+		err = WSAAddressToStringA( ( LPSOCKADDR ) rawAddress, sockaddrLen, NULL, addressUTF8, &addressLen );
+
+		require_noerr( err, exit );
+
+		ok = UTF8ToBSTR( addressUTF8, address );
+
+		require_action( ok, exit, err = kDNSServiceErr_Unknown );
+
+
+
+		eventManager->Fire_AddressFound( service, ( DNSSDFlags ) flags, ifIndex, hostName, addressFamily, address, ttl );
+
+	}
+
+
+
+exit:
+
+
+
+	return;
+
+}
+
+
+
+
+
+void DNSSD_API
+CDNSSDService::NATPortMappingReply
+    (
+    DNSServiceRef                    sdRef,
+    DNSServiceFlags                  flags,
+    uint32_t                         ifIndex,
+    DNSServiceErrorType              errorCode,
+    uint32_t                         externalAddress,   /* four byte IPv4 address in network byte order */
+    DNSServiceProtocol               protocol,
+    uint16_t                         internalPort,
+    uint16_t                         externalPort,      /* may be different than the requested port     */
+    uint32_t                         ttl,               /* may be different than the requested ttl      */
+    void                             *context
+    )
+
+{
+
+	CComObject<CDNSSDService>	* service		= NULL;
+
+	CDNSSDEventManager			* eventManager	= NULL;
+
+	int err = 0;
+
+	
+
+	service = ( CComObject< CDNSSDService>* ) context;
+
+	require_action( service, exit, err = kDNSServiceErr_Unknown );
+
+
+
+	if ( service->ShouldHandleReply( errorCode, eventManager ) )
+
+	{
+
+		eventManager->Fire_MappingCreated( service, ( DNSSDFlags ) flags, ifIndex, externalAddress, ( DNSSDAddressFamily ) ( protocol & 0x8 ), ( DNSSDProtocol ) ( protocol & 0x80 ), ntohs( internalPort ), ntohs( externalPort ), ttl  );
+
+	}
+
+
+
+exit:
+
+
+
+	return;
+
+}
+
+
+
+
+
+void DNSSD_API
+CDNSSDService::RegisterRecordReply
+		(
+		DNSServiceRef		sdRef,
+		DNSRecordRef		RecordRef,
+		DNSServiceFlags		flags,
+		DNSServiceErrorType	errorCode,
+		void				*context
+		)
+
+{
+
+	CComObject<CDNSSDRecord>	* record		= NULL;
+
+	CDNSSDService				* service		= NULL;
+
+	CDNSSDEventManager			* eventManager	= NULL;
+
+	int err = 0;
+
+	
+
+	record = ( CComObject< CDNSSDRecord >* ) context;
+
+	require_action( record, exit, err = kDNSServiceErr_Unknown );
+
+	service = record->GetServiceObject();
+
+	require_action( service, exit, err = kDNSServiceErr_Unknown );
+
+
+
+	if ( service->ShouldHandleReply( errorCode, eventManager ) )
+
+	{
+
+		eventManager->Fire_RecordRegistered( record, ( DNSSDFlags ) flags );
+
+	}
+
+
+
+exit:
+
+
+
+	return;
+
+}
+
+
+
+
+
+BOOL
+
+CDNSSDService::ShouldHandleReply( DNSServiceErrorType errorCode, CDNSSDEventManager *& eventManager )
+
+{
+
+	BOOL ok = FALSE;
+
+
+
+	if ( !this->Stopped() )
+
+	{
+
+		eventManager = this->GetEventManager();
+
+		require_action( eventManager, exit, ok = FALSE );
+
+
+
+		if ( !errorCode )
+
+		{
+
+			ok = TRUE;
+
+		}
+
+		else
+
+		{
+
+			eventManager->Fire_OperationFailed( this, ( DNSSDError ) errorCode );
+
+		}
+
+	}
+
+
+
+exit:
+
+
+
+	return ok;
+
+}
+
+
+
+
+
+LRESULT CALLBACK
+
+CDNSSDService::WndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
+
+{
+
+	if ( msg == WM_SOCKET )
+
+	{
+
+		SocketMap::iterator it;
+
+			
+
+		it = m_socketMap.find( ( SOCKET ) wParam );
+
+		check( it != m_socketMap.end() );
+
+
+
+		if ( it != m_socketMap.end() )
+
+		{
+
+			DNSServiceProcessResult( it->second->m_primary );
+
+		}
+
+	}
+
+
+
+	return DefWindowProc(hWnd, msg, wParam, lParam);;
+
+}
+
diff --git a/mdnsresponder/mDNSWindows/DLLX/DNSSDService.h b/mdnsresponder/mDNSWindows/DLLX/DNSSDService.h
new file mode 100755
index 0000000..5eb8dcb
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DLLX/DNSSDService.h
@@ -0,0 +1,429 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2009 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+
+
+#pragma once
+
+#include "resource.h"       // main symbols
+
+
+
+#include "DLLX.h"
+
+#include "DNSSDEventManager.h"
+
+#include <CommonServices.h>
+
+#include <DebugServices.h>
+
+#include <dns_sd.h>
+
+#include <map>
+
+
+
+
+
+#if defined(_WIN32_WCE) && !defined(_CE_DCOM) && !defined(_CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA)
+
+#error "Single-threaded COM objects are not properly supported on Windows CE platform, such as the Windows Mobile platforms that do not include full DCOM support. Define _CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA to force ATL to support creating single-thread COM object's and allow use of it's single-threaded COM object implementations. The threading model in your rgs file was set to 'Free' as that is the only threading model supported in non DCOM Windows CE platforms."
+
+#endif
+
+
+
+
+
+
+
+// CDNSSDService
+
+
+
+class ATL_NO_VTABLE CDNSSDService :
+
+	public CComObjectRootEx<CComSingleThreadModel>,
+
+	public CComCoClass<CDNSSDService, &CLSID_DNSSDService>,
+
+	public IDispatchImpl<IDNSSDService, &IID_IDNSSDService, &LIBID_Bonjour, /*wMajor =*/ 1, /*wMinor =*/ 0>
+
+{
+
+public:
+
+
+
+	typedef CComObjectRootEx<CComSingleThreadModel> Super;
+
+
+
+	CDNSSDService()
+
+	:
+
+		m_isPrimary( FALSE ),
+
+		m_eventManager( NULL ),
+
+		m_stopped( FALSE ),
+
+		m_primary( NULL ),
+
+		m_subord( NULL )
+
+	{
+
+	}
+
+
+
+DECLARE_REGISTRY_RESOURCEID(IDR_DNSSDSERVICE)
+
+
+
+
+
+BEGIN_COM_MAP(CDNSSDService)
+
+	COM_INTERFACE_ENTRY(IDNSSDService)
+
+	COM_INTERFACE_ENTRY(IDispatch)
+
+END_COM_MAP()
+
+
+
+	DECLARE_PROTECT_FINAL_CONSTRUCT()
+
+
+
+	HRESULT
+
+	FinalConstruct();
+
+
+
+	void 
+
+	FinalRelease();
+
+
+
+public:
+
+
+
+	inline DNSServiceRef
+
+	GetPrimaryRef()
+
+	{
+
+		return m_primary;
+
+	}
+
+
+
+	inline void
+
+	SetPrimaryRef( DNSServiceRef primary )
+
+	{
+
+		m_primary = primary;
+
+	}
+
+
+
+	inline DNSServiceRef
+
+	GetSubordRef()
+
+	{
+
+		return m_subord;
+
+	}
+
+
+
+	inline void
+
+	SetSubordRef( DNSServiceRef subord )
+
+	{
+
+		m_subord = subord;
+
+	}
+
+
+
+	inline CDNSSDEventManager*
+
+	GetEventManager()
+
+	{
+
+		return m_eventManager;
+
+	}
+
+
+
+	inline void
+
+	SetEventManager( IDNSSDEventManager * eventManager )
+
+	{
+
+		if ( m_eventManager )
+
+		{
+
+			m_eventManager->Release();
+
+			m_eventManager = NULL;
+
+		}
+
+
+
+		if ( eventManager )
+
+		{
+
+			m_eventManager = dynamic_cast< CDNSSDEventManager* >( eventManager );
+
+			check( m_eventManager );
+
+			m_eventManager->AddRef();
+
+		}
+
+	}
+
+
+
+	inline BOOL
+
+	Stopped()
+
+	{
+
+		return m_stopped;
+
+	}
+
+
+
+private:
+
+
+
+	static void DNSSD_API
+	DomainEnumReply
+		(
+		DNSServiceRef                       sdRef,
+		DNSServiceFlags                     flags,
+		uint32_t                            ifIndex,
+		DNSServiceErrorType                 errorCode,
+		const char                          *replyDomain,
+		void                                *context
+		);
+
+
+
+	static void DNSSD_API
+	BrowseReply
+		(
+		DNSServiceRef                       sdRef,
+		DNSServiceFlags                     flags,
+		uint32_t                            interfaceIndex,
+		DNSServiceErrorType                 errorCode,
+		const char                          *serviceName,
+		const char                          *regtype,
+		const char                          *replyDomain,
+		void                                *context
+		);
+
+
+
+	static void DNSSD_API
+
+	ResolveReply
+
+		(
+		DNSServiceRef                       sdRef,
+		DNSServiceFlags                     flags,
+		uint32_t                            interfaceIndex,
+		DNSServiceErrorType                 errorCode,
+		const char                          *fullname,
+		const char                          *hosttarget,
+		uint16_t                            port,
+		uint16_t                            txtLen,
+		const unsigned char                 *txtRecord,
+		void                                *context
+
+		);
+
+
+
+	static void DNSSD_API
+	RegisterReply
+		(
+		DNSServiceRef                       sdRef,
+		DNSServiceFlags                     flags,
+		DNSServiceErrorType                 errorCode,
+		const char                          *name,
+		const char                          *regtype,
+		const char                          *domain,
+		void                                *context
+		);
+
+
+
+	static void DNSSD_API
+	QueryRecordReply
+		(
+		DNSServiceRef                       sdRef,
+		DNSServiceFlags                     flags,
+		uint32_t                            interfaceIndex,
+		DNSServiceErrorType                 errorCode,
+		const char                          *fullname,
+		uint16_t                            rrtype,
+		uint16_t                            rrclass,
+		uint16_t                            rdlen,
+		const void                          *rdata,
+		uint32_t                            ttl,
+		void                                *context
+		);
+
+
+
+	static void DNSSD_API
+    GetAddrInfoReply
+		(
+		DNSServiceRef                    sdRef,
+		DNSServiceFlags                  flags,
+		uint32_t                         interfaceIndex,
+		DNSServiceErrorType              errorCode,
+		const char                       *hostname,
+		const struct sockaddr            *address,
+		uint32_t                         ttl,
+		void                             *context
+		);
+
+
+
+	static void DNSSD_API
+	NATPortMappingReply
+		(
+		DNSServiceRef                    sdRef,
+		DNSServiceFlags                  flags,
+		uint32_t                         interfaceIndex,
+		DNSServiceErrorType              errorCode,
+		uint32_t                         externalAddress,   /* four byte IPv4 address in network byte order */
+		DNSServiceProtocol               protocol,
+		uint16_t                         internalPort,
+		uint16_t                         externalPort,      /* may be different than the requested port     */
+		uint32_t                         ttl,               /* may be different than the requested ttl      */
+		void                             *context
+		);
+
+
+
+	static void DNSSD_API
+	RegisterRecordReply
+		(
+		DNSServiceRef                       sdRef,
+		DNSRecordRef                        RecordRef,
+		DNSServiceFlags                     flags,
+		DNSServiceErrorType                 errorCode,
+		void                                *context
+		);
+
+
+
+	inline BOOL
+
+	ShouldHandleReply( DNSServiceErrorType errorCode, CDNSSDEventManager *& eventManager );
+
+	
+
+	static LRESULT CALLBACK
+
+	WndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam );
+
+
+
+	typedef std::map< SOCKET, CDNSSDService* > SocketMap;
+
+
+
+	static BOOL				m_registeredWindowClass;
+
+	static HWND				m_hiddenWindow;
+
+	static SocketMap		m_socketMap;
+
+	CDNSSDEventManager	*	m_eventManager;
+
+	BOOL					m_stopped;
+
+	BOOL					m_isPrimary;
+
+	DNSServiceRef			m_primary;
+
+	DNSServiceRef			m_subord;
+
+public:
+
+	STDMETHOD(EnumerateDomains)(DNSSDFlags flags, ULONG ifIndex, IDNSSDEventManager *eventManager, IDNSSDService **service);  
+
+	STDMETHOD(Browse)(DNSSDFlags flags, ULONG interfaceIndex, BSTR regtype, BSTR domain, IDNSSDEventManager* eventManager, IDNSSDService** sdref);
+
+	STDMETHOD(Resolve)(DNSSDFlags flags, ULONG ifIndex, BSTR serviceName, BSTR regType, BSTR domain, IDNSSDEventManager* eventManager, IDNSSDService** service);
+
+	STDMETHOD(Register)(DNSSDFlags flags, ULONG ifIndex, BSTR name, BSTR regType, BSTR domain, BSTR host, USHORT port, ITXTRecord *record, IDNSSDEventManager *eventManager, IDNSSDService **service);
+
+	STDMETHOD(QueryRecord)(DNSSDFlags flags, ULONG ifIndex, BSTR fullname, DNSSDRRType rrtype, DNSSDRRClass rrclass, IDNSSDEventManager *eventManager, IDNSSDService **service);      
+
+	STDMETHOD(RegisterRecord)(DNSSDFlags flags, ULONG ifIndex, BSTR fullname, DNSSDRRType rrtype, DNSSDRRClass rrclass, VARIANT rdata, ULONG ttl, IDNSSDEventManager* eventManager, IDNSSDRecord** record);
+
+	STDMETHOD(AddRecord)(DNSSDFlags flags, DNSSDRRType rrtype, VARIANT rdata, ULONG ttl, IDNSSDRecord ** record);
+
+	STDMETHOD(ReconfirmRecord)(DNSSDFlags flags, ULONG ifIndex, BSTR fullname, DNSSDRRType rrtype, DNSSDRRClass rrclass, VARIANT rdata);
+
+	STDMETHOD(GetProperty)(BSTR prop, VARIANT * value);
+
+	STDMETHOD(GetAddrInfo)(DNSSDFlags flags, ULONG ifIndex, DNSSDAddressFamily addressFamily, BSTR hostname, IDNSSDEventManager *eventManager, IDNSSDService **service);      
+
+	STDMETHOD(NATPortMappingCreate)(DNSSDFlags flags, ULONG ifIndex, DNSSDAddressFamily addressFamily, DNSSDProtocol protocol, USHORT internalPort, USHORT externalPort, ULONG ttl, IDNSSDEventManager *eventManager, IDNSSDService **service);
+
+	STDMETHOD(Stop)(void);
+
+};
+
+
+
+OBJECT_ENTRY_AUTO(__uuidof(DNSSDService), CDNSSDService)
+
diff --git a/mdnsresponder/mDNSWindows/DLLX/DNSSDService.rgs b/mdnsresponder/mDNSWindows/DLLX/DNSSDService.rgs
new file mode 100755
index 0000000..8a84297
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DLLX/DNSSDService.rgs
@@ -0,0 +1,27 @@
+HKCR

+{

+	Bonjour.DNSSDService.1 = s 'DNSSDService Class'

+	{

+		CLSID = s '{24CD4DE9-FF84-4701-9DC1-9B69E0D1090A}'

+	}

+	Bonjour.DNSSDService = s 'DNSSDService Class'

+	{

+		CLSID = s '{24CD4DE9-FF84-4701-9DC1-9B69E0D1090A}'

+		CurVer = s 'Bonjour.DNSSDService.1'

+	}

+	NoRemove CLSID

+	{

+		ForceRemove {24CD4DE9-FF84-4701-9DC1-9B69E0D1090A} = s 'DNSSDService Class'

+		{

+			ProgID = s 'Bonjour.DNSSDService.1'

+			VersionIndependentProgID = s 'Bonjour.DNSSDService'

+			ForceRemove 'Programmable'

+			InprocServer32 = s '%MODULE%'

+			{

+				val ThreadingModel = s 'Apartment'

+			}

+			val AppID = s '%APPID%'

+			'TypeLib' = s '{18FBED6D-F2B7-4EC8-A4A4-46282E635308}'

+		}

+	}

+}

diff --git a/mdnsresponder/mDNSWindows/DLLX/StringServices.cpp b/mdnsresponder/mDNSWindows/DLLX/StringServices.cpp
new file mode 100755
index 0000000..f00750d
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DLLX/StringServices.cpp
@@ -0,0 +1,344 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2009 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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 "StringServices.h"
+
+#include <DebugServices.h>
+
+
+
+
+
+extern BOOL
+
+BSTRToUTF8
+
+	(
+
+	BSTR			inString,
+
+	std::string	&	outString
+
+	)
+
+{
+
+	USES_CONVERSION;
+
+	
+
+	char	*	utf8String	= NULL;
+
+	OSStatus    err			= kNoErr;
+
+
+
+	outString = "";
+
+	if ( inString )
+
+	{
+		TCHAR	*	utf16String	= NULL;
+		size_t      size		= 0;
+
+
+		utf16String = OLE2T( inString );
+
+		require_action( utf16String != NULL, exit, err = kUnknownErr );
+
+
+
+		if ( wcslen( utf16String ) > 0 )
+
+		{
+
+			size = (size_t) WideCharToMultiByte( CP_UTF8, 0, utf16String, ( int ) wcslen( utf16String ), NULL, 0, NULL, NULL );
+
+			err = translate_errno( size != 0, GetLastError(), kUnknownErr );
+
+			require_noerr( err, exit );
+
+
+
+			try
+
+			{
+
+				utf8String = new char[ size + 1 ];
+
+			}
+
+			catch ( ... )
+
+			{
+
+				utf8String = NULL;
+
+			}
+
+
+
+			require_action( utf8String != NULL, exit, err = kNoMemoryErr );
+
+			size = (size_t) WideCharToMultiByte( CP_UTF8, 0, utf16String, ( int ) wcslen( utf16String ), utf8String, (int) size, NULL, NULL);
+
+			err = translate_errno( size != 0, GetLastError(), kUnknownErr );
+
+			require_noerr( err, exit );
+
+
+
+			// have to add the trailing 0 because WideCharToMultiByte doesn't do it,
+
+			// although it does return the correct size
+
+
+
+			utf8String[size] = '\0';
+
+			outString = utf8String;
+
+		}
+	}
+
+
+
+exit:
+
+
+
+	if ( utf8String != NULL )
+
+	{
+
+		delete [] utf8String;
+
+	}
+
+
+
+	return ( !err ) ? TRUE : FALSE;
+
+}
+
+
+
+
+
+extern BOOL
+
+UTF8ToBSTR
+
+	(
+
+	const char	*	inString,
+
+	CComBSTR	&	outString
+
+	)
+
+{
+
+	wchar_t	*	unicode	= NULL;
+
+	OSStatus	err		= 0;
+
+
+
+	if ( inString )
+
+	{
+		int n;
+
+		n = MultiByteToWideChar( CP_UTF8, 0, inString, -1, NULL, 0 );
+
+	    
+
+		if ( n > 0 )
+
+		{
+
+			try
+
+			{
+
+				unicode = new wchar_t[ n ];
+
+			}
+
+			catch ( ... )
+
+			{
+
+				unicode = NULL;
+
+			}
+
+
+
+			require_action( unicode, exit, err = ERROR_INSUFFICIENT_BUFFER );
+
+
+
+			n = MultiByteToWideChar( CP_UTF8, 0, inString, -1, unicode, n );
+
+		}
+
+
+
+		outString = unicode;
+
+	}
+
+
+exit:
+
+
+
+    if ( unicode != NULL )
+
+    {
+
+        delete [] unicode;
+
+	}
+
+
+
+	return ( !err ) ? TRUE : FALSE;
+
+}
+
+
+
+
+
+BOOL
+
+ByteArrayToVariant
+
+	(
+
+	const void	*	inArray,
+
+	size_t			inArrayLen,
+
+	VARIANT		*	outVariant
+
+	)
+
+{
+
+	LPBYTE			buf	= NULL;
+
+	HRESULT			hr	= 0;
+
+	BOOL			ok	= TRUE;
+
+
+
+	VariantClear( outVariant );
+
+	outVariant->vt		= VT_ARRAY|VT_UI1;
+
+	outVariant->parray	= SafeArrayCreateVector( VT_UI1, 0, ( ULONG ) inArrayLen );
+
+	require_action( outVariant->parray, exit, ok = FALSE );
+
+	hr = SafeArrayAccessData( outVariant->parray, (LPVOID *)&buf );
+
+	require_action( hr == S_OK, exit, ok = FALSE );
+
+	memcpy( buf, inArray, inArrayLen );
+
+	hr = SafeArrayUnaccessData( outVariant->parray );
+
+	require_action( hr == S_OK, exit, ok = FALSE );
+
+
+
+exit:
+
+
+
+	return ok;
+
+}
+
+
+
+
+
+extern BOOL
+
+VariantToByteArray
+
+	(
+
+	VARIANT				*	inVariant,
+
+	std::vector< BYTE >	&	outArray
+
+	)
+
+{
+
+	SAFEARRAY	*	psa			= NULL;
+
+	BYTE		*	pData		= NULL;
+
+	ULONG			cElements	= 0;
+
+	HRESULT			hr;
+
+	BOOL			ok = TRUE;
+
+
+
+	require_action( V_VT( inVariant ) == ( VT_ARRAY|VT_UI1 ), exit, ok = FALSE );
+
+	psa = V_ARRAY( inVariant );
+
+	require_action( psa, exit, ok = FALSE );
+
+	require_action( SafeArrayGetDim( psa ) == 1, exit, ok = FALSE );
+
+	hr = SafeArrayAccessData( psa, ( LPVOID* )&pData );
+
+	require_action( hr == S_OK, exit, ok = FALSE );
+
+	cElements = psa->rgsabound[0].cElements;
+
+	outArray.reserve( cElements );
+
+	outArray.assign( cElements, 0 );
+
+	memcpy( &outArray[ 0 ], pData, cElements );
+
+	SafeArrayUnaccessData( psa );
+
+
+
+exit:
+
+
+
+	return ok;
+
+}
\ No newline at end of file
diff --git a/mdnsresponder/mDNSWindows/DLLX/StringServices.h b/mdnsresponder/mDNSWindows/DLLX/StringServices.h
new file mode 100755
index 0000000..12aa996
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DLLX/StringServices.h
@@ -0,0 +1,102 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2009 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+
+
+#ifndef _StringServices_h
+
+#define _StringServices_h
+
+
+
+#include <atlbase.h>
+
+#include <vector>
+
+#include <string>
+
+
+
+
+
+extern BOOL
+
+BSTRToUTF8
+
+	(
+
+	BSTR			inString,
+
+	std::string	&	outString
+
+	);
+
+
+
+
+
+extern BOOL
+
+UTF8ToBSTR
+
+	(
+
+	const char	*	inString,
+
+	CComBSTR	&	outString
+
+	);
+
+
+
+
+
+extern BOOL
+
+ByteArrayToVariant
+
+	(
+
+	const void	*	inArray,
+
+	size_t			inArrayLen,
+
+	VARIANT		*	outVariant
+
+	);
+
+
+
+
+
+extern BOOL
+
+VariantToByteArray
+
+	(
+
+	VARIANT				*	inVariant,
+
+	std::vector< BYTE >	&	outArray
+
+	);
+
+
+
+
+
+#endif
\ No newline at end of file
diff --git a/mdnsresponder/mDNSWindows/DLLX/TXTRecord.cpp b/mdnsresponder/mDNSWindows/DLLX/TXTRecord.cpp
new file mode 100755
index 0000000..e5345f8
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DLLX/TXTRecord.cpp
@@ -0,0 +1,381 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2009 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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 "stdafx.h"
+
+#include "TXTRecord.h"
+
+#include "StringServices.h"
+
+#include <DebugServices.h>
+
+
+
+
+
+// CTXTRecord
+
+
+
+
+
+STDMETHODIMP CTXTRecord::SetValue(BSTR key, VARIANT value)
+
+{
+
+	std::string			keyUTF8;
+
+	ByteArray			valueArray;
+
+	BOOL				ok;
+
+	DNSServiceErrorType	err;
+
+	HRESULT				hr = S_OK;
+
+
+
+	if ( !m_allocated )
+
+	{
+
+		TXTRecordCreate( &m_tref, 0, NULL );
+
+		m_allocated = TRUE;
+
+	}
+
+
+
+	ok = BSTRToUTF8( key, keyUTF8 );
+
+	require_action( ok, exit, hr = S_FALSE );
+
+
+
+	ok = VariantToByteArray( &value, valueArray );
+
+	require_action( ok, exit, hr = S_FALSE );
+
+
+
+	err = TXTRecordSetValue( &m_tref, keyUTF8.c_str(), ( uint8_t ) valueArray.size(), &valueArray[ 0 ] );
+
+	require_action( !err, exit, hr = S_FALSE );
+
+
+
+exit:
+
+
+
+	return hr;
+
+}
+
+
+
+STDMETHODIMP CTXTRecord::RemoveValue(BSTR key)
+
+{
+
+	HRESULT hr = S_OK;
+
+
+
+	if ( m_allocated )
+
+	{
+
+		std::string			keyUTF8;
+
+		BOOL				ok;
+
+		DNSServiceErrorType	err;
+
+
+
+		ok = BSTRToUTF8( key, keyUTF8 );
+
+		require_action( ok, exit, hr = S_FALSE );
+
+
+
+		err = TXTRecordRemoveValue( &m_tref, keyUTF8.c_str() );
+
+		require_action( !err, exit, hr = S_FALSE );
+
+	}
+
+
+
+exit:
+
+
+
+	return hr;
+
+}
+
+
+
+STDMETHODIMP CTXTRecord::ContainsKey(BSTR key, VARIANT_BOOL* retval)
+
+{
+
+	std::string keyUTF8;
+
+	int			ret	= 0;
+
+	HRESULT		err	= S_OK;
+
+
+
+	if ( m_byteArray.size() > 0 )
+
+	{
+
+		BOOL ok;
+
+
+
+		ok = BSTRToUTF8( key, keyUTF8 );
+
+		require_action( ok, exit, err = S_FALSE );
+
+
+
+		ret = TXTRecordContainsKey( ( uint16_t ) m_byteArray.size(), &m_byteArray[ 0 ], keyUTF8.c_str() );
+
+	}
+
+
+
+	*retval = ( ret ) ? VARIANT_TRUE : VARIANT_FALSE;
+
+
+
+exit:
+
+
+
+	return err;
+
+}
+
+
+
+STDMETHODIMP CTXTRecord::GetValueForKey(BSTR key, VARIANT* value)
+
+{
+
+	std::string		keyUTF8;
+
+	const void	*	rawValue;
+
+	uint8_t			rawValueLen;
+
+	BOOL			ok	= TRUE;
+
+	HRESULT			hr	= S_OK;
+
+
+
+	VariantClear( value );
+
+
+
+	if ( m_byteArray.size() > 0 )
+
+	{
+
+		ok = BSTRToUTF8( key, keyUTF8 );
+
+		require_action( ok, exit, hr = S_FALSE );
+
+
+
+		rawValue = TXTRecordGetValuePtr( ( uint16_t ) m_byteArray.size(), &m_byteArray[ 0 ], keyUTF8.c_str(), &rawValueLen );
+
+
+
+		if ( rawValue )
+
+		{
+
+			ok = ByteArrayToVariant( rawValue, rawValueLen, value );
+
+			require_action( ok, exit, hr = S_FALSE );
+
+		}
+
+	}
+
+
+
+exit:
+
+
+
+	return hr;
+
+}
+
+
+
+STDMETHODIMP CTXTRecord::GetCount(ULONG* count)
+
+{
+
+	*count = 0;
+
+
+
+	if ( m_byteArray.size() > 0 )
+
+	{
+
+		*count = TXTRecordGetCount( ( uint16_t ) m_byteArray.size(), &m_byteArray[ 0 ] );
+
+	}
+
+
+
+	return S_OK;
+
+}
+
+
+
+STDMETHODIMP CTXTRecord::GetKeyAtIndex(ULONG index, BSTR* retval)
+
+{
+
+	char				keyBuf[ 64 ];
+
+	uint8_t				rawValueLen;
+
+	const void		*	rawValue;
+
+	CComBSTR			temp;
+
+	DNSServiceErrorType	err;
+
+	BOOL				ok;
+
+	HRESULT				hr = S_OK;
+
+
+
+	err = TXTRecordGetItemAtIndex( ( uint16_t ) m_byteArray.size(), &m_byteArray[ 0 ], ( uint16_t ) index, sizeof( keyBuf ), keyBuf, &rawValueLen, &rawValue );
+
+	require_action( !err, exit, hr = S_FALSE );
+
+
+
+	ok = UTF8ToBSTR( keyBuf, temp );
+
+	require_action( ok, exit, hr = S_FALSE );
+
+
+
+	*retval = temp;
+
+
+
+exit:
+
+
+
+	return hr;
+
+}
+
+
+
+STDMETHODIMP CTXTRecord::GetValueAtIndex(ULONG index, VARIANT* retval)
+
+{
+
+	char				keyBuf[ 64 ];
+
+	uint8_t				rawValueLen;
+
+	const void		*	rawValue;
+
+	CComBSTR			temp;
+
+	DNSServiceErrorType	err;
+
+	BOOL				ok;
+
+	HRESULT				hr = S_OK;
+
+
+
+	err = TXTRecordGetItemAtIndex( ( uint16_t ) m_byteArray.size(), &m_byteArray[ 0 ], ( uint16_t ) index, sizeof( keyBuf ), keyBuf, &rawValueLen, &rawValue );
+
+	require_action( !err, exit, hr = S_FALSE );
+
+
+
+	ok = ByteArrayToVariant( rawValue, rawValueLen, retval );
+
+	require_action( ok, exit, hr = S_FALSE );
+
+
+
+exit:
+
+
+
+	return hr;
+
+}
+
+
+
+
+
+void
+
+CTXTRecord::SetBytes
+
+	(
+
+	const unsigned char	*	bytes,
+
+	uint16_t				len
+
+	)
+
+{
+
+	check ( bytes != NULL );
+
+	check( len );
+
+
+
+	m_byteArray.reserve( len );
+
+	m_byteArray.assign( bytes, bytes + len );
+
+}
+
diff --git a/mdnsresponder/mDNSWindows/DLLX/TXTRecord.h b/mdnsresponder/mDNSWindows/DLLX/TXTRecord.h
new file mode 100755
index 0000000..67f3bdc
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DLLX/TXTRecord.h
@@ -0,0 +1,203 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2009 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+
+
+#pragma once
+
+#include "resource.h"       // main symbols
+
+#include "DLLX.h"
+
+#include <vector>
+
+#include <dns_sd.h>
+
+
+
+
+
+#if defined(_WIN32_WCE) && !defined(_CE_DCOM) && !defined(_CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA)
+
+#error "Single-threaded COM objects are not properly supported on Windows CE platform, such as the Windows Mobile platforms that do not include full DCOM support. Define _CE_ALLOW_SINGLE_THREADED_OBJECTS_IN_MTA to force ATL to support creating single-thread COM object's and allow use of it's single-threaded COM object implementations. The threading model in your rgs file was set to 'Free' as that is the only threading model supported in non DCOM Windows CE platforms."
+
+#endif
+
+
+
+
+
+
+
+// CTXTRecord
+
+
+
+class ATL_NO_VTABLE CTXTRecord :
+
+	public CComObjectRootEx<CComSingleThreadModel>,
+
+	public CComCoClass<CTXTRecord, &CLSID_TXTRecord>,
+
+	public IDispatchImpl<ITXTRecord, &IID_ITXTRecord, &LIBID_Bonjour, /*wMajor =*/ 1, /*wMinor =*/ 0>
+
+{
+
+public:
+
+	CTXTRecord()
+
+	:
+
+		m_allocated( FALSE )
+
+	{
+
+	}
+
+
+
+DECLARE_REGISTRY_RESOURCEID(IDR_TXTRECORD)
+
+
+
+
+
+BEGIN_COM_MAP(CTXTRecord)
+
+	COM_INTERFACE_ENTRY(ITXTRecord)
+
+	COM_INTERFACE_ENTRY(IDispatch)
+
+END_COM_MAP()
+
+
+
+
+
+
+
+	DECLARE_PROTECT_FINAL_CONSTRUCT()
+
+
+
+	HRESULT FinalConstruct()
+
+	{
+
+		return S_OK;
+
+	}
+
+
+
+	void FinalRelease()
+
+	{
+
+		if ( m_allocated )
+
+		{
+
+			TXTRecordDeallocate( &m_tref );
+
+		}
+
+	}
+
+
+
+public:
+
+
+
+	STDMETHOD(SetValue)(BSTR key, VARIANT value);
+
+	STDMETHOD(RemoveValue)(BSTR key);
+
+	STDMETHOD(ContainsKey)(BSTR key, VARIANT_BOOL* retval);
+
+	STDMETHOD(GetValueForKey)(BSTR key, VARIANT* value);
+
+	STDMETHOD(GetCount)(ULONG* count);
+
+	STDMETHOD(GetKeyAtIndex)(ULONG index, BSTR* retval);
+
+	STDMETHOD(GetValueAtIndex)(ULONG index, VARIANT* retval);
+
+
+
+private:
+
+
+
+	typedef std::vector< BYTE > ByteArray;
+
+	ByteArray		m_byteArray;
+
+	BOOL			m_allocated;
+
+	TXTRecordRef	m_tref;
+
+
+
+public:
+
+
+
+	uint16_t
+
+	GetLen()
+
+	{
+
+		return TXTRecordGetLength( &m_tref );
+
+	}
+
+
+
+	const void*
+
+	GetBytes()
+
+	{
+
+		return TXTRecordGetBytesPtr( &m_tref );
+
+	}
+
+
+
+	void
+
+	SetBytes
+
+		(
+
+		const unsigned char	*	bytes,
+
+		uint16_t				len
+
+		);
+
+};
+
+
+
+OBJECT_ENTRY_AUTO(__uuidof(TXTRecord), CTXTRecord)
+
diff --git a/mdnsresponder/mDNSWindows/DLLX/TXTRecord.rgs b/mdnsresponder/mDNSWindows/DLLX/TXTRecord.rgs
new file mode 100755
index 0000000..ce2fe1e
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DLLX/TXTRecord.rgs
@@ -0,0 +1,27 @@
+HKCR

+{

+	Bonjour.TXTRecord.1 = s 'TXTRecord Class'

+	{

+		CLSID = s '{AFEE063C-05BA-4248-A26E-168477F49734}'

+	}

+	Bonjour.TXTRecord = s 'TXTRecord Class'

+	{

+		CLSID = s '{AFEE063C-05BA-4248-A26E-168477F49734}'

+		CurVer = s 'Bonjour.TXTRecord.1'

+	}

+	NoRemove CLSID

+	{

+		ForceRemove {AFEE063C-05BA-4248-A26E-168477F49734} = s 'TXTRecord Class'

+		{

+			ProgID = s 'Bonjour.TXTRecord.1'

+			VersionIndependentProgID = s 'Bonjour.TXTRecord'

+			ForceRemove 'Programmable'

+			InprocServer32 = s '%MODULE%'

+			{

+				val ThreadingModel = s 'Apartment'

+			}

+			val AppID = s '%APPID%'

+			'TypeLib' = s '{18FBED6D-F2B7-4EC8-A4A4-46282E635308}'

+		}

+	}

+}

diff --git a/mdnsresponder/mDNSWindows/DLLX/_IDNSSDEvents_CP.h b/mdnsresponder/mDNSWindows/DLLX/_IDNSSDEvents_CP.h
new file mode 100755
index 0000000..5c48397
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DLLX/_IDNSSDEvents_CP.h
@@ -0,0 +1,358 @@
+

+// Wizard-generated connection point proxy class

+// WARNING: This file may be regenerated by the wizard

+

+

+#pragma once

+

+template<class T>

+class CProxy_IDNSSDEvents :

+	public IConnectionPointImpl<T, &__uuidof(_IDNSSDEvents)>

+{

+public:

+	HRESULT Fire_DomainFound( IDNSSDService * service,  DNSSDFlags flags,  ULONG ifIndex,  BSTR domain)

+	{

+		HRESULT hr = S_OK;

+		T * pThis = static_cast<T *>(this);

+		int cConnections = m_vec.GetSize();

+

+		for (int iConnection = 0; iConnection < cConnections; iConnection++)

+		{

+			pThis->Lock();

+			CComPtr<IUnknown> punkConnection = m_vec.GetAt(iConnection);

+			pThis->Unlock();

+

+			IDispatch * pConnection = static_cast<IDispatch *>(punkConnection.p);

+

+			if (pConnection)

+			{

+				CComVariant avarParams[4];

+				avarParams[3] = service;

+				avarParams[2] = flags;

+				avarParams[1] = ifIndex;

+				avarParams[1].vt = VT_UI4;

+				avarParams[0] = domain;

+				avarParams[0].vt = VT_BSTR;

+				DISPPARAMS params = { avarParams, NULL, 4, 0 };

+				hr = pConnection->Invoke(1, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &params, NULL, NULL, NULL);

+			}

+		}

+		return hr;

+	}

+	HRESULT Fire_DomainLost( IDNSSDService * service,  DNSSDFlags flags,  ULONG ifIndex,  BSTR domain)

+	{

+		HRESULT hr = S_OK;

+		T * pThis = static_cast<T *>(this);

+		int cConnections = m_vec.GetSize();

+

+		for (int iConnection = 0; iConnection < cConnections; iConnection++)

+		{

+			pThis->Lock();

+			CComPtr<IUnknown> punkConnection = m_vec.GetAt(iConnection);

+			pThis->Unlock();

+

+			IDispatch * pConnection = static_cast<IDispatch *>(punkConnection.p);

+

+			if (pConnection)

+			{

+				CComVariant avarParams[4];

+				avarParams[3] = service;

+				avarParams[2] = flags;

+				avarParams[1] = ifIndex;

+				avarParams[1].vt = VT_UI4;

+				avarParams[0] = domain;

+				avarParams[0].vt = VT_BSTR;

+				DISPPARAMS params = { avarParams, NULL, 4, 0 };

+				hr = pConnection->Invoke(2, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &params, NULL, NULL, NULL);

+			}

+		}

+		return hr;

+	}

+	HRESULT Fire_ServiceFound( IDNSSDService * browser,  DNSSDFlags flags,  ULONG ifIndex,  BSTR serviceName,  BSTR regType,  BSTR domain)

+	{

+		HRESULT hr = S_OK;

+		T * pThis = static_cast<T *>(this);

+		int cConnections = m_vec.GetSize();

+

+		for (int iConnection = 0; iConnection < cConnections; iConnection++)

+		{

+			pThis->Lock();

+			CComPtr<IUnknown> punkConnection = m_vec.GetAt(iConnection);

+			pThis->Unlock();

+

+			IDispatch * pConnection = static_cast<IDispatch *>(punkConnection.p);

+

+			if (pConnection)

+			{

+				CComVariant avarParams[6];

+				avarParams[5] = browser;

+				avarParams[4] = flags;

+				avarParams[3] = ifIndex;

+				avarParams[3].vt = VT_UI4;

+				avarParams[2] = serviceName;

+				avarParams[2].vt = VT_BSTR;

+				avarParams[1] = regType;

+				avarParams[1].vt = VT_BSTR;

+				avarParams[0] = domain;

+				avarParams[0].vt = VT_BSTR;

+				DISPPARAMS params = { avarParams, NULL, 6, 0 };

+				hr = pConnection->Invoke(3, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &params, NULL, NULL, NULL);

+			}

+		}

+		return hr;

+	}

+	HRESULT Fire_ServiceLost( IDNSSDService * browser,  DNSSDFlags flags,  ULONG ifIndex,  BSTR serviceName,  BSTR regType,  BSTR domain)

+	{

+		HRESULT hr = S_OK;

+		T * pThis = static_cast<T *>(this);

+		int cConnections = m_vec.GetSize();

+

+		for (int iConnection = 0; iConnection < cConnections; iConnection++)

+		{

+			pThis->Lock();

+			CComPtr<IUnknown> punkConnection = m_vec.GetAt(iConnection);

+			pThis->Unlock();

+

+			IDispatch * pConnection = static_cast<IDispatch *>(punkConnection.p);

+

+			if (pConnection)

+			{

+				CComVariant avarParams[6];

+				avarParams[5] = browser;

+				avarParams[4] = flags;

+				avarParams[3] = ifIndex;

+				avarParams[3].vt = VT_UI4;

+				avarParams[2] = serviceName;

+				avarParams[2].vt = VT_BSTR;

+				avarParams[1] = regType;

+				avarParams[1].vt = VT_BSTR;

+				avarParams[0] = domain;

+				avarParams[0].vt = VT_BSTR;

+				DISPPARAMS params = { avarParams, NULL, 6, 0 };

+				hr = pConnection->Invoke(4, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &params, NULL, NULL, NULL);

+			}

+		}

+		return hr;

+	}

+	HRESULT Fire_ServiceResolved( IDNSSDService * service,  DNSSDFlags flags,  ULONG ifIndex,  BSTR fullName,  BSTR hostName,  USHORT port,  ITXTRecord * record)

+	{

+		HRESULT hr = S_OK;

+		T * pThis = static_cast<T *>(this);

+		int cConnections = m_vec.GetSize();

+

+		for (int iConnection = 0; iConnection < cConnections; iConnection++)

+		{

+			pThis->Lock();

+			CComPtr<IUnknown> punkConnection = m_vec.GetAt(iConnection);

+			pThis->Unlock();

+

+			IDispatch * pConnection = static_cast<IDispatch *>(punkConnection.p);

+

+			if (pConnection)

+			{

+				CComVariant avarParams[7];

+				avarParams[6] = service;

+				avarParams[5] = flags;

+				avarParams[4] = ifIndex;

+				avarParams[4].vt = VT_UI4;

+				avarParams[3] = fullName;

+				avarParams[3].vt = VT_BSTR;

+				avarParams[2] = hostName;

+				avarParams[2].vt = VT_BSTR;

+				avarParams[1] = port;

+				avarParams[1].vt = VT_UI2;

+				avarParams[0] = record;

+				DISPPARAMS params = { avarParams, NULL, 7, 0 };

+				hr = pConnection->Invoke(5, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &params, NULL, NULL, NULL);

+			}

+		}

+		return hr;

+	}

+	HRESULT Fire_ServiceRegistered( IDNSSDService * service,  DNSSDFlags flags,  BSTR name,  BSTR regType,  BSTR domain)

+	{

+		HRESULT hr = S_OK;

+		T * pThis = static_cast<T *>(this);

+		int cConnections = m_vec.GetSize();

+

+		for (int iConnection = 0; iConnection < cConnections; iConnection++)

+		{

+			pThis->Lock();

+			CComPtr<IUnknown> punkConnection = m_vec.GetAt(iConnection);

+			pThis->Unlock();

+

+			IDispatch * pConnection = static_cast<IDispatch *>(punkConnection.p);

+

+			if (pConnection)

+			{

+				CComVariant avarParams[5];

+				avarParams[4] = service;

+				avarParams[3] = flags;

+				avarParams[2] = name;

+				avarParams[2].vt = VT_BSTR;

+				avarParams[1] = regType;

+				avarParams[1].vt = VT_BSTR;

+				avarParams[0] = domain;

+				avarParams[0].vt = VT_BSTR;

+				DISPPARAMS params = { avarParams, NULL, 5, 0 };

+				hr = pConnection->Invoke(6, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &params, NULL, NULL, NULL);

+			}

+		}

+		return hr;

+	}

+	HRESULT Fire_QueryRecordAnswered( IDNSSDService * service,  DNSSDFlags flags,  ULONG ifIndex,  BSTR fullName,  DNSSDRRType rrtype,  DNSSDRRClass rrclass,  VARIANT rdata,  ULONG ttl)

+	{

+		HRESULT hr = S_OK;

+		T * pThis = static_cast<T *>(this);

+		int cConnections = m_vec.GetSize();

+

+		for (int iConnection = 0; iConnection < cConnections; iConnection++)

+		{

+			pThis->Lock();

+			CComPtr<IUnknown> punkConnection = m_vec.GetAt(iConnection);

+			pThis->Unlock();

+

+			IDispatch * pConnection = static_cast<IDispatch *>(punkConnection.p);

+

+			if (pConnection)

+			{

+				CComVariant avarParams[8];

+				avarParams[7] = service;

+				avarParams[6] = flags;

+				avarParams[5] = ifIndex;

+				avarParams[5].vt = VT_UI4;

+				avarParams[4] = fullName;

+				avarParams[4].vt = VT_BSTR;

+				avarParams[3] = rrtype;

+				avarParams[2] = rrclass;

+				avarParams[1] = rdata;

+				avarParams[0] = ttl;

+				avarParams[0].vt = VT_UI4;

+				DISPPARAMS params = { avarParams, NULL, 8, 0 };

+				hr = pConnection->Invoke(7, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &params, NULL, NULL, NULL);

+			}

+		}

+		return hr;

+	}

+	HRESULT Fire_RecordRegistered( IDNSSDRecord * record,  DNSSDFlags flags)

+	{

+		HRESULT hr = S_OK;

+		T * pThis = static_cast<T *>(this);

+		int cConnections = m_vec.GetSize();

+

+		for (int iConnection = 0; iConnection < cConnections; iConnection++)

+		{

+			pThis->Lock();

+			CComPtr<IUnknown> punkConnection = m_vec.GetAt(iConnection);

+			pThis->Unlock();

+

+			IDispatch * pConnection = static_cast<IDispatch *>(punkConnection.p);

+

+			if (pConnection)

+			{

+				CComVariant avarParams[2];

+				avarParams[1] = record;

+				avarParams[0] = flags;

+				DISPPARAMS params = { avarParams, NULL, 2, 0 };

+				hr = pConnection->Invoke(8, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &params, NULL, NULL, NULL);

+			}

+		}

+		return hr;

+	}

+	HRESULT Fire_AddressFound( IDNSSDService * service,  DNSSDFlags flags,  ULONG ifIndex,  BSTR hostname,  DNSSDAddressFamily addressFamily,  BSTR address,  ULONG ttl)

+	{

+		HRESULT hr = S_OK;

+		T * pThis = static_cast<T *>(this);

+		int cConnections = m_vec.GetSize();

+

+		for (int iConnection = 0; iConnection < cConnections; iConnection++)

+		{

+			pThis->Lock();

+			CComPtr<IUnknown> punkConnection = m_vec.GetAt(iConnection);

+			pThis->Unlock();

+

+			IDispatch * pConnection = static_cast<IDispatch *>(punkConnection.p);

+

+			if (pConnection)

+			{

+				CComVariant avarParams[7];

+				avarParams[6] = service;

+				avarParams[5] = flags;

+				avarParams[4] = ifIndex;

+				avarParams[4].vt = VT_UI4;

+				avarParams[3] = hostname;

+				avarParams[3].vt = VT_BSTR;

+				avarParams[2] = addressFamily;

+				avarParams[1] = address;

+				avarParams[1].vt = VT_BSTR;

+				avarParams[0] = ttl;

+				avarParams[0].vt = VT_UI4;

+				DISPPARAMS params = { avarParams, NULL, 7, 0 };

+				hr = pConnection->Invoke(9, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &params, NULL, NULL, NULL);

+			}

+		}

+		return hr;

+	}

+	HRESULT Fire_MappingCreated( IDNSSDService * service,  DNSSDFlags flags,  ULONG ifIndex,  ULONG externalAddress,  DNSSDAddressFamily addressFamily,  DNSSDProtocol protocol,  USHORT internalPort,  USHORT externalPort,  ULONG ttl)

+	{

+		HRESULT hr = S_OK;

+		T * pThis = static_cast<T *>(this);

+		int cConnections = m_vec.GetSize();

+

+		for (int iConnection = 0; iConnection < cConnections; iConnection++)

+		{

+			pThis->Lock();

+			CComPtr<IUnknown> punkConnection = m_vec.GetAt(iConnection);

+			pThis->Unlock();

+

+			IDispatch * pConnection = static_cast<IDispatch *>(punkConnection.p);

+

+			if (pConnection)

+			{

+				CComVariant avarParams[9];

+				avarParams[8] = service;

+				avarParams[7] = flags;

+				avarParams[6] = ifIndex;

+				avarParams[6].vt = VT_UI4;

+				avarParams[5] = externalAddress;

+				avarParams[5].vt = VT_UI4;

+				avarParams[4] = addressFamily;

+				avarParams[3] = protocol;

+				avarParams[2] = internalPort;

+				avarParams[2].vt = VT_UI2;

+				avarParams[1] = externalPort;

+				avarParams[1].vt = VT_UI2;

+				avarParams[0] = ttl;

+				avarParams[0].vt = VT_UI4;

+				DISPPARAMS params = { avarParams, NULL, 9, 0 };

+				hr = pConnection->Invoke(10, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &params, NULL, NULL, NULL);

+			}

+		}

+		return hr;

+	}

+	HRESULT Fire_OperationFailed( IDNSSDService * service,  DNSSDError error)

+	{

+		HRESULT hr = S_OK;

+		T * pThis = static_cast<T *>(this);

+		int cConnections = m_vec.GetSize();

+

+		for (int iConnection = 0; iConnection < cConnections; iConnection++)

+		{

+			pThis->Lock();

+			CComPtr<IUnknown> punkConnection = m_vec.GetAt(iConnection);

+			pThis->Unlock();

+

+			IDispatch * pConnection = static_cast<IDispatch *>(punkConnection.p);

+

+			if (pConnection)

+			{

+				CComVariant avarParams[2];

+				avarParams[1] = service;

+				avarParams[0] = error;

+				DISPPARAMS params = { avarParams, NULL, 2, 0 };

+				hr = pConnection->Invoke(11, IID_NULL, LOCALE_USER_DEFAULT, DISPATCH_METHOD, &params, NULL, NULL, NULL);

+			}

+		}

+		return hr;

+	}

+};

+

diff --git a/mdnsresponder/mDNSWindows/DLLX/dlldatax.c b/mdnsresponder/mDNSWindows/DLLX/dlldatax.c
new file mode 100755
index 0000000..a581532
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DLLX/dlldatax.c
@@ -0,0 +1,51 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+
+
+#ifdef _MERGE_PROXYSTUB // merge proxy stub DLL
+
+
+
+#define REGISTER_PROXY_DLL //DllRegisterServer, etc.
+
+
+
+#define _WIN32_WINNT 0x0500	//for WinNT 4.0 or Win95 with DCOM
+
+#define USE_STUBLESS_PROXY	//defined only with MIDL switch /Oicf
+
+
+
+#pragma comment(lib, "rpcns4.lib")
+
+#pragma comment(lib, "rpcrt4.lib")
+
+
+
+#define ENTRY_PREFIX	Prx
+
+
+
+#include "dlldata.c"
+
+#include "DLLX_p.c"
+
+
+
+#endif //_MERGE_PROXYSTUB
+
diff --git a/mdnsresponder/mDNSWindows/DLLX/dlldatax.h b/mdnsresponder/mDNSWindows/DLLX/dlldatax.h
new file mode 100755
index 0000000..f8816a3
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DLLX/dlldatax.h
@@ -0,0 +1,49 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+
+
+#pragma once
+
+
+
+#ifdef _MERGE_PROXYSTUB
+
+
+
+extern "C" 
+
+{
+
+BOOL WINAPI PrxDllMain(HINSTANCE hInstance, DWORD dwReason, 
+
+	LPVOID lpReserved);
+
+STDAPI PrxDllCanUnloadNow(void);
+
+STDAPI PrxDllGetClassObject(REFCLSID rclsid, REFIID riid, LPVOID* ppv);
+
+STDAPI PrxDllRegisterServer(void);
+
+STDAPI PrxDllUnregisterServer(void);
+
+}
+
+
+
+#endif
+
diff --git a/mdnsresponder/mDNSWindows/DLLX/resource.h b/mdnsresponder/mDNSWindows/DLLX/resource.h
new file mode 100755
index 0000000..5613187
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DLLX/resource.h
@@ -0,0 +1,30 @@
+//{{NO_DEPENDENCIES}}

+// Microsoft Visual C++ generated include file.

+// Used by DLLX.rc

+//

+#define IDS_PROJNAME                    100

+#define IDR_DLLX		                101

+#define IDR_DNSSD                       102

+#define IDR_DNSSDSERVICE                103

+#define IDR_BROWSELISTENER              104

+#define IDR_RESOLVELISTENER             105

+#define IDR_TXTRECORD                   106

+#define IDR_ENUMERATEDOMAINSLISTENER    107

+#define IDR_REGISTERLISTENER            108

+#define IDR_QUERYRECORDLISTENER         109

+#define IDR_GETADDRINFOLISTENER         110

+#define IDR_DNSSDRECORD                 111

+#define IDR_REGISTERRECORDLISTENER      112

+#define IDR_NATPORTMAPPINGLISTENER      113

+#define IDR_DNSSDEVENTMANAGER           114

+

+// Next default values for new objects

+// 

+#ifdef APSTUDIO_INVOKED

+#ifndef APSTUDIO_READONLY_SYMBOLS

+#define _APS_NEXT_RESOURCE_VALUE        201

+#define _APS_NEXT_COMMAND_VALUE         32768

+#define _APS_NEXT_CONTROL_VALUE         201

+#define _APS_NEXT_SYMED_VALUE           115

+#endif

+#endif

diff --git a/mdnsresponder/mDNSWindows/DLLX/stdafx.h b/mdnsresponder/mDNSWindows/DLLX/stdafx.h
new file mode 100755
index 0000000..66fb37b
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DLLX/stdafx.h
@@ -0,0 +1,88 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2009 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+
+
+#pragma once
+
+
+
+#ifndef STRICT
+
+#define STRICT
+
+#endif
+
+
+
+// Modify the following defines if you have to target a platform prior to the ones specified below.
+
+// Refer to MSDN for the latest info on corresponding values for different platforms.
+
+#ifndef WINVER				// Allow use of features specific to Windows XP or later.
+
+#define WINVER 0x0501		// Change this to the appropriate value to target other versions of Windows.
+
+#endif
+
+
+
+#ifndef _WIN32_WINNT		// Allow use of features specific to Windows XP or later.                   
+
+#define _WIN32_WINNT 0x0501	// Change this to the appropriate value to target other versions of Windows.
+
+#endif						
+
+
+
+#ifndef _WIN32_WINDOWS		// Allow use of features specific to Windows 98 or later.
+
+#define _WIN32_WINDOWS 0x0410 // Change this to the appropriate value to target Windows Me or later.
+
+#endif
+
+
+
+#ifndef _WIN32_IE			// Allow use of features specific to IE 6.0 or later.
+
+#define _WIN32_IE 0x0600	// Change this to the appropriate value to target other versions of IE.
+
+#endif
+
+
+
+#define _ATL_APARTMENT_THREADED
+
+#define _ATL_NO_AUTOMATIC_NAMESPACE
+
+
+
+#define _ATL_CSTRING_EXPLICIT_CONSTRUCTORS	// some CString constructors will be explicit
+
+
+
+
+
+#include "resource.h"
+
+#include <atlbase.h>
+
+#include <atlcom.h>
+
+
+
+using namespace ATL;
\ No newline at end of file
diff --git a/mdnsresponder/mDNSWindows/DNSServiceBrowser/Windows/ApplicationVS2002.sln b/mdnsresponder/mDNSWindows/DNSServiceBrowser/Windows/ApplicationVS2002.sln
new file mode 100644
index 0000000..5ce1a0a
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DNSServiceBrowser/Windows/ApplicationVS2002.sln
@@ -0,0 +1,21 @@
+Microsoft Visual Studio Solution File, Format Version 7.00
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Application", "ApplicationVS2002.vcproj", "{EDE4B529-4CF5-4A49-9B6F-C10F0EA24278}"
+EndProject
+Global
+	GlobalSection(SolutionConfiguration) = preSolution
+		ConfigName.0 = Debug
+		ConfigName.1 = Release
+	EndGlobalSection
+	GlobalSection(ProjectDependencies) = postSolution
+	EndGlobalSection
+	GlobalSection(ProjectConfiguration) = postSolution
+		{EDE4B529-4CF5-4A49-9B6F-C10F0EA24278}.Debug.ActiveCfg = Debug|Win32
+		{EDE4B529-4CF5-4A49-9B6F-C10F0EA24278}.Debug.Build.0 = Debug|Win32
+		{EDE4B529-4CF5-4A49-9B6F-C10F0EA24278}.Release.ActiveCfg = Release|Win32
+		{EDE4B529-4CF5-4A49-9B6F-C10F0EA24278}.Release.Build.0 = Release|Win32
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+	EndGlobalSection
+	GlobalSection(ExtensibilityAddIns) = postSolution
+	EndGlobalSection
+EndGlobal
diff --git a/mdnsresponder/mDNSWindows/DNSServiceBrowser/Windows/ApplicationVS2002.vcproj b/mdnsresponder/mDNSWindows/DNSServiceBrowser/Windows/ApplicationVS2002.vcproj
new file mode 100644
index 0000000..c1a2424
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DNSServiceBrowser/Windows/ApplicationVS2002.vcproj
@@ -0,0 +1,253 @@
+<?xml version="1.0" encoding = "Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="7.00"
+	Name="Application"
+	ProjectGUID="{EDE4B529-4CF5-4A49-9B6F-C10F0EA24278}"
+	Keyword="MFCProj">
+	<Platforms>
+		<Platform
+			Name="Win32"/>
+	</Platforms>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory="Debug"
+			IntermediateDirectory="Debug"
+			ConfigurationType="1"
+			UseOfMFC="1"
+			CharacterSet="1">
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories=".\Resources;..\;..\..\..\;..\..\..\..\mDNSCore;..\..\..\DNSServices"
+				PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;DEBUG=1"
+				StringPooling="TRUE"
+				MinimalRebuild="FALSE"
+				BasicRuntimeChecks="3"
+				SmallerTypeCheck="FALSE"
+				RuntimeLibrary="1"
+				BufferSecurityCheck="TRUE"
+				EnableFunctionLevelLinking="FALSE"
+				ForceConformanceInForLoopScope="TRUE"
+				RuntimeTypeInfo="TRUE"
+				UsePrecompiledHeader="2"
+				PrecompiledHeaderThrough="StdAfx.h"
+				PrecompiledHeaderFile=".\Debug/Application.pch"
+				AssemblerListingLocation=".\Debug/"
+				ObjectFile=".\Debug/"
+				ProgramDataBaseFileName=".\Debug/"
+				BrowseInformation="1"
+				WarningLevel="4"
+				WarnAsError="TRUE"
+				SuppressStartupBanner="TRUE"
+				Detect64BitPortabilityProblems="TRUE"
+				DebugInformationFormat="3"
+				CompileAs="2"/>
+			<Tool
+				Name="VCCustomBuildTool"/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalOptions="/MACHINE:I386 /IGNORE:4089"
+				AdditionalDependencies="ws2_32.lib"
+				OutputFile="DNSServiceBrowser Debug.exe"
+				LinkIncremental="1"
+				SuppressStartupBanner="TRUE"
+				IgnoreDefaultLibraryNames="wsock32.lib"
+				GenerateDebugInformation="TRUE"
+				ProgramDatabaseFile=".\Debug/Application.pdb"
+				SubSystem="2"/>
+			<Tool
+				Name="VCMIDLTool"
+				PreprocessorDefinitions="_DEBUG"
+				MkTypLibCompatible="FALSE"
+				SuppressStartupBanner="TRUE"
+				TargetEnvironment="1"
+				TypeLibraryName=".\Debug/Application.tlb"/>
+			<Tool
+				Name="VCPostBuildEventTool"/>
+			<Tool
+				Name="VCPreBuildEventTool"/>
+			<Tool
+				Name="VCPreLinkEventTool"/>
+			<Tool
+				Name="VCResourceCompilerTool"
+				PreprocessorDefinitions="_AFXDLL;_DEBUG"
+				Culture="1033"/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"/>
+			<Tool
+				Name="VCWebDeploymentTool"/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory=".\Release"
+			IntermediateDirectory=".\Release"
+			ConfigurationType="1"
+			UseOfMFC="1"
+			CharacterSet="1"
+			WholeProgramOptimization="FALSE">
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="2"
+				GlobalOptimizations="TRUE"
+				InlineFunctionExpansion="0"
+				FavorSizeOrSpeed="2"
+				OmitFramePointers="TRUE"
+				OptimizeForWindowsApplication="FALSE"
+				AdditionalIncludeDirectories=".\Resources;..\;..\..\..\;..\..\..\..\mDNSCore;..\..\..\DNSServices"
+				PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS"
+				StringPooling="TRUE"
+				MinimalRebuild="FALSE"
+				RuntimeLibrary="0"
+				BufferSecurityCheck="FALSE"
+				EnableFunctionLevelLinking="FALSE"
+				ForceConformanceInForLoopScope="TRUE"
+				RuntimeTypeInfo="TRUE"
+				UsePrecompiledHeader="2"
+				PrecompiledHeaderThrough="stdafx.h"
+				PrecompiledHeaderFile=".\Release/Application.pch"
+				AssemblerListingLocation=".\Release/"
+				ObjectFile=".\Release/"
+				ProgramDataBaseFileName=".\Release/"
+				BrowseInformation="1"
+				WarningLevel="4"
+				WarnAsError="TRUE"
+				SuppressStartupBanner="TRUE"
+				Detect64BitPortabilityProblems="TRUE"
+				CompileAs="2"/>
+			<Tool
+				Name="VCCustomBuildTool"/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalOptions="/MACHINE:I386 /IGNORE:4089"
+				AdditionalDependencies="ws2_32.lib"
+				OutputFile="DNSServiceBrowser.exe"
+				LinkIncremental="1"
+				SuppressStartupBanner="TRUE"
+				IgnoreDefaultLibraryNames="wsock32.lib"
+				ProgramDatabaseFile=".\Release/Application.pdb"
+				SubSystem="2"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"/>
+			<Tool
+				Name="VCMIDLTool"
+				PreprocessorDefinitions="NDEBUG"
+				MkTypLibCompatible="TRUE"
+				SuppressStartupBanner="TRUE"
+				TargetEnvironment="1"
+				TypeLibraryName=".\Release/Application.tlb"/>
+			<Tool
+				Name="VCPostBuildEventTool"/>
+			<Tool
+				Name="VCPreBuildEventTool"/>
+			<Tool
+				Name="VCPreLinkEventTool"/>
+			<Tool
+				Name="VCResourceCompilerTool"
+				PreprocessorDefinitions="_AFXDLL;NDEBUG"
+				Culture="1033"/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"/>
+			<Tool
+				Name="VCWebDeploymentTool"/>
+		</Configuration>
+	</Configurations>
+	<Files>
+		<Filter
+			Name="Source Files"
+			Filter="">
+			<File
+				RelativePath="Sources\AboutDialog.cpp">
+			</File>
+			<File
+				RelativePath="Sources\AboutDialog.h">
+			</File>
+			<File
+				RelativePath="Sources\Application.cpp">
+			</File>
+			<File
+				RelativePath="Sources\Application.h">
+			</File>
+			<File
+				RelativePath="Sources\ChooserDialog.cpp">
+			</File>
+			<File
+				RelativePath="Sources\ChooserDialog.h">
+			</File>
+			<File
+				RelativePath="Sources\LoginDialog.cpp">
+			</File>
+			<File
+				RelativePath="Sources\LoginDialog.h">
+			</File>
+			<File
+				RelativePath="Sources\StdAfx.cpp">
+			</File>
+			<File
+				RelativePath="Sources\StdAfx.h">
+			</File>
+		</Filter>
+		<Filter
+			Name="Resource Files"
+			Filter="ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;rc">
+			<File
+				RelativePath="Resources\Application.ico">
+			</File>
+			<File
+				RelativePath="Resources\Application.rc">
+			</File>
+			<File
+				RelativePath="Resources\Application.rc2">
+			</File>
+			<File
+				RelativePath=".\Resources\Resource.h">
+			</File>
+		</Filter>
+		<Filter
+			Name="Support"
+			Filter="">
+			<File
+				RelativePath="..\..\..\CommonServices.h">
+			</File>
+			<File
+				RelativePath="..\..\..\..\mDNSCore\DNSCommon.c">
+			</File>
+			<File
+				RelativePath="..\..\..\..\mDNSCore\DNSCommon.h">
+			</File>
+			<File
+				RelativePath="..\..\..\..\mDNSCore\DNSDigest.c">
+			</File>
+			<File
+				RelativePath="..\..\..\DNSServices\DNSServices.c">
+			</File>
+			<File
+				RelativePath="..\..\..\DebugServices.c">
+			</File>
+			<File
+				RelativePath="..\..\..\DebugServices.h">
+			</File>
+			<File
+				RelativePath="..\..\..\..\mDNSCore\mDNS.c">
+			</File>
+			<File
+				RelativePath="..\..\..\..\mDNSCore\mDNSClientAPI.h">
+			</File>
+			<File
+				RelativePath="..\..\..\..\mDNSCore\mDNSDebug.h">
+			</File>
+			<File
+				RelativePath="..\..\..\mDNSWin32.c">
+			</File>
+			<File
+				RelativePath="..\..\..\..\mDNSCore\uDNS.c">
+			</File>
+			<File
+				RelativePath="..\..\..\..\mDNSCore\uDNS.h">
+			</File>
+		</Filter>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>
diff --git a/mdnsresponder/mDNSWindows/DNSServiceBrowser/Windows/ApplicationVS2003.sln b/mdnsresponder/mDNSWindows/DNSServiceBrowser/Windows/ApplicationVS2003.sln
new file mode 100644
index 0000000..bdaaa3b
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DNSServiceBrowser/Windows/ApplicationVS2003.sln
@@ -0,0 +1,21 @@
+Microsoft Visual Studio Solution File, Format Version 8.00
+Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Application", "ApplicationVS2003.vcproj", "{EDE4B529-4CF5-4A49-9B6F-C10F0EA24278}"
+	ProjectSection(ProjectDependencies) = postProject
+	EndProjectSection
+EndProject
+Global
+	GlobalSection(SolutionConfiguration) = preSolution
+		Debug = Debug
+		Release = Release
+	EndGlobalSection
+	GlobalSection(ProjectConfiguration) = postSolution
+		{EDE4B529-4CF5-4A49-9B6F-C10F0EA24278}.Debug.ActiveCfg = Debug|Win32
+		{EDE4B529-4CF5-4A49-9B6F-C10F0EA24278}.Debug.Build.0 = Debug|Win32
+		{EDE4B529-4CF5-4A49-9B6F-C10F0EA24278}.Release.ActiveCfg = Release|Win32
+		{EDE4B529-4CF5-4A49-9B6F-C10F0EA24278}.Release.Build.0 = Release|Win32
+	EndGlobalSection
+	GlobalSection(ExtensibilityGlobals) = postSolution
+	EndGlobalSection
+	GlobalSection(ExtensibilityAddIns) = postSolution
+	EndGlobalSection
+EndGlobal
diff --git a/mdnsresponder/mDNSWindows/DNSServiceBrowser/Windows/ApplicationVS2003.vcproj b/mdnsresponder/mDNSWindows/DNSServiceBrowser/Windows/ApplicationVS2003.vcproj
new file mode 100644
index 0000000..3c8cb1c
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DNSServiceBrowser/Windows/ApplicationVS2003.vcproj
@@ -0,0 +1,267 @@
+<?xml version="1.0" encoding="Windows-1252"?>
+<VisualStudioProject
+	ProjectType="Visual C++"
+	Version="7.10"
+	Name="Application"
+	ProjectGUID="{EDE4B529-4CF5-4A49-9B6F-C10F0EA24278}"
+	Keyword="MFCProj">
+	<Platforms>
+		<Platform
+			Name="Win32"/>
+	</Platforms>
+	<Configurations>
+		<Configuration
+			Name="Debug|Win32"
+			OutputDirectory="Debug"
+			IntermediateDirectory="Debug"
+			ConfigurationType="1"
+			UseOfMFC="1"
+			CharacterSet="1">
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="0"
+				AdditionalIncludeDirectories=".\Resources;..\;..\..\..\;..\..\..\..\mDNSCore;..\..\..\DNSServices"
+				PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;DEBUG=1"
+				StringPooling="TRUE"
+				MinimalRebuild="FALSE"
+				BasicRuntimeChecks="3"
+				SmallerTypeCheck="FALSE"
+				RuntimeLibrary="1"
+				BufferSecurityCheck="TRUE"
+				EnableFunctionLevelLinking="FALSE"
+				ForceConformanceInForLoopScope="TRUE"
+				RuntimeTypeInfo="TRUE"
+				UsePrecompiledHeader="2"
+				PrecompiledHeaderThrough="StdAfx.h"
+				PrecompiledHeaderFile=".\Debug/Application.pch"
+				AssemblerListingLocation=".\Debug/"
+				ObjectFile=".\Debug/"
+				ProgramDataBaseFileName=".\Debug/"
+				BrowseInformation="1"
+				WarningLevel="4"
+				WarnAsError="TRUE"
+				SuppressStartupBanner="TRUE"
+				Detect64BitPortabilityProblems="TRUE"
+				DebugInformationFormat="3"
+				CompileAs="0"/>
+			<Tool
+				Name="VCCustomBuildTool"/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalOptions="/MACHINE:I386 /IGNORE:4089"
+				AdditionalDependencies="ws2_32.lib"
+				OutputFile="DNSServiceBrowser Debug.exe"
+				LinkIncremental="1"
+				SuppressStartupBanner="TRUE"
+				IgnoreDefaultLibraryNames="wsock32.lib"
+				GenerateDebugInformation="TRUE"
+				ProgramDatabaseFile=".\Debug/Application.pdb"
+				SubSystem="2"/>
+			<Tool
+				Name="VCMIDLTool"
+				PreprocessorDefinitions="_DEBUG"
+				MkTypLibCompatible="FALSE"
+				SuppressStartupBanner="TRUE"
+				TargetEnvironment="1"
+				TypeLibraryName=".\Debug/Application.tlb"/>
+			<Tool
+				Name="VCPostBuildEventTool"/>
+			<Tool
+				Name="VCPreBuildEventTool"/>
+			<Tool
+				Name="VCPreLinkEventTool"/>
+			<Tool
+				Name="VCResourceCompilerTool"
+				PreprocessorDefinitions="_AFXDLL;_DEBUG"
+				Culture="1033"/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"/>
+			<Tool
+				Name="VCWebDeploymentTool"/>
+			<Tool
+				Name="VCManagedWrapperGeneratorTool"/>
+			<Tool
+				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+		</Configuration>
+		<Configuration
+			Name="Release|Win32"
+			OutputDirectory=".\Release"
+			IntermediateDirectory=".\Release"
+			ConfigurationType="1"
+			UseOfMFC="1"
+			CharacterSet="1"
+			WholeProgramOptimization="FALSE">
+			<Tool
+				Name="VCCLCompilerTool"
+				Optimization="2"
+				GlobalOptimizations="TRUE"
+				InlineFunctionExpansion="0"
+				FavorSizeOrSpeed="2"
+				OmitFramePointers="TRUE"
+				OptimizeForWindowsApplication="FALSE"
+				AdditionalIncludeDirectories=".\Resources;..\;..\..\..\;..\..\..\..\mDNSCore;..\..\..\DNSServices"
+				PreprocessorDefinitions="WIN32;NDEBUG;_WINDOWS"
+				StringPooling="TRUE"
+				MinimalRebuild="FALSE"
+				RuntimeLibrary="0"
+				BufferSecurityCheck="FALSE"
+				EnableFunctionLevelLinking="FALSE"
+				ForceConformanceInForLoopScope="TRUE"
+				RuntimeTypeInfo="TRUE"
+				UsePrecompiledHeader="2"
+				PrecompiledHeaderThrough="stdafx.h"
+				PrecompiledHeaderFile=".\Release/Application.pch"
+				AssemblerListingLocation=".\Release/"
+				ObjectFile=".\Release/"
+				ProgramDataBaseFileName=".\Release/"
+				BrowseInformation="1"
+				WarningLevel="4"
+				WarnAsError="TRUE"
+				SuppressStartupBanner="TRUE"
+				Detect64BitPortabilityProblems="TRUE"
+				CompileAs="0"/>
+			<Tool
+				Name="VCCustomBuildTool"/>
+			<Tool
+				Name="VCLinkerTool"
+				AdditionalOptions="/MACHINE:I386 /IGNORE:4089"
+				AdditionalDependencies="ws2_32.lib"
+				OutputFile="DNSServiceBrowser.exe"
+				LinkIncremental="1"
+				SuppressStartupBanner="TRUE"
+				IgnoreDefaultLibraryNames="wsock32.lib"
+				ProgramDatabaseFile=".\Release/Application.pdb"
+				SubSystem="2"
+				OptimizeReferences="2"
+				EnableCOMDATFolding="2"/>
+			<Tool
+				Name="VCMIDLTool"
+				PreprocessorDefinitions="NDEBUG"
+				MkTypLibCompatible="TRUE"
+				SuppressStartupBanner="TRUE"
+				TargetEnvironment="1"
+				TypeLibraryName=".\Release/Application.tlb"/>
+			<Tool
+				Name="VCPostBuildEventTool"/>
+			<Tool
+				Name="VCPreBuildEventTool"/>
+			<Tool
+				Name="VCPreLinkEventTool"/>
+			<Tool
+				Name="VCResourceCompilerTool"
+				PreprocessorDefinitions="_AFXDLL;NDEBUG"
+				Culture="1033"/>
+			<Tool
+				Name="VCWebServiceProxyGeneratorTool"/>
+			<Tool
+				Name="VCXMLDataGeneratorTool"/>
+			<Tool
+				Name="VCWebDeploymentTool"/>
+			<Tool
+				Name="VCManagedWrapperGeneratorTool"/>
+			<Tool
+				Name="VCAuxiliaryManagedWrapperGeneratorTool"/>
+		</Configuration>
+	</Configurations>
+	<References>
+	</References>
+	<Files>
+		<Filter
+			Name="Source Files"
+			Filter="">
+			<File
+				RelativePath="Sources\AboutDialog.cpp">
+			</File>
+			<File
+				RelativePath="Sources\AboutDialog.h">
+			</File>
+			<File
+				RelativePath="Sources\Application.cpp">
+			</File>
+			<File
+				RelativePath="Sources\Application.h">
+			</File>
+			<File
+				RelativePath="Sources\ChooserDialog.cpp">
+			</File>
+			<File
+				RelativePath="Sources\ChooserDialog.h">
+			</File>
+			<File
+				RelativePath="Sources\LoginDialog.cpp">
+			</File>
+			<File
+				RelativePath="Sources\LoginDialog.h">
+			</File>
+			<File
+				RelativePath="Sources\StdAfx.cpp">
+			</File>
+			<File
+				RelativePath="Sources\StdAfx.h">
+			</File>
+		</Filter>
+		<Filter
+			Name="Resource Files"
+			Filter="ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;rc">
+			<File
+				RelativePath="Resources\Application.ico">
+			</File>
+			<File
+				RelativePath="Resources\Application.rc">
+			</File>
+			<File
+				RelativePath="Resources\Application.rc2">
+			</File>
+			<File
+				RelativePath=".\Resources\Resource.h">
+			</File>
+		</Filter>
+		<Filter
+			Name="Support"
+			Filter="">
+			<File
+				RelativePath="..\..\..\CommonServices.h">
+			</File>
+			<File
+				RelativePath="..\..\..\..\mDNSCore\DNSCommon.c">
+			</File>
+			<File
+				RelativePath="..\..\..\..\mDNSCore\DNSCommon.h">
+			</File>
+			<File
+				RelativePath="..\..\..\..\mDNSCore\DNSDigest.c">
+			</File>
+			<File
+				RelativePath="..\..\..\DNSServices\DNSServices.c">
+			</File>
+			<File
+				RelativePath="..\..\..\DebugServices.c">
+			</File>
+			<File
+				RelativePath="..\..\..\DebugServices.h">
+			</File>
+			<File
+				RelativePath="..\..\..\..\mDNSCore\mDNS.c">
+			</File>
+			<File
+				RelativePath="..\..\..\..\mDNSCore\mDNSClientAPI.h">
+			</File>
+			<File
+				RelativePath="..\..\..\..\mDNSCore\mDNSDebug.h">
+			</File>
+			<File
+				RelativePath="..\..\..\mDNSWin32.c">
+			</File>
+			<File
+				RelativePath="..\..\..\..\mDNSCore\uDNS.c">
+			</File>
+			<File
+				RelativePath="..\..\..\..\mDNSCore\uDNS.h">
+			</File>
+		</Filter>
+	</Files>
+	<Globals>
+	</Globals>
+</VisualStudioProject>
diff --git a/mdnsresponder/mDNSWindows/DNSServiceBrowser/Windows/Resources/Application.ico b/mdnsresponder/mDNSWindows/DNSServiceBrowser/Windows/Resources/Application.ico
new file mode 100644
index 0000000..b0a8639
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DNSServiceBrowser/Windows/Resources/Application.ico
Binary files differ
diff --git a/mdnsresponder/mDNSWindows/DNSServiceBrowser/Windows/Resources/Application.rc b/mdnsresponder/mDNSWindows/DNSServiceBrowser/Windows/Resources/Application.rc
new file mode 100644
index 0000000..3943139
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DNSServiceBrowser/Windows/Resources/Application.rc
@@ -0,0 +1,323 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "Resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE 
+BEGIN
+    "Resource.h\0"
+END
+
+2 TEXTINCLUDE 
+BEGIN
+    "#include ""afxres.h""\r\n"
+    "\0"
+END
+
+3 TEXTINCLUDE 
+BEGIN
+    "#define _AFX_NO_SPLITTER_RESOURCES\r\n"
+    "#define _AFX_NO_OLE_RESOURCES\r\n"
+    "#define _AFX_NO_TRACKER_RESOURCES\r\n"
+    "#define _AFX_NO_PROPERTY_RESOURCES\r\n"
+    "\r\n"
+    "#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\r\n"
+    "#ifdef _WIN32\r\n"
+    "LANGUAGE 9, 1\r\n"
+    "#pragma code_page(1252)\r\n"
+    "#endif //_WIN32\r\n"
+    "#include ""Resources\\Application.rc2""  // non-Microsoft Visual C++ edited resources\r\n"
+    "#include ""afxres.rc""         // Standard components\r\n"
+    "#endif\r\n"
+    "\0"
+END
+
+#endif    // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDR_MAIN_ICON           ICON                    "Application.ico"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_CHOOSER_DIALOG DIALOGEX 0, 0, 512, 316
+STYLE DS_SETFONT | DS_FIXEDSYS | DS_CENTER | WS_MINIMIZEBOX | WS_POPUP | 
+    WS_CAPTION | WS_SYSMENU
+EXSTYLE WS_EX_APPWINDOW
+CAPTION "DNSServiceBrowser"
+MENU IDR_CHOOSER_DIALOG_MENU
+FONT 8, "MS Shell Dlg", 0, 0, 0x0
+BEGIN
+    CONTROL         "",IDC_SERVICE_LIST,"SysListView32",LVS_REPORT | 
+                    LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | 
+                    LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,8,8,268,256
+    CONTROL         "",IDC_CHOOSER_LIST,"SysListView32",LVS_REPORT | 
+                    LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | 
+                    LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,282,8,224,170
+    CONTROL         "",IDC_DOMAIN_LIST,"SysListView32",LVS_REPORT | 
+                    LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_ALIGNLEFT | 
+                    LVS_NOSORTHEADER | WS_BORDER | WS_TABSTOP,8,272,268,38
+    GROUPBOX        "Information",IDC_STATIC,282,182,224,128
+    RTEXT           "Name:",IDC_STATIC,288,195,38,8
+    EDITTEXT        IDC_INFO_NAME_TEXT,330,195,168,10,ES_AUTOHSCROLL | 
+                    ES_READONLY | NOT WS_BORDER,WS_EX_STATICEDGE
+    RTEXT           "IP address:",IDC_STATIC,288,208,38,8
+    EDITTEXT        IDC_INFO_IP_TEXT,330,208,168,10,ES_AUTOHSCROLL | 
+                    ES_READONLY | NOT WS_BORDER,WS_EX_STATICEDGE
+    RTEXT           "Interface:",IDC_STATIC,288,221,38,8
+    EDITTEXT        IDC_INFO_INTERFACE_TEXT,330,221,168,10,ES_AUTOHSCROLL | 
+                    ES_READONLY | NOT WS_BORDER,WS_EX_STATICEDGE
+    RTEXT           "Host Name:",IDC_STATIC,287,234,38,8
+    EDITTEXT        IDC_INFO_HOST_NAME_TEXT,330,234,168,10,ES_AUTOHSCROLL | 
+                    ES_READONLY | NOT WS_BORDER,WS_EX_STATICEDGE
+    RTEXT           "Text:",IDC_STATIC,288,247,38,8
+    EDITTEXT        IDC_INFO_TEXT_TEXT,330,247,168,57,ES_MULTILINE | 
+                    ES_AUTOVSCROLL | ES_AUTOHSCROLL | ES_READONLY | NOT 
+                    WS_BORDER,WS_EX_STATICEDGE
+END
+
+IDD_ABOUT_DIALOG DIALOGEX 0, 0, 244, 73
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | WS_POPUP | WS_CAPTION
+CAPTION "About DNSServiceBrowser"
+FONT 8, "MS Shell Dlg", 0, 0, 0x0
+BEGIN
+    ICON            IDR_MAIN_ICON,IDC_ABOUT_APP_ICON,12,12,20,20
+    LTEXT           "DNSServiceBrowser",IDC_ABOUT_APP_NAME_TEXT,44,11,192,
+                    12
+    LTEXT           "Version 1.2d1",IDC_ABOUT_APP_VERSION_TEXT,44,25,192,8
+    LTEXT           "Copyright (C) 2002-2004 Apple Computer, Inc.",
+                    IDC_ABOUT_COPYRIGHT_TEXT,4,60,156,8
+    DEFPUSHBUTTON   "OK",IDOK,192,52,44,14
+END
+
+IDD_LOGIN DIALOGEX 0, 0, 180, 89
+STYLE DS_SETFONT | DS_MODALFRAME | WS_POPUP | WS_CAPTION
+CAPTION "Login"
+FONT 8, "MS Shell Dlg", 0, 0, 0x0
+BEGIN
+    LTEXT           "Enter a username and password. Leave blank to use the default username and/or password.",
+                    IDC_STATIC,8,8,156,16,NOT WS_GROUP
+    RTEXT           "Username:",IDC_STATIC,10,34,36,8,NOT WS_GROUP
+    EDITTEXT        IDC_LOGIN_USERNAME_TEXT,50,32,118,12,ES_AUTOHSCROLL
+    RTEXT           "Password:",IDC_STATIC,10,50,36,8,NOT WS_GROUP
+    EDITTEXT        IDC_LOGIN_PASSWORD_TEXT,50,48,118,12,ES_PASSWORD | 
+                    ES_AUTOHSCROLL
+    DEFPUSHBUTTON   "OK",IDOK,129,70,44,14
+    PUSHBUTTON      "Cancel",IDCANCEL,77,70,44,14
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,0,0,1
+ PRODUCTVERSION 1,0,0,1
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "040904b0"
+        BEGIN
+            VALUE "CompanyName", "Apple Computer, Inc."
+            VALUE "FileDescription", "DNSServiceBrowser for Windows"
+            VALUE "FileVersion", "1, 0, 0, 1"
+            VALUE "InternalName", "DNSServiceBrowser for Windows"
+            VALUE "LegalCopyright", "Copyright (C) 2002-2004 Apple Computer, Inc."
+            VALUE "OriginalFilename", "DNSServiceBrowser.exe"
+            VALUE "ProductName", "DNSServiceBrowser for Windows"
+            VALUE "ProductVersion", "1, 0, 0, 1"
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x409, 1200
+    END
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Menu
+//
+
+IDR_CHOOSER_DIALOG_MENU MENU 
+BEGIN
+    POPUP "File"
+    BEGIN
+        MENUITEM "Close &Window\tCtrl+W",       ID_FILE_CLOSE
+        MENUITEM SEPARATOR
+        MENUITEM "Exit",                        ID_FILE_EXIT
+    END
+    POPUP "Help"
+    BEGIN
+        MENUITEM "About DNSServiceBrowser...", ID_HELP_ABOUT
+    END
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Accelerator
+//
+
+IDR_CHOOSER_DIALOG_MENU_ACCELERATORS ACCELERATORS 
+BEGIN
+    "S",            ID_FILE_SAVE,           VIRTKEY, CONTROL, NOINVERT
+    "W",            ID_FILE_CLOSE,          VIRTKEY, CONTROL, NOINVERT
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// RT_MANIFEST
+//
+
+1 RT_MANIFEST 
+BEGIN
+    0x3f3c, 0x6d78, 0x206c, 0x6576, 0x7372, 0x6f69, 0x3d6e, 0x3122, 0x302e, 
+    0x2022, 0x6e65, 0x6f63, 0x6964, 0x676e, 0x223d, 0x5455, 0x2d46, 0x2238, 
+    0x7320, 0x6174, 0x646e, 0x6c61, 0x6e6f, 0x3d65, 0x7922, 0x7365, 0x3f22, 
+    0x203e, 0x0a0d, 0x613c, 0x7373, 0x6d65, 0x6c62, 0x2079, 0x0a0d, 0x2020, 
+    0x7820, 0x6c6d, 0x736e, 0x223d, 0x7275, 0x3a6e, 0x6373, 0x6568, 0x616d, 
+    0x2d73, 0x696d, 0x7263, 0x736f, 0x666f, 0x2d74, 0x6f63, 0x3a6d, 0x7361, 
+    0x2e6d, 0x3176, 0x2022, 0x0a0d, 0x2020, 0x6d20, 0x6e61, 0x6669, 0x7365, 
+    0x5674, 0x7265, 0x6973, 0x6e6f, 0x223d, 0x2e31, 0x2230, 0x0d3e, 0x3c0a, 
+    0x7361, 0x6573, 0x626d, 0x796c, 0x6449, 0x6e65, 0x6974, 0x7974, 0x0d20, 
+    0x200a, 0x2020, 0x7020, 0x6f72, 0x6563, 0x7373, 0x726f, 0x7241, 0x6863, 
+    0x7469, 0x6365, 0x7574, 0x6572, 0x223d, 0x3878, 0x2236, 0x0d20, 0x200a, 
+    0x2020, 0x7620, 0x7265, 0x6973, 0x6e6f, 0x223d, 0x2e35, 0x2e31, 0x2e30, 
+    0x2230, 0x0a0d, 0x2020, 0x2020, 0x7974, 0x6570, 0x223d, 0x6977, 0x336e, 
+    0x2232, 0x0a0d, 0x2020, 0x2020, 0x616e, 0x656d, 0x223d, 0x7041, 0x2e70, 
+    0x7865, 0x2265, 0x3e2f, 0x0a0d, 0x2020, 0x2020, 0x643c, 0x7365, 0x7263, 
+    0x7069, 0x6974, 0x6e6f, 0x413e, 0x7269, 0x6f50, 0x7472, 0x4120, 0x6d64, 
+    0x6e69, 0x5520, 0x6974, 0x696c, 0x7974, 0x2f3c, 0x6564, 0x6373, 0x6972, 
+    0x7470, 0x6f69, 0x3e6e, 0x0a0d, 0x2020, 0x2020, 0x643c, 0x7065, 0x6e65, 
+    0x6564, 0x636e, 0x3e79, 0x0a0d, 0x2020, 0x2020, 0x643c, 0x7065, 0x6e65, 
+    0x6564, 0x746e, 0x7341, 0x6573, 0x626d, 0x796c, 0x0d3e, 0x200a, 0x2020, 
+    0x3c20, 0x7361, 0x6573, 0x626d, 0x796c, 0x6449, 0x6e65, 0x6974, 0x7974, 
+    0x0a0d, 0x2020, 0x2020, 0x2020, 0x2020, 0x7420, 0x7079, 0x3d65, 0x7722, 
+    0x6e69, 0x3233, 0x0d22, 0x200a, 0x2020, 0x2020, 0x2020, 0x2020, 0x616e, 
+    0x656d, 0x223d, 0x694d, 0x7263, 0x736f, 0x666f, 0x2e74, 0x6957, 0x646e, 
+    0x776f, 0x2e73, 0x6f43, 0x6d6d, 0x6e6f, 0x432d, 0x6e6f, 0x7274, 0x6c6f, 
+    0x2273, 0x0a0d, 0x2020, 0x2020, 0x2020, 0x2020, 0x7620, 0x7265, 0x6973, 
+    0x6e6f, 0x223d, 0x2e36, 0x2e30, 0x2e30, 0x2230, 0x0a0d, 0x2020, 0x2020, 
+    0x2020, 0x2020, 0x7020, 0x6275, 0x696c, 0x4b63, 0x7965, 0x6f54, 0x656b, 
+    0x3d6e, 0x3622, 0x3935, 0x6235, 0x3436, 0x3431, 0x6334, 0x6663, 0x6431, 
+    0x2266, 0x0a0d, 0x2020, 0x2020, 0x2020, 0x2020, 0x6c20, 0x6e61, 0x7567, 
+    0x6761, 0x3d65, 0x2a22, 0x0d22, 0x200a, 0x2020, 0x2020, 0x2020, 0x2020, 
+    0x7270, 0x636f, 0x7365, 0x6f73, 0x4172, 0x6372, 0x6968, 0x6574, 0x7463, 
+    0x7275, 0x3d65, 0x7822, 0x3638, 0x2f22, 0x0d3e, 0x200a, 0x2020, 0x3c20, 
+    0x642f, 0x7065, 0x6e65, 0x6564, 0x746e, 0x7341, 0x6573, 0x626d, 0x796c, 
+    0x0d3e, 0x200a, 0x2020, 0x3c20, 0x642f, 0x7065, 0x6e65, 0x6564, 0x636e, 
+    0x3e79, 0x0a0d, 0x2f3c, 0x7361, 0x6573, 0x626d, 0x796c, 0x0d3e, "\012" 
+END
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO 
+BEGIN
+    IDD_CHOOSER_DIALOG, DIALOG
+    BEGIN
+        RIGHTMARGIN, 468
+    END
+
+    IDD_LOGIN, DIALOG
+    BEGIN
+        BOTTOMMARGIN, 62
+    END
+END
+#endif    // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE 
+BEGIN
+    IDS_ABOUTBOX            "&About DNSServiceBrowser"
+    IDS_CHOOSER_DOMAIN_COLUMN_NAME "Domains"
+    IDP_SOCKETS_INIT_FAILED "Windows sockets initialization failed."
+    IDS_CHOOSER_SERVICE_COLUMN_TYPE "Services"
+    IDS_CHOOSER_CHOOSER_NAME_COLUMN_NAME "Name"
+    IDS_CHOOSER_CHOOSER_IP_COLUMN_NAME "IP Address"
+    IDS_CHOOSER_SERVICE_COLUMN_DESC "Description"
+END
+
+#endif    // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+#define _AFX_NO_SPLITTER_RESOURCES
+#define _AFX_NO_OLE_RESOURCES
+#define _AFX_NO_TRACKER_RESOURCES
+#define _AFX_NO_PROPERTY_RESOURCES
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE 9, 1
+#pragma code_page(1252)
+#endif //_WIN32
+#include "Resources\Application.rc2"  // non-Microsoft Visual C++ edited resources
+#include "afxres.rc"         // Standard components
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+#endif    // not APSTUDIO_INVOKED
+
diff --git a/mdnsresponder/mDNSWindows/DNSServiceBrowser/Windows/Resources/Application.rc2 b/mdnsresponder/mDNSWindows/DNSServiceBrowser/Windows/Resources/Application.rc2
new file mode 100644
index 0000000..ec5e69f
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DNSServiceBrowser/Windows/Resources/Application.rc2
@@ -0,0 +1,20 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+#ifdef APSTUDIO_INVOKED
+	#error this file is not editable by Microsoft Visual C++
+#endif //APSTUDIO_INVOKED
diff --git a/mdnsresponder/mDNSWindows/DNSServiceBrowser/Windows/Resources/Resource.h b/mdnsresponder/mDNSWindows/DNSServiceBrowser/Windows/Resources/Resource.h
new file mode 100644
index 0000000..62602a4
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DNSServiceBrowser/Windows/Resources/Resource.h
@@ -0,0 +1,53 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by Application.rc
+//
+#define IDS_ABOUTBOX                    101
+#define IDS_CHOOSER_DOMAIN_COLUMN_NAME  102
+#define IDP_SOCKETS_INIT_FAILED         103
+#define IDS_CHOOSER_SERVICE_COLUMN_NAME 104
+#define IDS_CHOOSER_SERVICE_COLUMN_TYPE 104
+#define IDS_CHOOSER_CHOOSER_NAME_COLUMN_NAME 105
+#define IDS_CHOOSER_CHOOSER_IP_COLUMN_NAME 106
+#define IDS_CHOOSER_SERVICE_COLUMN_DESC 107
+#define IDC_NAME_TEXT2                  124
+#define IDC_INFO_NAME_TEXT              124
+#define IDC_DESCRIPTION_TEXT2           125
+#define IDC_INFO_TEXT_TEXT              125
+#define IDC_IP_TEXT2                    126
+#define IDC_INFO_IP_TEXT                126
+#define IDC_IP_TEXT3                    127
+#define IDC_INFO_INTERFACE_TEXT         127
+#define IDR_MAIN_ICON                   128
+#define IDC_INFO_INTERFACE_TEXT2        128
+#define IDC_INFO_HOST_NAME_TEXT         128
+#define IDR_CHOOSER_DIALOG_MENU         136
+#define IDD_CHOOSER_DIALOG              143
+#define IDD_ABOUT_DIALOG                144
+#define IDD_LOGIN                       145
+#define IDR_CHOOSER_DIALOG_MENU_ACCELERATORS 146
+#define IDC_CHOOSER_LIST                1000
+#define IDC_SERVICE_LIST2               1001
+#define IDC_SERVICE_LIST                1001
+#define IDC_SERVICE_LIST3               1002
+#define IDC_DOMAIN_LIST                 1002
+#define IDC_ABOUT_APP_NAME_TEXT         1105
+#define IDC_ABOUT_APP_VERSION_TEXT      1106
+#define IDC_ABOUT_COPYRIGHT_TEXT        1107
+#define IDC_ABOUT_APP_ICON              1108
+#define IDC_LOGIN_USERNAME_TEXT         1182
+#define IDC_EDIT2                       1183
+#define IDC_LOGIN_PASSWORD_TEXT         1183
+#define ID_FILE_EXIT                    32771
+#define ID_HELP_ABOUT                   32806
+
+// Next default values for new objects
+// 
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE        164
+#define _APS_NEXT_COMMAND_VALUE         32809
+#define _APS_NEXT_CONTROL_VALUE         1185
+#define _APS_NEXT_SYMED_VALUE           101
+#endif
+#endif
diff --git a/mdnsresponder/mDNSWindows/DNSServiceBrowser/Windows/Sources/AboutDialog.cpp b/mdnsresponder/mDNSWindows/DNSServiceBrowser/Windows/Sources/AboutDialog.cpp
new file mode 100644
index 0000000..8dd6b09
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DNSServiceBrowser/Windows/Sources/AboutDialog.cpp
@@ -0,0 +1,71 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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	<stdlib.h>
+
+#include	"stdafx.h"
+
+#include	"AboutDialog.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+//===========================================================================================================================
+//	Message Map
+//===========================================================================================================================
+
+BEGIN_MESSAGE_MAP(AboutDialog, CDialog)
+	//{{AFX_MSG_MAP(AboutDialog)
+	//}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+//===========================================================================================================================
+//	AboutDialog
+//===========================================================================================================================
+
+AboutDialog::AboutDialog(CWnd* pParent /*=NULL*/)
+	: CDialog(AboutDialog::IDD, pParent)
+{
+	//{{AFX_DATA_INIT(AboutDialog)
+		// Note: the ClassWizard will add member initialization here
+	//}}AFX_DATA_INIT
+}
+
+//===========================================================================================================================
+//	OnInitDialog
+//===========================================================================================================================
+
+BOOL	AboutDialog::OnInitDialog() 
+{
+	CDialog::OnInitDialog();
+	return( true );
+}
+
+//===========================================================================================================================
+//	DoDataExchange
+//===========================================================================================================================
+
+void	AboutDialog::DoDataExchange(CDataExchange* pDX)
+{
+	CDialog::DoDataExchange(pDX);
+	//{{AFX_DATA_MAP(AboutDialog)
+		// Note: the ClassWizard will add DDX and DDV calls here
+	//}}AFX_DATA_MAP
+}
diff --git a/mdnsresponder/mDNSWindows/DNSServiceBrowser/Windows/Sources/AboutDialog.h b/mdnsresponder/mDNSWindows/DNSServiceBrowser/Windows/Sources/AboutDialog.h
new file mode 100644
index 0000000..cdd257c
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DNSServiceBrowser/Windows/Sources/AboutDialog.h
@@ -0,0 +1,62 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+#if !defined(AFX_ABOUTDIALOG_H__4B8A04B2_9735_4F4A_AFCA_15F85FB3D763__INCLUDED_)
+#define AFX_ABOUTDIALOG_H__4B8A04B2_9735_4F4A_AFCA_15F85FB3D763__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include	"Resource.h"
+
+//===========================================================================================================================
+//	AboutDialog
+//===========================================================================================================================
+
+class	AboutDialog : public CDialog
+{
+	public:
+		
+		// Creation/Deletion
+		
+		AboutDialog(CWnd* pParent = NULL);   // standard constructor
+		
+		//{{AFX_DATA(AboutDialog)
+		enum { IDD = IDD_ABOUT_DIALOG };
+			// Note: the ClassWizard will add data members here
+		//}}AFX_DATA
+		
+		// ClassWizard generated virtual function overrides
+		//{{AFX_VIRTUAL(AboutDialog)
+		protected:
+		virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
+		//}}AFX_VIRTUAL
+
+	protected:
+
+		// Generated message map functions
+		//{{AFX_MSG(AboutDialog)
+		virtual BOOL OnInitDialog();
+		//}}AFX_MSG
+		DECLARE_MESSAGE_MAP()
+};
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_ABOUTDIALOG_H__4B8A04B2_9735_4F4A_AFCA_15F85FB3D763__INCLUDED_)
diff --git a/mdnsresponder/mDNSWindows/DNSServiceBrowser/Windows/Sources/Application.cpp b/mdnsresponder/mDNSWindows/DNSServiceBrowser/Windows/Sources/Application.cpp
new file mode 100644
index 0000000..f7f4b03
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DNSServiceBrowser/Windows/Sources/Application.cpp
@@ -0,0 +1,111 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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	<assert.h>
+
+#include	"StdAfx.h"
+
+#include	"DNSServices.h"
+
+#include	"Application.h"
+
+#include	"ChooserDialog.h"
+
+#include	"stdafx.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+//===========================================================================================================================
+//	Message Map
+//===========================================================================================================================
+
+BEGIN_MESSAGE_MAP(Application, CWinApp)
+	//{{AFX_MSG_MAP(Application)
+		// NOTE - the ClassWizard will add and remove mapping macros here.
+		//    DO NOT EDIT what you see in these blocks of generated code!
+	//}}AFX_MSG
+	ON_COMMAND(ID_HELP, CWinApp::OnHelp)
+END_MESSAGE_MAP()
+
+//===========================================================================================================================
+//	Globals
+//===========================================================================================================================
+
+Application		gApp;
+
+//===========================================================================================================================
+//	Application
+//===========================================================================================================================
+
+Application::Application( void )
+{
+	//
+}
+
+//===========================================================================================================================
+//	InitInstance
+//===========================================================================================================================
+
+BOOL	Application::InitInstance()
+{
+	DNSStatus		err;
+	
+	// Standard MFC initialization.
+
+#if( !defined( AFX_DEPRECATED ) )
+	#ifdef _AFXDLL
+		Enable3dControls();			// Call this when using MFC in a shared DLL
+	#else
+		Enable3dControlsStatic();	// Call this when linking to MFC statically
+	#endif
+#endif
+
+	InitCommonControls();
+	
+	// Set up DNS Services.
+	
+	err = DNSServicesInitialize( 0, 512 );
+	assert( err == kDNSNoErr );
+	
+	// Create the chooser dialog.
+	
+	ChooserDialog *		dialog;
+	
+	m_pMainWnd = NULL;
+	dialog = new ChooserDialog;
+	dialog->Create( IDD_CHOOSER_DIALOG );
+	m_pMainWnd = dialog;
+	dialog->ShowWindow( SW_SHOW );
+	
+	return( true );
+}
+
+//===========================================================================================================================
+//	ExitInstance
+//===========================================================================================================================
+
+int	Application::ExitInstance( void )
+{
+	// Clean up DNS Services.
+	
+	DNSServicesFinalize();
+	return( 0 );
+}
diff --git a/mdnsresponder/mDNSWindows/DNSServiceBrowser/Windows/Sources/Application.h b/mdnsresponder/mDNSWindows/DNSServiceBrowser/Windows/Sources/Application.h
new file mode 100644
index 0000000..8368a49
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DNSServiceBrowser/Windows/Sources/Application.h
@@ -0,0 +1,68 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+#if !defined(AFX_ADMIN_H__8663733F_6A15_439F_B568_F5A0125CD572__INCLUDED_)
+#define AFX_ADMIN_H__8663733F_6A15_439F_B568_F5A0125CD572__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include	"stdafx.h"
+
+#ifndef __AFXWIN_H__
+	#error include 'stdafx.h' before including this file for PCH
+#endif
+
+#include	"Resource.h"
+
+//===========================================================================================================================
+//	Globals
+//===========================================================================================================================
+
+extern class Application		gApp;
+
+//===========================================================================================================================
+//	Application
+//===========================================================================================================================
+
+class	Application : public CWinApp
+{
+	public:
+		
+		// Creation/Deletion
+		
+		Application();
+		
+		// ClassWizard generated virtual function overrides
+		//{{AFX_VIRTUAL(Application)
+		public:
+		virtual BOOL InitInstance();
+		virtual int ExitInstance( void );
+		//}}AFX_VIRTUAL
+		
+		//{{AFX_MSG(Application)
+			// NOTE - the ClassWizard will add and remove member functions here.
+			//    DO NOT EDIT what you see in these blocks of generated code !
+		//}}AFX_MSG
+		DECLARE_MESSAGE_MAP()
+};
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_ADMIN_H__8663733F_6A15_439F_B568_F5A0125CD572__INCLUDED_)
diff --git a/mdnsresponder/mDNSWindows/DNSServiceBrowser/Windows/Sources/ChooserDialog.cpp b/mdnsresponder/mDNSWindows/DNSServiceBrowser/Windows/Sources/ChooserDialog.cpp
new file mode 100644
index 0000000..9e4db65
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DNSServiceBrowser/Windows/Sources/ChooserDialog.cpp
@@ -0,0 +1,1426 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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	<assert.h>
+#include	<stdio.h>
+#include	<stdlib.h>
+#include	<string.h>
+#include	<time.h>
+
+#include	<algorithm>
+#include	<memory>
+
+#include	"stdafx.h"
+
+#include	"DNSServices.h"
+
+#include	"Application.h"
+#include	"AboutDialog.h"
+#include	"LoginDialog.h"
+#include	"Resource.h"
+
+#include	"ChooserDialog.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+#if 0
+#pragma mark == Constants ==
+#endif
+
+//===========================================================================================================================
+//	Constants
+//===========================================================================================================================
+
+// Menus
+
+enum
+{
+	kChooserMenuIndexFile	= 0, 
+	kChooserMenuIndexHelp	= 1 
+};
+
+// Domain List
+	
+#define kDomainListDefaultDomainColumnWidth		 		164 
+	
+// Service List
+
+#define kServiceListDefaultServiceColumnTypeWidth		146
+#define kServiceListDefaultServiceColumnDescWidth		230
+	
+// Chooser List
+	
+#define kChooserListDefaultNameColumnWidth				190
+#define kChooserListDefaultIPColumnWidth				120
+
+// Windows User Messages
+
+#define	WM_USER_DOMAIN_ADD								( WM_USER + 0x100 )
+#define	WM_USER_DOMAIN_REMOVE							( WM_USER + 0x101 )
+#define	WM_USER_SERVICE_ADD								( WM_USER + 0x102 )
+#define	WM_USER_SERVICE_REMOVE							( WM_USER + 0x103 )
+#define	WM_USER_RESOLVE									( WM_USER + 0x104 )
+
+#if 0
+#pragma mark == Constants - Service Table ==
+#endif
+
+//===========================================================================================================================
+//	Constants - Service Table
+//===========================================================================================================================
+
+struct	KnownServiceEntry
+{
+	const char *		serviceType;
+	const char *		description;
+	const char *		urlScheme;
+	bool				useText;
+};
+
+static const KnownServiceEntry		kKnownServiceTable[] =
+{
+	{ "_accountedge._tcp.",	 		"MYOB AccountEdge", 										"", 			false },
+	{ "_aecoretech._tcp.", 			"Apple Application Engineering Services",					"", 			false },
+	{ "_afpovertcp._tcp.", 			"Apple File Sharing (AFP)", 								"afp://", 		false },
+	{ "_airport._tcp.", 			"AirPort Base Station",										"", 			false }, 
+	{ "_apple-sasl._tcp.", 			"Apple Password Server", 									"", 			false },
+	{ "_aquamon._tcp.", 			"AquaMon", 													"", 			false },
+	{ "_async._tcp", 				"address-o-sync", 											"", 			false },
+	{ "_auth._tcp.", 				"Authentication Service",									"", 			false },
+	{ "_bootps._tcp.", 				"Bootstrap Protocol Server",								"", 			false },
+	{ "_bousg._tcp.", 				"Bag Of Unusual Strategy Games",							"", 			false },
+	{ "_browse._udp.", 				"DNS Service Discovery",									"", 			false },
+	{ "_cheat._tcp.", 				"The Cheat",												"", 			false },
+	{ "_chess._tcp", 				"Project Gridlock", 										"", 			false },
+	{ "_chfts._tcp", 				"Fluid Theme Server", 										"", 			false },
+	{ "_clipboard._tcp", 			"Clipboard Sharing", 										"", 			false },
+	{ "_contactserver._tcp.", 		"Now Up-to-Date & Contact",									"", 			false },
+	{ "_cvspserver._tcp", 			"CVS PServer", 												"", 			false },
+	{ "_cytv._tcp.", 				"CyTV Network streaming for Elgato EyeTV",					"", 			false },
+	{ "_daap._tcp.", 				"Digital Audio Access Protocol (iTunes)",					"daap://",		false }, 
+	{ "_distcc._tcp", 				"Distributed Compiler", 									"", 			false },
+	{ "_dns-sd._udp", 				"DNS Service Discovery", 									"", 			false },
+	{ "_dpap._tcp.", 				"Digital Picture Access Protocol (iPhoto)",					"", 			false },
+	{ "_earphoria._tcp.", 			"Earphoria",												"", 			false },
+	{ "_ecbyesfsgksc._tcp.", 		"Net Monitor Anti-Piracy Service",							"",				false },
+	{ "_eheap._tcp.", 				"Interactive Room Software",								"",				false },
+	{ "_embrace._tcp.", 			"DataEnvoy",												"",				false },
+	{ "_eppc._tcp.", 				"Remote AppleEvents", 										"eppc://", 		false }, 
+	{ "_exec._tcp.", 				"Remote Process Execution",									"",				false },
+	{ "_facespan._tcp.", 			"FaceSpan",													"",				false },
+	{ "_fjork._tcp.", 				"Fjork",													"",				false },
+	{ "_ftp._tcp.", 				"File Transfer (FTP)", 										"ftp://", 		false }, 
+	{ "_ftpcroco._tcp.", 			"Crocodile FTP Server",										"",				false },
+	{ "_gbs-smp._tcp.", 			"SnapMail",													"",				false },
+	{ "_gbs-stp._tcp.", 			"SnapTalk",													"",				false },
+	{ "_grillezvous._tcp.", 		"Roxio ToastAnywhere(tm) Recorder Sharing",					"",				false },
+	{ "_h323._tcp.", 				"H.323",													"",				false },
+	{ "_hotwayd._tcp", 				"Hotwayd", 													"", 			false },
+	{ "_http._tcp.", 				"Web Server (HTTP)", 										"http://", 		true  }, 
+	{ "_hydra._tcp", 				"SubEthaEdit", 												"", 			false },
+	{ "_ica-networking._tcp.", 		"Image Capture Networking",									"", 			false }, 
+	{ "_ichalkboard._tcp.", 		"iChalk",													"", 			false }, 
+	{ "_ichat._tcp.", 				"iChat",				 									"ichat://",		false }, 
+	{ "_iconquer._tcp.",	 		"iConquer",													"", 			false }, 
+	{ "_imap._tcp.", 				"Internet Message Access Protocol",							"",				false },
+	{ "_imidi._tcp.", 				"iMidi",													"",				false },
+	{ "_ipp._tcp.", 				"Printer (IPP)", 											"ipp://", 		false },
+	{ "_ishare._tcp.", 				"iShare",													"",				false },
+	{ "_isparx._tcp.", 				"iSparx",													"",				false },
+	{ "_istorm._tcp", 				"iStorm", 													"", 			false },
+	{ "_iwork._tcp.", 				"iWork Server",												"",				false },
+	{ "_liaison._tcp.", 			"Liaison",													"",				false },
+	{ "_login._tcp.", 				"Remote Login a la Telnet",									"",				false },
+	{ "_lontalk._tcp.", 			"LonTalk over IP (ANSI 852)",								"",				false },
+	{ "_lonworks._tcp.", 			"Echelon LNS Remote Client",								"",				false },
+	{ "_macfoh-remote._tcp.", 		"MacFOH Remote",											"",				false },
+	{ "_moneyworks._tcp.", 			"MoneyWorks",												"",				false },
+	{ "_mp3sushi._tcp", 			"MP3 Sushi", 												"", 			false },
+	{ "_mttp._tcp.", 				"MenuTunes Sharing",										"",				false },
+	{ "_ncbroadcast._tcp.", 		"Network Clipboard Broadcasts",								"",				false },
+	{ "_ncdirect._tcp.", 			"Network Clipboard Direct Transfers",						"",				false },
+	{ "_ncsyncserver._tcp.", 		"Network Clipboard Sync Server",							"",				false },
+	{ "_newton-dock._tcp.", 		"Escale",													"",				false },
+	{ "_nfs._tcp", 					"NFS", 														"", 			false },
+	{ "_nssocketport._tcp.", 		"DO over NSSocketPort",										"",				false },
+	{ "_omni-bookmark._tcp.", 		"OmniWeb",													"",				false },
+	{ "_openbase._tcp.", 			"OpenBase SQL",												"",				false },
+	{ "_p2pchat._tcp.", 			"Peer-to-Peer Chat",										"",				false },
+	{ "_pdl-datastream._tcp.", 		"Printer (PDL)", 											"pdl://", 		false }, 
+	{ "_poch._tcp.", 				"Parallel OperatiOn and Control Heuristic",					"",				false },
+	{ "_pop_2_ambrosia._tcp.",		"Pop-Pop",													"",				false },
+	{ "_pop3._tcp", 				"POP3 Server", 												"", 			false },
+	{ "_postgresql._tcp", 			"PostgreSQL Server", 										"", 			false },
+	{ "_presence._tcp", 			"iChat AV", 												"", 			false },
+	{ "_printer._tcp.", 			"Printer (LPR)", 											"lpr://", 		false }, 
+	{ "_ptp._tcp.", 				"Picture Transfer (PTP)", 									"ptp://", 		false },
+	{ "_register._tcp", 			"DNS Service Discovery", 									"", 			false },
+	{ "_rfb._tcp.", 				"Remote Frame Buffer",										"",				false },
+	{ "_riousbprint._tcp.", 		"Remote I/O USB Printer Protocol",							"",				false },
+	{ "_rtsp._tcp.", 				"Real Time Stream Control Protocol",						"",				false },
+	{ "_safarimenu._tcp", 			"Safari Menu", 												"", 			false },
+	{ "_scone._tcp", 				"Scone", 													"", 			false },
+	{ "_sdsharing._tcp.", 			"Speed Download",											"",				false },
+	{ "_seeCard._tcp.", 			"seeCard",													"",				false },
+	{ "_services._udp.", 			"DNS Service Discovery",									"",				false },
+	{ "_shell._tcp.", 				"like exec, but automatic authentication",					"",				false },
+	{ "_shout._tcp.", 				"Shout",													"",				false },
+	{ "_shoutcast._tcp", 			"Nicecast", 												"", 			false },
+	{ "_smb._tcp.", 				"Windows File Sharing (SMB)", 								"smb://", 		false }, 
+	{ "_soap._tcp.", 				"Simple Object Access Protocol", 							"", 			false }, 
+	{ "_spincrisis._tcp.", 			"Spin Crisis",												"",				false },
+	{ "_spl-itunes._tcp.", 			"launchTunes",												"",				false },
+	{ "_spr-itunes._tcp.", 			"netTunes",													"",				false },
+	{ "_ssh._tcp.", 				"Secure Shell (SSH)", 										"ssh://", 		false }, 
+	{ "_ssscreenshare._tcp", 		"Screen Sharing", 											"", 			false },
+	{ "_sge-exec._tcp", 			"Sun Grid Engine (Execution Host)", 						"", 			false },
+	{ "_sge-qmaster._tcp", 			"Sun Grid Engine (Master)", 								"", 			false },
+	{ "_stickynotes._tcp", 			"Sticky Notes", 											"", 			false },
+	{ "_strateges._tcp", 			"Strateges", 												"", 			false },
+	{ "_sxqdea._tcp", 				"Synchronize! Pro X", 										"", 			false },
+	{ "_sybase-tds._tcp", 			"Sybase Server", 											"", 			false },
+	{ "_tce._tcp", 					"Power Card", 												"", 			false },
+	{ "_teamlist._tcp", 			"ARTIS Team Task",											"", 			false },
+	{ "_teleport._tcp", 			"teleport",													"", 			false },
+	{ "_telnet._tcp.", 				"Telnet", 													"telnet://", 	false }, 
+	{ "_tftp._tcp.", 				"Trivial File Transfer (TFTP)", 							"tftp://", 		false }, 
+	{ "_tinavigator._tcp.", 		"TI Navigator", 											"", 			false }, 
+	{ "_tivo_servemedia._tcp", 		"TiVo",														"", 			false },
+	{ "_upnp._tcp.", 				"Universal Plug and Play", 									"", 			false }, 
+	{ "_utest._tcp.", 				"uTest", 													"", 			false }, 
+	{ "_vue4rendercow._tcp",		"VueProRenderCow",											"", 			false },
+	{ "_webdav._tcp.", 				"WebDAV", 													"webdav://",	false }, 
+	{ "_whamb._tcp.", 				"Whamb", 													"",				false }, 
+	{ "_workstation._tcp", 			"Macintosh Manager",										"", 			false },
+	{ "_ws._tcp", 					"Web Services",												"", 			false },
+	{ "_xserveraid._tcp.", 			"Xserve RAID",												"xsr://", 		false }, 
+	{ "_xsync._tcp.",	 			"Xserve RAID Synchronization",								"",		 		false }, 
+	
+	{ "",	 						"",															"",		 		false }, 
+	
+	// Unofficial and invalid service types that will be phased out:
+	
+	{ "_clipboardsharing._tcp.",			"ClipboardSharing",									"",		 		false }, 
+	{ "_MacOSXDupSuppress._tcp.",			"Mac OS X Duplicate Suppression",					"",		 		false }, 
+	{ "_netmonitorserver._tcp.",			"Net Monitor Server",								"",		 		false }, 
+	{ "_networkclipboard._tcp.",			"Network Clipboard",								"",		 		false }, 
+	{ "_slimdevices_slimp3_cli._tcp.",		"SliMP3 Server Command-Line Interface",				"",		 		false }, 
+	{ "_slimdevices_slimp3_http._tcp.",		"SliMP3 Server Web Interface",						"",		 		false }, 
+	{ "_tieducationalhandhelddevice._tcp.",	"TI Connect Manager",								"",		 		false }, 
+	{ "_tivo_servemedia._tcp.",				"TiVo",												"",		 		false }, 
+	
+	{ NULL,							NULL,														NULL,			false }, 
+};
+
+#if 0
+#pragma mark == Structures ==
+#endif
+
+//===========================================================================================================================
+//	Structures
+//===========================================================================================================================
+
+struct	DomainEventInfo
+{
+	DNSBrowserEventType		eventType;
+	CString					domain;
+	DNSNetworkAddress		ifIP;
+};
+
+struct	ServiceEventInfo
+{
+	DNSBrowserEventType		eventType;
+	std::string				name;
+	std::string				type;
+	std::string				domain;
+	DNSNetworkAddress		ifIP;
+};
+
+#if 0
+#pragma mark == Prototypes ==
+#endif
+
+//===========================================================================================================================
+//	Prototypes
+//===========================================================================================================================
+
+static void
+	BrowserCallBack( 
+		void *					inContext, 
+		DNSBrowserRef			inRef, 
+		DNSStatus				inStatusCode,
+		const DNSBrowserEvent *	inEvent );
+
+static char *	DNSNetworkAddressToString( const DNSNetworkAddress *inAddr, char *outString );
+
+static DWORD	UTF8StringToStringObject( const char *inUTF8, CString &inObject );
+static DWORD	StringObjectToUTF8String( CString &inObject, std::string &outUTF8 );
+
+#if 0
+#pragma mark == Message Map ==
+#endif
+
+//===========================================================================================================================
+//	Message Map
+//===========================================================================================================================
+
+BEGIN_MESSAGE_MAP(ChooserDialog, CDialog)
+	//{{AFX_MSG_MAP(ChooserDialog)
+	ON_WM_SYSCOMMAND()
+	ON_NOTIFY(LVN_ITEMCHANGED, IDC_DOMAIN_LIST, OnDomainListChanged)
+	ON_NOTIFY(LVN_ITEMCHANGED, IDC_SERVICE_LIST, OnServiceListChanged)
+	ON_NOTIFY(LVN_ITEMCHANGED, IDC_CHOOSER_LIST, OnChooserListChanged)
+	ON_NOTIFY(NM_DBLCLK, IDC_CHOOSER_LIST, OnChooserListDoubleClick)
+	ON_COMMAND(ID_HELP_ABOUT, OnAbout)
+	ON_WM_INITMENUPOPUP()
+	ON_WM_ACTIVATE()
+	ON_COMMAND(ID_FILE_CLOSE, OnFileClose)
+	ON_COMMAND(ID_FILE_EXIT, OnExit)
+	ON_WM_CLOSE()
+	ON_WM_NCDESTROY()
+	//}}AFX_MSG_MAP
+	ON_MESSAGE( WM_USER_DOMAIN_ADD, OnDomainAdd )
+	ON_MESSAGE( WM_USER_DOMAIN_REMOVE, OnDomainRemove )
+	ON_MESSAGE( WM_USER_SERVICE_ADD, OnServiceAdd )
+	ON_MESSAGE( WM_USER_SERVICE_REMOVE, OnServiceRemove )
+	ON_MESSAGE( WM_USER_RESOLVE, OnResolve )
+END_MESSAGE_MAP()
+
+#if 0
+#pragma mark == Routines ==
+#endif
+
+//===========================================================================================================================
+//	ChooserDialog
+//===========================================================================================================================
+
+ChooserDialog::ChooserDialog( CWnd *inParent )
+	: CDialog( ChooserDialog::IDD, inParent)
+{
+	//{{AFX_DATA_INIT(ChooserDialog)
+		// Note: the ClassWizard will add member initialization here
+	//}}AFX_DATA_INIT
+	
+	// Load menu accelerator table.
+
+	mMenuAcceleratorTable = ::LoadAccelerators( AfxGetInstanceHandle(), MAKEINTRESOURCE( IDR_CHOOSER_DIALOG_MENU_ACCELERATORS ) );
+	assert( mMenuAcceleratorTable );
+	
+	mBrowser 			= NULL;
+	mIsServiceBrowsing	= false;
+}
+
+//===========================================================================================================================
+//	~ChooserDialog
+//===========================================================================================================================
+
+ChooserDialog::~ChooserDialog( void )
+{
+	if( mBrowser )
+	{
+		DNSStatus		err;
+		
+		err = DNSBrowserRelease( mBrowser, 0 );
+		assert( err == kDNSNoErr );
+	}
+}
+
+//===========================================================================================================================
+//	DoDataExchange
+//===========================================================================================================================
+
+void ChooserDialog::DoDataExchange( CDataExchange *pDX )
+{
+	CDialog::DoDataExchange(pDX);
+
+	//{{AFX_DATA_MAP(ChooserDialog)
+	DDX_Control(pDX, IDC_SERVICE_LIST, mServiceList);
+	DDX_Control(pDX, IDC_DOMAIN_LIST, mDomainList);
+	DDX_Control(pDX, IDC_CHOOSER_LIST, mChooserList);
+	//}}AFX_DATA_MAP
+}
+
+//===========================================================================================================================
+//	OnInitDialog
+//===========================================================================================================================
+
+BOOL	ChooserDialog::OnInitDialog( void )
+{
+	HICON			icon;
+	BOOL			result;
+	CString			tempString;
+	DNSStatus		err;
+	
+	// Initialize our parent.
+
+	CDialog::OnInitDialog();
+	
+	// Set up the window icon.
+	
+	icon = AfxGetApp()->LoadIcon( IDR_MAIN_ICON );
+	assert( icon );
+	if( icon )
+	{
+		SetIcon( icon, TRUE );		// Set big icon
+		SetIcon( icon, FALSE );		// Set small icon
+	}
+	
+	// Set up the Domain List.
+	
+	result = tempString.LoadString( IDS_CHOOSER_DOMAIN_COLUMN_NAME );
+	assert( result );
+	mDomainList.InsertColumn( 0, tempString, LVCFMT_LEFT, kDomainListDefaultDomainColumnWidth );
+	
+	// Set up the Service List.
+	
+	result = tempString.LoadString( IDS_CHOOSER_SERVICE_COLUMN_TYPE );
+	assert( result );
+	mServiceList.InsertColumn( 0, tempString, LVCFMT_LEFT, kServiceListDefaultServiceColumnTypeWidth );
+	
+	result = tempString.LoadString( IDS_CHOOSER_SERVICE_COLUMN_DESC );
+	assert( result );
+	mServiceList.InsertColumn( 1, tempString, LVCFMT_LEFT, kServiceListDefaultServiceColumnDescWidth );
+	
+	PopulateServicesList();
+	
+	// Set up the Chooser List.
+	
+	result = tempString.LoadString( IDS_CHOOSER_CHOOSER_NAME_COLUMN_NAME );
+	assert( result );
+	mChooserList.InsertColumn( 0, tempString, LVCFMT_LEFT, kChooserListDefaultNameColumnWidth );
+	
+	result = tempString.LoadString( IDS_CHOOSER_CHOOSER_IP_COLUMN_NAME );
+	assert( result );
+	mChooserList.InsertColumn( 1, tempString, LVCFMT_LEFT, kChooserListDefaultIPColumnWidth );
+	
+	// Set up the other controls.
+	
+	UpdateInfoDisplay();
+	
+	// Start browsing for domains.
+	
+	err = DNSBrowserCreate( 0, BrowserCallBack, this, &mBrowser );
+	assert( err == kDNSNoErr );
+	
+	err = DNSBrowserStartDomainSearch( mBrowser, 0 );
+	assert( err == kDNSNoErr );
+	
+	return( true );
+}
+
+//===========================================================================================================================
+//	OnFileClose
+//===========================================================================================================================
+
+void ChooserDialog::OnFileClose() 
+{
+	OnClose();
+}
+
+//===========================================================================================================================
+//	OnActivate
+//===========================================================================================================================
+
+void ChooserDialog::OnActivate( UINT nState, CWnd* pWndOther, BOOL bMinimized )
+{
+	// Always make the active window the "main" window so modal dialogs work better and the app quits after closing 
+	// the last window.
+
+	gApp.m_pMainWnd = this;
+
+	CDialog::OnActivate(nState, pWndOther, bMinimized);
+}
+
+//===========================================================================================================================
+//	PostNcDestroy
+//===========================================================================================================================
+
+void	ChooserDialog::PostNcDestroy() 
+{
+	// Call the base class to do the normal cleanup.
+
+	delete this;
+}
+
+//===========================================================================================================================
+//	PreTranslateMessage
+//===========================================================================================================================
+
+BOOL	ChooserDialog::PreTranslateMessage(MSG* pMsg) 
+{
+	BOOL		result;
+	
+	result = false;
+	assert( mMenuAcceleratorTable );
+	if( mMenuAcceleratorTable )
+	{
+		result = ::TranslateAccelerator( m_hWnd, mMenuAcceleratorTable, pMsg );
+	}
+	if( !result )
+	{
+		result = CDialog::PreTranslateMessage( pMsg );
+	}
+	return( result );
+}
+
+//===========================================================================================================================
+//	OnInitMenuPopup
+//===========================================================================================================================
+
+void	ChooserDialog::OnInitMenuPopup( CMenu *pPopupMenu, UINT nIndex, BOOL bSysMenu ) 
+{
+	CDialog::OnInitMenuPopup( pPopupMenu, nIndex, bSysMenu );
+
+	switch( nIndex )
+	{
+		case kChooserMenuIndexFile:
+			break;
+
+		case kChooserMenuIndexHelp:
+			break;
+
+		default:
+			break;
+	}
+}
+
+//===========================================================================================================================
+//	OnExit
+//===========================================================================================================================
+
+void ChooserDialog::OnExit() 
+{
+	OnClose();
+}
+
+//===========================================================================================================================
+//	OnAbout
+//===========================================================================================================================
+
+void	ChooserDialog::OnAbout() 
+{
+	AboutDialog		dialog;
+	
+	dialog.DoModal();
+}
+
+//===========================================================================================================================
+//	OnSysCommand
+//===========================================================================================================================
+
+void	ChooserDialog::OnSysCommand( UINT inID, LPARAM inParam ) 
+{
+	CDialog::OnSysCommand( inID, inParam );
+}
+
+//===========================================================================================================================
+//	OnClose
+//===========================================================================================================================
+
+void ChooserDialog::OnClose() 
+{
+	StopBrowsing();
+	
+	gApp.m_pMainWnd = this;
+	DestroyWindow();
+}
+
+//===========================================================================================================================
+//	OnNcDestroy
+//===========================================================================================================================
+
+void ChooserDialog::OnNcDestroy() 
+{
+	gApp.m_pMainWnd = this;
+
+	CDialog::OnNcDestroy();
+}
+
+//===========================================================================================================================
+//	OnDomainListChanged
+//===========================================================================================================================
+
+void	ChooserDialog::OnDomainListChanged( NMHDR *pNMHDR, LRESULT *pResult ) 
+{
+	UNUSED_ALWAYS( pNMHDR );
+	
+	// Domain list changes have similar effects to service list changes so reuse that code path by calling it here.
+	
+	OnServiceListChanged( NULL, NULL );
+	
+	*pResult = 0;
+}
+
+//===========================================================================================================================
+//	OnServiceListChanged
+//===========================================================================================================================
+
+void	ChooserDialog::OnServiceListChanged( NMHDR *pNMHDR, LRESULT *pResult ) 
+{
+	int				selectedType;
+	int				selectedDomain;
+	
+	UNUSED_ALWAYS( pNMHDR );
+	
+	// Stop any existing service search.
+	
+	StopBrowsing();
+	
+	// If a domain and service type are selected, start searching for the service type on the domain.
+	
+	selectedType 	= mServiceList.GetNextItem( -1, LVNI_SELECTED );
+	selectedDomain 	= mDomainList.GetNextItem( -1, LVNI_SELECTED );
+	
+	if( ( selectedType >= 0 ) && ( selectedDomain >= 0 ) )
+	{
+		CString				s;
+		std::string			utf8;
+		const char *		type;
+		
+		s = mDomainList.GetItemText( selectedDomain, 0 );
+		StringObjectToUTF8String( s, utf8 );
+		type = mServiceTypes[ selectedType ].serviceType.c_str();
+		if( *type != '\0' )
+		{
+			StartBrowsing( type, utf8.c_str() );
+		}
+	}
+	
+	if( pResult )
+	{
+		*pResult = 0;
+	}
+}
+
+//===========================================================================================================================
+//	OnChooserListChanged
+//===========================================================================================================================
+
+void	ChooserDialog::OnChooserListChanged( NMHDR *pNMHDR, LRESULT *pResult ) 
+{
+	UNUSED_ALWAYS( pNMHDR );
+	
+	UpdateInfoDisplay();
+	*pResult = 0;
+}
+
+//===========================================================================================================================
+//	OnChooserListDoubleClick
+//===========================================================================================================================
+
+void	ChooserDialog::OnChooserListDoubleClick( NMHDR *pNMHDR, LRESULT *pResult )
+{
+	int		selectedItem;
+	
+	UNUSED_ALWAYS( pNMHDR );
+	
+	// Display the service instance if it is selected. Otherwise, clear all the info.
+	
+	selectedItem = mChooserList.GetNextItem( -1, LVNI_SELECTED );
+	if( selectedItem >= 0 )
+	{
+		ServiceInstanceInfo *			p;
+		CString							url;
+		const KnownServiceEntry *		service;
+		
+		assert( selectedItem < (int) mServiceInstances.size() );
+		p = &mServiceInstances[ selectedItem ];
+		
+		// Search for a known service type entry that matches.
+		
+		for( service = kKnownServiceTable; service->serviceType; ++service )
+		{
+			if( p->type == service->serviceType )
+			{
+				break;
+			}
+		}
+		if( service->serviceType )
+		{
+			const char *		text;
+			
+			// Create a URL representing the service instance.
+			
+			if( strcmp( service->serviceType, "_smb._tcp." ) == 0 )
+			{
+				// Special case for SMB (no port number).
+				
+				url.Format( TEXT( "%s%s/" ), service->urlScheme, (const char *) p->ip.c_str() ); 
+			}
+			else if( strcmp( service->serviceType, "_ftp._tcp." ) == 0 )
+			{
+				// Special case for FTP to get login info.
+
+				LoginDialog		dialog;
+				CString			username;
+				CString			password;
+				
+				if( !dialog.GetLogin( username, password ) )
+				{
+					goto exit;
+				}
+				
+				// Build URL in the following format:
+				//
+				// ftp://[username[:password]@]<ip>
+				
+				url += service->urlScheme;
+				if( username.GetLength() > 0 )
+				{
+					url += username;
+					if( password.GetLength() > 0 )
+					{
+						url += ':';
+						url += password;
+					}
+					url += '@';
+				}
+				url += p->ip.c_str();
+			}
+			else if( strcmp( service->serviceType, "_http._tcp." ) == 0 )
+			{
+				// Special case for HTTP to exclude "path=" if present.
+				
+				text = service->useText ? p->text.c_str() : "";
+				if( strncmp( text, "path=", 5 ) == 0 )
+				{
+					text += 5;
+				}
+				if( *text != '/' )
+				{
+					url.Format( TEXT( "%s%s/%s" ), service->urlScheme, (const char *) p->ip.c_str(), text );
+				}
+				else
+				{
+					url.Format( TEXT( "%s%s%s" ), service->urlScheme, (const char *) p->ip.c_str(), text );
+				}
+			}
+			else
+			{
+				text = service->useText ? p->text.c_str() : "";
+				url.Format( TEXT( "%s%s/%s" ), service->urlScheme, (const char *) p->ip.c_str(), text ); 
+			}
+			
+			// Let the system open the URL in the correct app.
+			
+			{
+				CWaitCursor		waitCursor;
+				
+				ShellExecute( NULL, TEXT( "open" ), url, TEXT( "" ), TEXT( "c:\\" ), SW_SHOWNORMAL );
+			}
+		}
+	}
+
+exit:
+	*pResult = 0;
+}
+
+//===========================================================================================================================
+//	OnCancel
+//===========================================================================================================================
+
+void ChooserDialog::OnCancel() 
+{
+	// Do nothing.
+}
+
+//===========================================================================================================================
+//	PopulateServicesList
+//===========================================================================================================================
+
+void	ChooserDialog::PopulateServicesList( void )
+{
+	ServiceTypeVector::iterator		i;
+	CString							type;
+	CString							desc;
+	std::string						tmp;
+	
+	// Add a fixed list of known services.
+	
+	if( mServiceTypes.empty() )
+	{
+		const KnownServiceEntry *		service;
+		
+		for( service = kKnownServiceTable; service->serviceType; ++service )
+		{
+			ServiceTypeInfo		info;
+			
+			info.serviceType 	= service->serviceType;
+			info.description 	= service->description;
+			info.urlScheme 		= service->urlScheme;
+			mServiceTypes.push_back( info );
+		}
+	}
+	
+	// Add each service to the list.
+	
+	for( i = mServiceTypes.begin(); i != mServiceTypes.end(); ++i )
+	{
+		const char *		p;
+		const char *		q;
+		
+		p  = ( *i ).serviceType.c_str();
+		if( *p == '_' ) ++p;							// Skip leading '_'.
+		q  = strchr( p, '.' );							// Find first '.'.
+		if( q )	tmp.assign( p, (size_t)( q - p ) );		// Use only up to the first '.'.
+		else	tmp.assign( p );						// No '.' so use the entire string.
+		UTF8StringToStringObject( tmp.c_str(), type );
+		UTF8StringToStringObject( ( *i ).description.c_str(), desc );
+		
+		int		n;
+		
+		n = mServiceList.GetItemCount();
+		mServiceList.InsertItem( n, type );
+		mServiceList.SetItemText( n, 1, desc );
+	}
+	
+	// Select the first service type by default.
+	
+	if( !mServiceTypes.empty() )
+	{
+		mServiceList.SetItemState( 0, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED );
+	}
+}
+
+//===========================================================================================================================
+//	UpdateInfoDisplay
+//===========================================================================================================================
+
+void	ChooserDialog::UpdateInfoDisplay( void )
+{
+	int							selectedItem;
+	std::string					name;
+	CString						s;
+	std::string					ip;
+	std::string					ifIP;
+	std::string					text;
+	std::string					textNewLines;
+	std::string					hostName;
+	CWnd *						item;
+	std::string::iterator		i;
+	
+	// Display the service instance if it is selected. Otherwise, clear all the info.
+	
+	selectedItem = mChooserList.GetNextItem( -1, LVNI_SELECTED );
+	if( selectedItem >= 0 )
+	{
+		ServiceInstanceInfo *		p;
+		
+		assert( selectedItem < (int) mServiceInstances.size() );
+		p = &mServiceInstances[ selectedItem ];
+		
+		name 		= p->name;
+		ip 			= p->ip;
+		ifIP 		= p->ifIP;
+		text 		= p->text;
+		hostName	= p->hostName;
+
+		// Sync up the list items with the actual data (IP address may change).
+		
+		UTF8StringToStringObject( ip.c_str(), s );
+		mChooserList.SetItemText( selectedItem, 1, s );
+	}
+	
+	// Name
+	
+	item = (CWnd *) this->GetDlgItem( IDC_INFO_NAME_TEXT );
+	assert( item );
+	UTF8StringToStringObject( name.c_str(), s );
+	item->SetWindowText( s );
+	
+	// IP
+	
+	item = (CWnd *) this->GetDlgItem( IDC_INFO_IP_TEXT );
+	assert( item );
+	UTF8StringToStringObject( ip.c_str(), s );
+	item->SetWindowText( s );
+	
+	// Interface
+	
+	item = (CWnd *) this->GetDlgItem( IDC_INFO_INTERFACE_TEXT );
+	assert( item );
+	UTF8StringToStringObject( ifIP.c_str(), s );
+	item->SetWindowText( s );
+	
+
+	item = (CWnd *) this->GetDlgItem( IDC_INFO_HOST_NAME_TEXT );
+	assert( item );
+	UTF8StringToStringObject( hostName.c_str(), s );
+	item->SetWindowText( s );
+
+	// Text
+	
+	item = (CWnd *) this->GetDlgItem( IDC_INFO_TEXT_TEXT );
+	assert( item );
+	for( i = text.begin(); i != text.end(); ++i )
+	{
+		if( *i == '\1' )
+		{
+			textNewLines += "\r\n";
+		}
+		else
+		{
+			textNewLines += *i;
+		}
+	}
+	UTF8StringToStringObject( textNewLines.c_str(), s );
+	item->SetWindowText( s );
+}
+
+#if 0
+#pragma mark -
+#endif
+
+//===========================================================================================================================
+//	OnDomainAdd
+//===========================================================================================================================
+
+LONG	ChooserDialog::OnDomainAdd( WPARAM inWParam, LPARAM inLParam )
+{
+	DomainEventInfo *						p;
+	std::auto_ptr < DomainEventInfo >		pAutoPtr;
+	int										n;
+	int										i;
+	CString									domain;
+	CString									s;
+	bool									found;
+	
+	UNUSED_ALWAYS( inWParam );
+	
+	assert( inLParam );
+	p = reinterpret_cast <DomainEventInfo *> ( inLParam );
+	pAutoPtr.reset( p );
+	
+	// Search to see if we already know about this domain. If not, add it to the list.
+	
+	found = false;
+	domain = p->domain;
+	n = mDomainList.GetItemCount();
+	for( i = 0; i < n; ++i )
+	{
+		s = mDomainList.GetItemText( i, 0 );
+		if( s == domain )
+		{
+			found = true;
+			break;
+		}
+	}
+	if( !found )
+	{
+		int		selectedItem;
+		
+		mDomainList.InsertItem( n, domain );
+		
+		// If no domains are selected and the domain being added is a default domain, select it.
+		
+		selectedItem = mDomainList.GetNextItem( -1, LVNI_SELECTED );
+		if( ( selectedItem < 0 ) && ( p->eventType == kDNSBrowserEventTypeAddDefaultDomain ) )
+		{
+			mDomainList.SetItemState( n, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED );
+		}
+	}
+	return( 0 );
+}
+
+//===========================================================================================================================
+//	OnDomainRemove
+//===========================================================================================================================
+
+LONG	ChooserDialog::OnDomainRemove( WPARAM inWParam, LPARAM inLParam )
+{
+	DomainEventInfo *						p;
+	std::auto_ptr < DomainEventInfo >		pAutoPtr;
+	int										n;
+	int										i;
+	CString									domain;
+	CString									s;
+	bool									found;
+	
+	UNUSED_ALWAYS( inWParam );
+	
+	assert( inLParam );
+	p = reinterpret_cast <DomainEventInfo *> ( inLParam );
+	pAutoPtr.reset( p );
+	
+	// Search to see if we know about this domain. If so, remove it from the list.
+	
+	found = false;
+	domain = p->domain;
+	n = mDomainList.GetItemCount();
+	for( i = 0; i < n; ++i )
+	{
+		s = mDomainList.GetItemText( i, 0 );
+		if( s == domain )
+		{
+			found = true;
+			break;
+		}
+	}
+	if( found )
+	{
+		mDomainList.DeleteItem( i );
+	}
+	return( 0 );
+}
+
+//===========================================================================================================================
+//	OnServiceAdd
+//===========================================================================================================================
+
+LONG	ChooserDialog::OnServiceAdd( WPARAM inWParam, LPARAM inLParam )
+{
+	ServiceEventInfo *						p;
+	std::auto_ptr < ServiceEventInfo >		pAutoPtr;
+	
+	UNUSED_ALWAYS( inWParam );
+	
+	assert( inLParam );
+	p = reinterpret_cast <ServiceEventInfo *> ( inLParam );
+	pAutoPtr.reset( p );
+	
+	return( 0 );
+}
+
+//===========================================================================================================================
+//	OnServiceRemove
+//===========================================================================================================================
+
+LONG	ChooserDialog::OnServiceRemove( WPARAM inWParam, LPARAM inLParam )
+{
+	ServiceEventInfo *						p;
+	std::auto_ptr < ServiceEventInfo >		pAutoPtr;
+	bool									found;
+	int										n;
+	int										i;
+	
+	UNUSED_ALWAYS( inWParam );
+	
+	assert( inLParam );
+	p = reinterpret_cast <ServiceEventInfo *> ( inLParam );
+	pAutoPtr.reset( p );
+	
+	// Search to see if we know about this service instance. If so, remove it from the list.
+	
+	found = false;
+	n = (int) mServiceInstances.size();
+	for( i = 0; i < n; ++i )
+	{
+		ServiceInstanceInfo *		q;
+		
+		// If the name, type, domain, and interface match, treat it as the same service instance.
+		
+		q = &mServiceInstances[ i ];
+		if( ( p->name 	== q->name ) 	&& 
+			( p->type 	== q->type ) 	&& 
+			( p->domain	== q->domain ) )
+		{
+			found = true;
+			break;
+		}
+	}
+	if( found )
+	{
+		mChooserList.DeleteItem( i );
+		assert( i < (int) mServiceInstances.size() );
+		mServiceInstances.erase( mServiceInstances.begin() + i );
+	}
+	return( 0 );
+}
+
+//===========================================================================================================================
+//	OnResolve
+//===========================================================================================================================
+
+LONG	ChooserDialog::OnResolve( WPARAM inWParam, LPARAM inLParam )
+{
+	ServiceInstanceInfo *						p;
+	std::auto_ptr < ServiceInstanceInfo >		pAutoPtr;
+	int											selectedType;
+	int											n;
+	int											i;
+	bool										found;
+	
+	UNUSED_ALWAYS( inWParam );
+	
+	assert( inLParam );
+	p = reinterpret_cast <ServiceInstanceInfo *> ( inLParam );
+	pAutoPtr.reset( p );
+	
+	// Make sure it is for an item of the correct type. This handles any resolves that may have been queued up.
+	
+	selectedType = mServiceList.GetNextItem( -1, LVNI_SELECTED );
+	assert( selectedType >= 0 );
+	if( selectedType >= 0 )
+	{
+		assert( selectedType <= (int) mServiceTypes.size() );
+		if( p->type != mServiceTypes[ selectedType ].serviceType )
+		{
+			goto exit;
+		}
+	}
+	
+	// Search to see if we know about this service instance. If so, update its info. Otherwise, add it to the list.
+	
+	found = false;
+	n = (int) mServiceInstances.size();
+	for( i = 0; i < n; ++i )
+	{
+		ServiceInstanceInfo *		q;
+		
+		// If the name, type, domain, and interface matches, treat it as the same service instance.
+				
+		q = &mServiceInstances[ i ];
+		if( ( p->name 	== q->name ) 	&& 
+			( p->type 	== q->type ) 	&& 
+			( p->domain	== q->domain ) 	&& 
+			( p->ifIP 	== q->ifIP ) )
+		{
+			found = true;
+			break;
+		}
+	}
+	if( found )
+	{
+		mServiceInstances[ i ] = *p;
+	}
+	else
+	{
+		CString		s;
+		
+		mServiceInstances.push_back( *p );
+		UTF8StringToStringObject( p->name.c_str(), s );
+		mChooserList.InsertItem( n, s );
+		
+		UTF8StringToStringObject( p->ip.c_str(), s );
+		mChooserList.SetItemText( n, 1, s );
+		
+		// If this is the only item, select it.
+		
+		if( n == 0 )
+		{
+			mChooserList.SetItemState( n, LVIS_SELECTED | LVIS_FOCUSED, LVIS_SELECTED | LVIS_FOCUSED );
+		}
+	}
+	UpdateInfoDisplay();
+
+exit:
+	return( 0 );
+}
+
+//===========================================================================================================================
+//	StartBrowsing
+//===========================================================================================================================
+
+void	ChooserDialog::StartBrowsing( const char *inType, const char *inDomain )
+{
+	DNSStatus		err;
+	
+	assert( mServiceInstances.empty() );
+	assert( mChooserList.GetItemCount() == 0 );
+	assert( !mIsServiceBrowsing );
+	
+	mChooserList.DeleteAllItems();
+	mServiceInstances.clear();
+	
+	mIsServiceBrowsing = true;
+	err = DNSBrowserStartServiceSearch( mBrowser, kDNSBrowserFlagAutoResolve, inType, inDomain );
+	assert( err == kDNSNoErr );
+}
+
+//===========================================================================================================================
+//	StopBrowsing
+//===========================================================================================================================
+
+void	ChooserDialog::StopBrowsing( void )
+{
+	// If searching, stop.
+	
+	if( mIsServiceBrowsing )
+	{
+		DNSStatus		err;
+		
+		mIsServiceBrowsing = false;
+		err = DNSBrowserStopServiceSearch( mBrowser, 0 );
+		assert( err == kDNSNoErr );
+	}
+	
+	// Remove all service instances.
+	
+	mChooserList.DeleteAllItems();
+	assert( mChooserList.GetItemCount() == 0 );
+	mServiceInstances.clear();
+	assert( mServiceInstances.empty() );
+	UpdateInfoDisplay();
+}
+
+#if 0
+#pragma mark -
+#endif
+
+//===========================================================================================================================
+//	BrowserCallBack
+//===========================================================================================================================
+
+static void
+	BrowserCallBack( 
+		void *					inContext, 
+		DNSBrowserRef			inRef, 
+		DNSStatus				inStatusCode,
+		const DNSBrowserEvent *	inEvent )
+{
+	ChooserDialog *		dialog;
+	UINT 				message;
+	BOOL				posted;
+	
+	UNUSED_ALWAYS( inStatusCode );
+	UNUSED_ALWAYS( inRef );
+	
+	// Check parameters.
+	
+	assert( inContext );
+	dialog = reinterpret_cast <ChooserDialog *> ( inContext );
+	
+	try
+	{
+		switch( inEvent->type )
+		{
+			case kDNSBrowserEventTypeRelease:
+				break;
+			
+			// Domains
+			
+			case kDNSBrowserEventTypeAddDomain:
+			case kDNSBrowserEventTypeAddDefaultDomain:
+			case kDNSBrowserEventTypeRemoveDomain:
+			{
+				DomainEventInfo *						domain;
+				std::auto_ptr < DomainEventInfo >		domainAutoPtr;
+				
+				domain = new DomainEventInfo;
+				domainAutoPtr.reset( domain );
+				
+				domain->eventType 	= inEvent->type;
+				domain->domain 		= inEvent->data.addDomain.domain;
+				domain->ifIP		= inEvent->data.addDomain.interfaceIP;
+				
+				message = ( inEvent->type == kDNSBrowserEventTypeRemoveDomain ) ? WM_USER_DOMAIN_REMOVE : WM_USER_DOMAIN_ADD;
+				posted = ::PostMessage( dialog->GetSafeHwnd(), message, 0, (LPARAM) domain );
+				assert( posted );
+				if( posted )
+				{
+					domainAutoPtr.release();
+				}
+				break;
+			}
+			
+			// Services
+			
+			case kDNSBrowserEventTypeAddService:
+			case kDNSBrowserEventTypeRemoveService:
+			{
+				ServiceEventInfo *						service;
+				std::auto_ptr < ServiceEventInfo >		serviceAutoPtr;
+				
+				service = new ServiceEventInfo;
+				serviceAutoPtr.reset( service );
+				
+				service->eventType 	= inEvent->type;
+				service->name 		= inEvent->data.addService.name;
+				service->type 		= inEvent->data.addService.type;
+				service->domain		= inEvent->data.addService.domain;
+				service->ifIP		= inEvent->data.addService.interfaceIP;
+				
+				message = ( inEvent->type == kDNSBrowserEventTypeAddService ) ? WM_USER_SERVICE_ADD : WM_USER_SERVICE_REMOVE;
+				posted = ::PostMessage( dialog->GetSafeHwnd(), message, 0, (LPARAM) service );
+				assert( posted );
+				if( posted )
+				{
+					serviceAutoPtr.release();
+				}
+				break;
+			}
+			
+			// Resolves
+			
+			case kDNSBrowserEventTypeResolved:
+				if( inEvent->data.resolved->address.addressType == kDNSNetworkAddressTypeIPv4  )
+				{
+					ServiceInstanceInfo *						serviceInstance;
+					std::auto_ptr < ServiceInstanceInfo >		serviceInstanceAutoPtr;
+					char										s[ 32 ];
+					
+					serviceInstance = new ServiceInstanceInfo;
+					serviceInstanceAutoPtr.reset( serviceInstance );
+					
+					serviceInstance->name 		= inEvent->data.resolved->name;
+					serviceInstance->type 		= inEvent->data.resolved->type;
+					serviceInstance->domain		= inEvent->data.resolved->domain;
+					serviceInstance->ip			= DNSNetworkAddressToString( &inEvent->data.resolved->address, s );
+					serviceInstance->ifIP		= DNSNetworkAddressToString( &inEvent->data.resolved->interfaceIP, s );
+					serviceInstance->text 		= inEvent->data.resolved->textRecord;
+					serviceInstance->hostName	= inEvent->data.resolved->hostName;
+
+					posted = ::PostMessage( dialog->GetSafeHwnd(), WM_USER_RESOLVE, 0, (LPARAM) serviceInstance );
+					assert( posted );
+					if( posted )
+					{
+						serviceInstanceAutoPtr.release();
+					}
+				}
+				break;
+			
+			default:
+				break;
+		}
+	}
+	catch( ... )
+	{
+		// Don't let exceptions escape.
+	}
+}
+
+//===========================================================================================================================
+//	DNSNetworkAddressToString
+//
+//	Note: Currently only supports IPv4 network addresses.
+//===========================================================================================================================
+
+static char *	DNSNetworkAddressToString( const DNSNetworkAddress *inAddr, char *outString )
+{
+	const DNSUInt8 *		p;
+	DNSUInt16				port;
+	
+	p = inAddr->u.ipv4.addr.v8;
+	port = ntohs( inAddr->u.ipv4.port.v16 );
+	if( port != kDNSPortInvalid )
+	{
+		sprintf( outString, "%u.%u.%u.%u:%u", p[ 0 ], p[ 1 ], p[ 2 ], p[ 3 ], port );
+	}
+	else
+	{
+		sprintf( outString, "%u.%u.%u.%u", p[ 0 ], p[ 1 ], p[ 2 ], p[ 3 ] );
+	}
+	return( outString );
+}
+
+//===========================================================================================================================
+//	UTF8StringToStringObject
+//===========================================================================================================================
+
+static DWORD	UTF8StringToStringObject( const char *inUTF8, CString &inObject )
+{
+	DWORD		err;
+	int			n;
+	BSTR		unicode;
+	
+	unicode = NULL;
+	
+	n = MultiByteToWideChar( CP_UTF8, 0, inUTF8, -1, NULL, 0 );
+	if( n > 0 )
+	{
+		unicode = (BSTR) malloc( (size_t)( n * sizeof( wchar_t ) ) );
+		if( !unicode )
+		{
+			err = ERROR_INSUFFICIENT_BUFFER;
+			goto exit;
+		}
+
+		n = MultiByteToWideChar( CP_UTF8, 0, inUTF8, -1, unicode, n );
+		try
+		{
+			inObject = unicode;
+		}
+		catch( ... )
+		{
+			err = ERROR_NO_UNICODE_TRANSLATION;
+			goto exit;
+		}
+	}
+	else
+	{
+		inObject = "";
+	}
+	err = 0;
+	
+exit:
+	if( unicode )
+	{
+		free( unicode );
+	}
+	return( err );
+}
+
+//===========================================================================================================================
+//	StringObjectToUTF8String
+//===========================================================================================================================
+
+static DWORD	StringObjectToUTF8String( CString &inObject, std::string &outUTF8 )
+{
+	DWORD		err;
+	BSTR		unicode;
+	int			nUnicode;
+	int			n;
+	char *		utf8;
+	
+	unicode = NULL;
+	utf8	= NULL;
+	
+	nUnicode = inObject.GetLength();
+	if( nUnicode > 0 )
+	{
+		unicode = inObject.AllocSysString();
+		n = WideCharToMultiByte( CP_UTF8, 0, unicode, nUnicode, NULL, 0, NULL, NULL );
+		assert( n > 0 );
+		
+		utf8 = (char *) malloc( (size_t) n );
+		assert( utf8 );
+		if( !utf8 ) { err = ERROR_INSUFFICIENT_BUFFER; goto exit; }
+		
+		n = WideCharToMultiByte( CP_UTF8, 0, unicode, nUnicode, utf8, n, NULL, NULL );
+		assert( n > 0 );
+		
+		try
+		{
+			outUTF8.assign( utf8, n );
+		}
+		catch( ... )
+		{
+			err = ERROR_NO_UNICODE_TRANSLATION;
+			goto exit;
+		}
+	}
+	else
+	{
+		outUTF8.clear();
+	}
+	err = 0;
+	
+exit:
+	if( unicode )
+	{
+		SysFreeString( unicode );
+	}
+	if( utf8 )
+	{
+		free( utf8 );
+	}
+	return( err );
+}
diff --git a/mdnsresponder/mDNSWindows/DNSServiceBrowser/Windows/Sources/ChooserDialog.h b/mdnsresponder/mDNSWindows/DNSServiceBrowser/Windows/Sources/ChooserDialog.h
new file mode 100644
index 0000000..41fd827
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DNSServiceBrowser/Windows/Sources/ChooserDialog.h
@@ -0,0 +1,131 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+#if !defined(AFX_CHOOSERDIALOG_H__AC258704_B307_4901_9F98_A0AC022FD8AC__INCLUDED_)
+#define AFX_CHOOSERDIALOG_H__AC258704_B307_4901_9F98_A0AC022FD8AC__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#include	<string>
+#include	<vector>
+
+#include	"afxcmn.h"
+
+#include	"Resource.h"
+
+#include	"DNSServices.h"
+
+//===========================================================================================================================
+//	Structures
+//===========================================================================================================================
+
+struct	ServiceInstanceInfo
+{
+	std::string		name;
+	std::string		type;
+	std::string		domain;
+	std::string		ip;
+	std::string		text;
+	std::string		ifIP;
+	std::string		hostName;
+};
+
+struct	ServiceTypeInfo
+{
+	std::string		serviceType;
+	std::string		description;
+	std::string		urlScheme;
+};
+
+//===========================================================================================================================
+//	ChooserDialog
+//===========================================================================================================================
+
+class	ChooserDialog : public CDialog
+{
+	public:
+
+		ChooserDialog(CWnd* pParent = NULL);
+		virtual	~ChooserDialog( void );
+		
+		//{{AFX_DATA(ChooserDialog)
+		enum { IDD = IDD_CHOOSER_DIALOG };
+		CListCtrl mServiceList;
+		CListCtrl mDomainList;
+		CListCtrl mChooserList;
+		//}}AFX_DATA
+
+		// ClassWizard generated virtual function overrides
+		//{{AFX_VIRTUAL(ChooserDialog)
+		public:
+		virtual BOOL PreTranslateMessage(MSG* pMsg);
+		protected:
+		virtual void DoDataExchange(CDataExchange* pDX);    // DDX/DDV support
+		virtual void PostNcDestroy();
+		//}}AFX_VIRTUAL
+
+	protected:
+		
+		typedef std::vector < ServiceInstanceInfo >		ServiceInstanceVector;
+		typedef std::vector < ServiceTypeInfo >			ServiceTypeVector;
+		
+		HACCEL						mMenuAcceleratorTable;
+		DNSBrowserRef				mBrowser;
+		BOOL						mIsServiceBrowsing;
+		ServiceInstanceVector		mServiceInstances;
+		ServiceTypeVector			mServiceTypes;
+		
+	public:
+
+		void	PopulateServicesList( void );
+		void	UpdateInfoDisplay( void );
+		
+		void	StartBrowsing( const char *inType, const char *inDomain );
+		void	StopBrowsing( void );
+
+	protected:
+
+		//{{AFX_MSG(ChooserDialog)
+		virtual BOOL OnInitDialog();
+		afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
+		afx_msg void OnDomainListChanged(NMHDR* pNMHDR, LRESULT* pResult);
+		afx_msg void OnServiceListChanged(NMHDR* pNMHDR, LRESULT* pResult);
+		afx_msg void OnChooserListChanged(NMHDR* pNMHDR, LRESULT* pResult);
+		afx_msg void OnChooserListDoubleClick(NMHDR* pNMHDR, LRESULT* pResult);
+		afx_msg void OnAbout();
+		afx_msg void OnInitMenuPopup(CMenu* pPopupMenu, UINT nIndex, BOOL bSysMenu);
+		afx_msg void OnActivate(UINT nState, CWnd* pWndOther, BOOL bMinimized);
+		afx_msg void OnFileClose();
+		virtual void OnCancel();
+		afx_msg void OnExit();
+		afx_msg void OnClose();
+		afx_msg void OnNcDestroy();
+		//}}AFX_MSG
+		afx_msg LONG OnDomainAdd( WPARAM inWParam, LPARAM inLParam );
+		afx_msg LONG OnDomainRemove( WPARAM inWParam, LPARAM inLParam );
+		afx_msg LONG OnServiceAdd( WPARAM inWParam, LPARAM inLParam );
+		afx_msg LONG OnServiceRemove( WPARAM inWParam, LPARAM inLParam );
+		afx_msg LONG OnResolve( WPARAM inWParam, LPARAM inLParam );
+		DECLARE_MESSAGE_MAP()
+};
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_CHOOSERDIALOG_H__AC258704_B307_4901_9F98_A0AC022FD8AC__INCLUDED_)
diff --git a/mdnsresponder/mDNSWindows/DNSServiceBrowser/Windows/Sources/LoginDialog.cpp b/mdnsresponder/mDNSWindows/DNSServiceBrowser/Windows/Sources/LoginDialog.cpp
new file mode 100644
index 0000000..b9a9ec9
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DNSServiceBrowser/Windows/Sources/LoginDialog.cpp
@@ -0,0 +1,109 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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	<assert.h>
+#include	<stdlib.h>
+
+#include	"stdafx.h"
+
+#include	"LoginDialog.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+//===========================================================================================================================
+//	Message Map
+//===========================================================================================================================
+
+BEGIN_MESSAGE_MAP( LoginDialog, CDialog )
+END_MESSAGE_MAP()
+
+//===========================================================================================================================
+//	LoginDialog
+//===========================================================================================================================
+
+LoginDialog::LoginDialog( CWnd *inParent )
+	: CDialog( LoginDialog::IDD, inParent )
+{
+	//
+}
+
+//===========================================================================================================================
+//	OnInitDialog
+//===========================================================================================================================
+
+BOOL	LoginDialog::OnInitDialog( void )
+{
+	CDialog::OnInitDialog();
+	return( TRUE );
+}
+
+//===========================================================================================================================
+//	DoDataExchange
+//===========================================================================================================================
+
+void	LoginDialog::DoDataExchange( CDataExchange *inDX )
+{
+	CDialog::DoDataExchange( inDX );
+}
+
+//===========================================================================================================================
+//	OnOK
+//===========================================================================================================================
+
+void	LoginDialog::OnOK( void )
+{
+	const CWnd *		control;
+		
+	// Username
+	
+	control = GetDlgItem( IDC_LOGIN_USERNAME_TEXT );
+	assert( control );
+	if( control )
+	{
+		control->GetWindowText( mUsername );
+	}
+	
+	// Password
+	
+	control = GetDlgItem( IDC_LOGIN_PASSWORD_TEXT );
+	assert( control );
+	if( control )
+	{
+		control->GetWindowText( mPassword );
+	}
+	
+	CDialog::OnOK();
+}
+
+//===========================================================================================================================
+//	GetLogin
+//===========================================================================================================================
+
+BOOL	LoginDialog::GetLogin( CString &outUsername, CString &outPassword )
+{
+	if( DoModal() == IDOK )
+	{
+		outUsername = mUsername;
+		outPassword = mPassword;
+		return( TRUE );
+	}
+	return( FALSE );
+}
diff --git a/mdnsresponder/mDNSWindows/DNSServiceBrowser/Windows/Sources/LoginDialog.h b/mdnsresponder/mDNSWindows/DNSServiceBrowser/Windows/Sources/LoginDialog.h
new file mode 100644
index 0000000..e53beb6
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DNSServiceBrowser/Windows/Sources/LoginDialog.h
@@ -0,0 +1,53 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+#ifndef	__LOGIN_DIALOG__
+#define	__LOGIN_DIALOG__
+
+#pragma once
+
+#include	"Resource.h"
+
+//===========================================================================================================================
+//	LoginDialog
+//===========================================================================================================================
+
+class	LoginDialog : public CDialog
+{
+	protected:
+	
+		CString		mUsername;
+		CString		mPassword;
+		
+	public:
+		
+		enum { IDD = IDD_LOGIN };
+		
+		LoginDialog( CWnd *inParent = NULL );
+		
+		virtual BOOL	GetLogin( CString &outUsername, CString &outPassword );
+	
+	protected:
+
+		virtual BOOL	OnInitDialog( void );
+		virtual void	DoDataExchange( CDataExchange *inDX );
+		virtual void	OnOK( void );
+		
+		DECLARE_MESSAGE_MAP()
+};
+
+#endif	// __LOGIN_DIALOG__
diff --git a/mdnsresponder/mDNSWindows/DNSServiceBrowser/Windows/Sources/StdAfx.cpp b/mdnsresponder/mDNSWindows/DNSServiceBrowser/Windows/Sources/StdAfx.cpp
new file mode 100644
index 0000000..ae2ca2e
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DNSServiceBrowser/Windows/Sources/StdAfx.cpp
@@ -0,0 +1,18 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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	"stdafx.h"
diff --git a/mdnsresponder/mDNSWindows/DNSServiceBrowser/Windows/Sources/StdAfx.h b/mdnsresponder/mDNSWindows/DNSServiceBrowser/Windows/Sources/StdAfx.h
new file mode 100644
index 0000000..c62bd3e
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DNSServiceBrowser/Windows/Sources/StdAfx.h
@@ -0,0 +1,48 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+#if !defined(AFX_STDAFX_H__424305D2_0A97_4AA0_B9B1_A7D90D18EBA0__INCLUDED_)
+#define AFX_STDAFX_H__424305D2_0A97_4AA0_B9B1_A7D90D18EBA0__INCLUDED_
+
+#if _MSC_VER > 1000
+#pragma once
+#endif // _MSC_VER > 1000
+
+#define VC_EXTRALEAN		// Exclude rarely-used stuff from Windows headers
+
+#ifndef WINVER				// Allow use of features specific to Windows 95 and Windows NT 4 or later.
+	#define WINVER 0x0400	// Change this to the appropriate value to target Windows 98 and Windows 2000 or later.
+#endif
+
+#include	<afxwin.h>		// MFC core and standard components
+#include	<afxext.h>		// MFC extensions
+#include	<afxdtctl.h>	// MFC support for Internet Explorer 4 Common Controls
+#ifndef _AFX_NO_AFXCMN_SUPPORT
+	#include	<afxcmn.h>	// MFC support for Windows Common Controls
+#endif // _AFX_NO_AFXCMN_SUPPORT
+
+#include	<winsock2.h>
+
+#include	<stdlib.h>
+
+#include	"DNSServices.h"
+
+#include	"Application.h"
+
+#include	"ChooserDialog.h"
+
+#endif // !defined(AFX_STDAFX_H__424305D2_0A97_4AA0_B9B1_A7D90D18EBA0__INCLUDED_)
diff --git a/mdnsresponder/mDNSWindows/DNSServiceBrowser/WindowsCE/Application.vcc b/mdnsresponder/mDNSWindows/DNSServiceBrowser/WindowsCE/Application.vcc
new file mode 100644
index 0000000..22f443d
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DNSServiceBrowser/WindowsCE/Application.vcc
@@ -0,0 +1,37 @@
+; CLW file contains information for the MFC ClassWizard
+
+[General Info]
+Version=1
+LastClass=BrowserDialog
+LastTemplate=CDialog
+NewFileInclude1=#include "stdafx.h"
+NewFileInclude2=#include "Application.h"
+
+ClassCount=3
+Class1=Application
+Class2=BrowserDialog
+
+ResourceCount=3
+Resource2=IDR_MAINFRAME
+Resource3=IDD_APPLICATION_DIALOG
+
+[CLS:Application]
+Type=0
+HeaderFile=Application.h
+ImplementationFile=Application.cpp
+Filter=N
+
+[CLS:BrowserDialog]
+Type=0
+HeaderFile=BrowserDialog.h
+ImplementationFile=BrowserDialog.cpp
+Filter=D
+
+
+[DLG:IDD_APPLICATION_DIALOG]
+Type=1
+ControlCount=3
+Control1=IDOK,button,1342242817
+Control2=IDCANCEL,button,1342242816
+Control3=IDC_STATIC,static,1342308352
+Class=BrowserDialog
diff --git a/mdnsresponder/mDNSWindows/DNSServiceBrowser/WindowsCE/Application.vcp b/mdnsresponder/mDNSWindows/DNSServiceBrowser/WindowsCE/Application.vcp
new file mode 100644
index 0000000..9c73567
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DNSServiceBrowser/WindowsCE/Application.vcp
@@ -0,0 +1,868 @@
+# Microsoft eMbedded Visual Tools Project File - Name="Application" - Package Owner=<4>
+# Microsoft eMbedded Visual Tools Generated Build File, Format Version 6.02
+# ** DO NOT EDIT **
+
+# TARGTYPE "Win32 (WCE ARMV4) Application" 0xa301
+# TARGTYPE "Win32 (WCE emulator) Application" 0xa601
+
+CFG=Application - Win32 (WCE emulator) Debug
+!MESSAGE This is not a valid makefile. To build this project using NMAKE,
+!MESSAGE use the Export Makefile command and run
+!MESSAGE 
+!MESSAGE NMAKE /f "Application.vcn".
+!MESSAGE 
+!MESSAGE You can specify a configuration when running NMAKE
+!MESSAGE by defining the macro CFG on the command line. For example:
+!MESSAGE 
+!MESSAGE NMAKE /f "Application.vcn" CFG="Application - Win32 (WCE emulator) Debug"
+!MESSAGE 
+!MESSAGE Possible choices for configuration are:
+!MESSAGE 
+!MESSAGE "Application - Win32 (WCE emulator) Release" (based on "Win32 (WCE emulator) Application")
+!MESSAGE "Application - Win32 (WCE emulator) Debug" (based on "Win32 (WCE emulator) Application")
+!MESSAGE "Application - Win32 (WCE ARMV4) Release" (based on "Win32 (WCE ARMV4) Application")
+!MESSAGE "Application - Win32 (WCE ARMV4) Debug" (based on "Win32 (WCE ARMV4) Application")
+!MESSAGE 
+
+# Begin Project
+# PROP AllowPerConfigDependencies 0
+# PROP Scc_ProjName ""
+# PROP Scc_LocalPath ""
+# PROP ATL_Project 2
+
+!IF  "$(CFG)" == "Application - Win32 (WCE emulator) Release"
+
+# PROP BASE Use_MFC 2
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "emulatorRel"
+# PROP BASE Intermediate_Dir "emulatorRel"
+# PROP BASE CPU_ID "{32E52003-403E-442D-BE48-DE10F8C6131D}"
+# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 2
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "emulatorRel"
+# PROP Intermediate_Dir "emulatorRel"
+# PROP CPU_ID "{32E52003-403E-442D-BE48-DE10F8C6131D}"
+# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+RSC=rc.exe
+# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "NDEBUG" /d "$(CePlatform)" /d "_X86_" /d "x86" /d "_i386_" /d "_AFXDLL" /r
+# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "NDEBUG" /d "$(CePlatform)" /d "_X86_" /d "x86" /d "_i386_" /d "_AFXDLL" /r
+CPP=cl.exe
+# ADD BASE CPP /nologo /W3 /D "_i386_" /D UNDER_CE=$(CEVersion) /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "UNICODE" /D "_UNICODE" /D "_X86_" /D "x86" /D "NDEBUG" /D "_WIN32_WCE_CEPC" /D "_AFXDLL" /Yu"stdafx.h" /Gs8192 /GF /O2 /c
+# ADD CPP /nologo /W3 /WX /I "Resources" /I "Sources" /I "..\..\..\DNSServices" /I "..\..\..\..\mDNSCore" /I "..\..\..\..\mDNSCore\PlatformSupport" /D "_i386_" /D "_X86_" /D "x86" /D "NDEBUG" /D "_WIN32_WCE_CEPC" /D UNDER_CE=$(CEVersion) /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "UNICODE" /D "_UNICODE" /D "_AFXDLL" /D DNS_SD_CLIENT_ENABLED=0 /Gs8192 /GF /O2 /c
+# SUBTRACT CPP /YX /Yc /Yu
+MTL=midl.exe
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 /nologo /base:"0x00010000" /stack:0x10000,0x1000 /entry:"wWinMainCRTStartup" /subsystem:$(CESubsystem) /MACHINE:IX86
+# ADD LINK32 ws2.lib /nologo /base:"0x00010000" /stack:0x10000,0x1000 /entry:"wWinMainCRTStartup" /subsystem:$(CESubsystem) /MACHINE:IX86
+
+!ELSEIF  "$(CFG)" == "Application - Win32 (WCE emulator) Debug"
+
+# PROP BASE Use_MFC 2
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "emulatorDbg"
+# PROP BASE Intermediate_Dir "emulatorDbg"
+# PROP BASE CPU_ID "{32E52003-403E-442D-BE48-DE10F8C6131D}"
+# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 2
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "emulatorDbg"
+# PROP Intermediate_Dir "emulatorDbg"
+# PROP CPU_ID "{32E52003-403E-442D-BE48-DE10F8C6131D}"
+# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+RSC=rc.exe
+# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "DEBUG" /d "$(CePlatform)" /d "_X86_" /d "x86" /d "_i386_" /d "_AFXDLL" /r
+# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "UNICODE" /d "_UNICODE" /d "DEBUG" /d "$(CePlatform)" /d "_X86_" /d "x86" /d "_i386_" /d "_AFXDLL" /r
+CPP=cl.exe
+# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D "_i386_" /D UNDER_CE=$(CEVersion) /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "UNICODE" /D "_UNICODE" /D "_X86_" /D "x86" /D "_WIN32_WCE_CEPC" /D "_AFXDLL" /Yu"stdafx.h" /Gs8192 /GF /c
+# ADD CPP /nologo /W3 /WX /Zi /Od /I "Resources" /I "Sources" /I "..\..\..\DNSServices" /I "..\..\..\..\mDNSCore" /I "..\..\..\..\mDNSCore\PlatformSupport" /D "_i386_" /D "_X86_" /D "x86" /D "_WIN32_WCE_CEPC" /D UNDER_CE=$(CEVersion) /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "UNICODE" /D "_UNICODE" /D "_AFXDLL" /D DNS_SD_CLIENT_ENABLED=0 /FR /Gs8192 /GF /c
+MTL=midl.exe
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 /nologo /base:"0x00010000" /stack:0x10000,0x1000 /entry:"wWinMainCRTStartup" /debug /subsystem:$(CESubsystem) /MACHINE:IX86
+# ADD LINK32 ws2.lib /nologo /base:"0x00010000" /stack:0x10000,0x1000 /entry:"wWinMainCRTStartup" /debug /subsystem:$(CESubsystem) /MACHINE:IX86
+
+!ELSEIF  "$(CFG)" == "Application - Win32 (WCE ARMV4) Release"
+
+# PROP BASE Use_MFC 2
+# PROP BASE Use_Debug_Libraries 0
+# PROP BASE Output_Dir "ARMV4Rel"
+# PROP BASE Intermediate_Dir "ARMV4Rel"
+# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}"
+# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 2
+# PROP Use_Debug_Libraries 0
+# PROP Output_Dir "ARMV4Rel"
+# PROP Intermediate_Dir "ARMV4Rel"
+# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}"
+# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+RSC=rc.exe
+# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /d "_AFXDLL" /r
+# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "NDEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /d "_AFXDLL" /r
+CPP=clarm.exe
+# ADD BASE CPP /nologo /W3 /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "UNICODE" /D "_UNICODE" /D "NDEBUG" /D "_AFXDLL" /Yu"stdafx.h" /O2 /M$(CECrtMT) /c
+# ADD CPP /nologo /W3 /WX /I "Resources" /I "Sources" /I "..\..\..\DNSServices" /I "..\..\..\..\mDNSCore" /I "..\..\..\..\mDNSCore\PlatformSupport" /D "ARM" /D "_ARM_" /D "ARMV4" /D "NDEBUG" /D UNDER_CE=$(CEVersion) /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "UNICODE" /D "_UNICODE" /D "_AFXDLL" /D DNS_SD_CLIENT_ENABLED=0 /O2 /M$(CECrtMT) /c
+# SUBTRACT CPP /YX /Yc /Yu
+MTL=midl.exe
+# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32
+# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /o "NUL" /win32
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 /nologo /base:"0x00010000" /stack:0x10000,0x1000 /entry:"wWinMainCRTStartup" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM
+# ADD LINK32 ws2.lib /nologo /base:"0x00010000" /stack:0x10000,0x1000 /entry:"wWinMainCRTStartup" /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM
+
+!ELSEIF  "$(CFG)" == "Application - Win32 (WCE ARMV4) Debug"
+
+# PROP BASE Use_MFC 2
+# PROP BASE Use_Debug_Libraries 1
+# PROP BASE Output_Dir "ARMV4Dbg"
+# PROP BASE Intermediate_Dir "ARMV4Dbg"
+# PROP BASE CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}"
+# PROP BASE Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}"
+# PROP BASE Target_Dir ""
+# PROP Use_MFC 2
+# PROP Use_Debug_Libraries 1
+# PROP Output_Dir "ARMV4Dbg"
+# PROP Intermediate_Dir "ARMV4Dbg"
+# PROP CPU_ID "{ECBEA43D-CD7B-4852-AD55-D4227B5D624B}"
+# PROP Platform_ID "{8A9A2F80-6887-11D3-842E-005004848CBA}"
+# PROP Ignore_Export_Lib 0
+# PROP Target_Dir ""
+RSC=rc.exe
+# ADD BASE RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /d "_AFXDLL" /r
+# ADD RSC /l 0x409 /d UNDER_CE=$(CEVersion) /d _WIN32_WCE=$(CEVersion) /d "DEBUG" /d "UNICODE" /d "_UNICODE" /d "$(CePlatform)" /d "ARM" /d "_ARM_" /d "ARMV4" /d "_AFXDLL" /r
+CPP=clarm.exe
+# ADD BASE CPP /nologo /W3 /Zi /Od /D "DEBUG" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "UNICODE" /D "_UNICODE" /D "_AFXDLL" /Yu"stdafx.h" /M$(CECrtMTDebug) /c
+# ADD CPP /nologo /W3 /WX /Zi /Od /I "Resources" /I "Sources" /I "..\..\..\DNSServices" /I "..\..\..\..\mDNSCore" /I "..\..\..\..\mDNSCore\PlatformSupport" /D "ARM" /D "_ARM_" /D "ARMV4" /D UNDER_CE=$(CEVersion) /D _WIN32_WCE=$(CEVersion) /D "$(CePlatform)" /D "UNICODE" /D "_UNICODE" /D "_AFXDLL" /D DNS_SD_CLIENT_ENABLED=0 /FR /M$(CECrtMTDebug) /c
+# SUBTRACT CPP /YX /Yc /Yu
+MTL=midl.exe
+# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32
+# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /o "NUL" /win32
+BSC32=bscmake.exe
+# ADD BASE BSC32 /nologo
+# ADD BSC32 /nologo
+LINK32=link.exe
+# ADD BASE LINK32 /nologo /base:"0x00010000" /stack:0x10000,0x1000 /entry:"wWinMainCRTStartup" /debug /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM
+# ADD LINK32 ws2.lib /nologo /base:"0x00010000" /stack:0x10000,0x1000 /entry:"wWinMainCRTStartup" /debug /subsystem:$(CESubsystem) /align:"4096" /MACHINE:ARM
+
+!ENDIF 
+
+# Begin Target
+
+# Name "Application - Win32 (WCE emulator) Release"
+# Name "Application - Win32 (WCE emulator) Debug"
+# Name "Application - Win32 (WCE ARMV4) Release"
+# Name "Application - Win32 (WCE ARMV4) Debug"
+# Begin Group "Source Files"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\Sources\Application.cpp
+
+!IF  "$(CFG)" == "Application - Win32 (WCE emulator) Release"
+
+DEP_CPP_APPLI=\
+	"..\..\..\DNSServices\DNSServices.h"\
+	".\Sources\Application.h"\
+	".\Sources\BrowserDialog.h"\
+	".\Sources\StdAfx.h"\
+	
+
+!ELSEIF  "$(CFG)" == "Application - Win32 (WCE emulator) Debug"
+
+DEP_CPP_APPLI=\
+	"..\..\..\DNSServices\DNSServices.h"\
+	".\Sources\Application.h"\
+	".\Sources\BrowserDialog.h"\
+	".\Sources\StdAfx.h"\
+	
+
+!ELSEIF  "$(CFG)" == "Application - Win32 (WCE ARMV4) Release"
+
+DEP_CPP_APPLI=\
+	"..\..\..\DNSServices\DNSServices.h"\
+	".\Sources\Application.h"\
+	".\Sources\BrowserDialog.h"\
+	".\Sources\StdAfx.h"\
+	
+
+!ELSEIF  "$(CFG)" == "Application - Win32 (WCE ARMV4) Debug"
+
+DEP_CPP_APPLI=\
+	"..\..\..\DNSServices\DNSServices.h"\
+	".\Sources\Application.h"\
+	".\Sources\BrowserDialog.h"\
+	".\Sources\StdAfx.h"\
+	
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\Sources\Application.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Sources\BrowserDialog.cpp
+
+!IF  "$(CFG)" == "Application - Win32 (WCE emulator) Release"
+
+DEP_CPP_BROWS=\
+	"..\..\..\DNSServices\DNSServices.h"\
+	".\Sources\Application.h"\
+	".\Sources\BrowserDialog.h"\
+	".\Sources\StdAfx.h"\
+	
+
+!ELSEIF  "$(CFG)" == "Application - Win32 (WCE emulator) Debug"
+
+DEP_CPP_BROWS=\
+	"..\..\..\DNSServices\DNSServices.h"\
+	".\Sources\Application.h"\
+	".\Sources\BrowserDialog.h"\
+	".\Sources\StdAfx.h"\
+	
+
+!ELSEIF  "$(CFG)" == "Application - Win32 (WCE ARMV4) Release"
+
+DEP_CPP_BROWS=\
+	"..\..\..\DNSServices\DNSServices.h"\
+	".\Sources\Application.h"\
+	".\Sources\BrowserDialog.h"\
+	".\Sources\StdAfx.h"\
+	
+
+!ELSEIF  "$(CFG)" == "Application - Win32 (WCE ARMV4) Debug"
+
+DEP_CPP_BROWS=\
+	"..\..\..\DNSServices\DNSServices.h"\
+	".\Sources\Application.h"\
+	".\Sources\BrowserDialog.h"\
+	".\Sources\StdAfx.h"\
+	
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\Sources\BrowserDialog.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Sources\StdAfx.cpp
+
+!IF  "$(CFG)" == "Application - Win32 (WCE emulator) Release"
+
+DEP_CPP_STDAF=\
+	".\Sources\StdAfx.h"\
+	
+# ADD CPP /Yc"stdafx.h"
+
+!ELSEIF  "$(CFG)" == "Application - Win32 (WCE emulator) Debug"
+
+DEP_CPP_STDAF=\
+	".\Sources\StdAfx.h"\
+	
+# ADD CPP /Yc"stdafx.h"
+
+!ELSEIF  "$(CFG)" == "Application - Win32 (WCE ARMV4) Release"
+
+DEP_CPP_STDAF=\
+	".\Sources\StdAfx.h"\
+	
+# ADD CPP /Yc"stdafx.h"
+
+!ELSEIF  "$(CFG)" == "Application - Win32 (WCE ARMV4) Debug"
+
+DEP_CPP_STDAF=\
+	".\Sources\StdAfx.h"\
+	
+# ADD CPP /Yc"stdafx.h"
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\Sources\StdAfx.h
+# End Source File
+# End Group
+# Begin Group "Resource Files"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=.\Resources\Application.ico
+# End Source File
+# Begin Source File
+
+SOURCE=.\Resources\Application.rc
+
+!IF  "$(CFG)" == "Application - Win32 (WCE emulator) Release"
+
+!ELSEIF  "$(CFG)" == "Application - Win32 (WCE emulator) Debug"
+
+!ELSEIF  "$(CFG)" == "Application - Win32 (WCE ARMV4) Release"
+
+!ELSEIF  "$(CFG)" == "Application - Win32 (WCE ARMV4) Debug"
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=.\Resources\Application.rc2
+# PROP Exclude_From_Scan -1
+# PROP BASE Exclude_From_Build 1
+# PROP Exclude_From_Build 1
+# End Source File
+# Begin Source File
+
+SOURCE=.\Resources\newres.h
+# End Source File
+# Begin Source File
+
+SOURCE=.\Resources\Resource.h
+# End Source File
+# End Group
+# Begin Group "Support"
+
+# PROP Default_Filter ""
+# Begin Source File
+
+SOURCE=..\..\..\CommonServices.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\DebugServices.c
+
+!IF  "$(CFG)" == "Application - Win32 (WCE emulator) Release"
+
+DEP_CPP_DEBUG=\
+	"..\..\..\..\mDNSCore\mDNSClientAPI.h"\
+	"..\..\..\..\mDNSCore\mDNSDebug.h"\
+	"..\..\..\CommonServices.h"\
+	"..\..\..\DebugServices.h"\
+	
+NODEP_CPP_DEBUG=\
+	"..\..\..\intLib.h"\
+	"..\..\..\logLib.h"\
+	"..\..\..\vxWorks.h"\
+	
+
+!ELSEIF  "$(CFG)" == "Application - Win32 (WCE emulator) Debug"
+
+DEP_CPP_DEBUG=\
+	"..\..\..\..\mDNSCore\mDNSClientAPI.h"\
+	"..\..\..\..\mDNSCore\mDNSDebug.h"\
+	"..\..\..\CommonServices.h"\
+	"..\..\..\DebugServices.h"\
+	
+NODEP_CPP_DEBUG=\
+	"..\..\..\intLib.h"\
+	"..\..\..\logLib.h"\
+	"..\..\..\vxWorks.h"\
+	
+
+!ELSEIF  "$(CFG)" == "Application - Win32 (WCE ARMV4) Release"
+
+DEP_CPP_DEBUG=\
+	"..\..\..\..\mDNSCore\mDNSClientAPI.h"\
+	"..\..\..\..\mDNSCore\mDNSDebug.h"\
+	"..\..\..\CommonServices.h"\
+	"..\..\..\DebugServices.h"\
+	
+NODEP_CPP_DEBUG=\
+	"..\..\..\intLib.h"\
+	"..\..\..\logLib.h"\
+	"..\..\..\vxWorks.h"\
+	
+
+!ELSEIF  "$(CFG)" == "Application - Win32 (WCE ARMV4) Debug"
+
+DEP_CPP_DEBUG=\
+	"..\..\..\..\mDNSCore\mDNSClientAPI.h"\
+	"..\..\..\..\mDNSCore\mDNSDebug.h"\
+	"..\..\..\CommonServices.h"\
+	"..\..\..\DebugServices.h"\
+	
+NODEP_CPP_DEBUG=\
+	"..\..\..\intLib.h"\
+	"..\..\..\logLib.h"\
+	"..\..\..\vxWorks.h"\
+	
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\DebugServices.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\..\mDNSCore\DNSCommon.c
+
+!IF  "$(CFG)" == "Application - Win32 (WCE emulator) Release"
+
+DEP_CPP_DNSCO=\
+	"..\..\..\..\mDNSCore\DNSCommon.h"\
+	"..\..\..\..\mDNSCore\mDNSClientAPI.h"\
+	"..\..\..\..\mDNSCore\mDNSDebug.h"\
+	
+
+!ELSEIF  "$(CFG)" == "Application - Win32 (WCE emulator) Debug"
+
+DEP_CPP_DNSCO=\
+	"..\..\..\..\mDNSCore\DNSCommon.h"\
+	"..\..\..\..\mDNSCore\mDNSClientAPI.h"\
+	"..\..\..\..\mDNSCore\mDNSDebug.h"\
+	
+
+!ELSEIF  "$(CFG)" == "Application - Win32 (WCE ARMV4) Release"
+
+DEP_CPP_DNSCO=\
+	"..\..\..\..\mDNSCore\DNSCommon.h"\
+	"..\..\..\..\mDNSCore\mDNSClientAPI.h"\
+	"..\..\..\..\mDNSCore\mDNSDebug.h"\
+	
+
+!ELSEIF  "$(CFG)" == "Application - Win32 (WCE ARMV4) Debug"
+
+DEP_CPP_DNSCO=\
+	"..\..\..\..\mDNSCore\DNSCommon.h"\
+	"..\..\..\..\mDNSCore\mDNSClientAPI.h"\
+	"..\..\..\..\mDNSCore\mDNSDebug.h"\
+	
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\..\mDNSCore\DNSCommon.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\..\mDNSCore\DNSDigest.c
+
+!IF  "$(CFG)" == "Application - Win32 (WCE emulator) Release"
+
+DEP_CPP_DNSDI=\
+	"..\..\..\..\mDNSCore\DNSCommon.h"\
+	"..\..\..\..\mDNSCore\mDNSClientAPI.h"\
+	"..\..\..\..\mDNSCore\mDNSDebug.h"\
+	
+
+!ELSEIF  "$(CFG)" == "Application - Win32 (WCE emulator) Debug"
+
+DEP_CPP_DNSDI=\
+	"..\..\..\..\mDNSCore\DNSCommon.h"\
+	"..\..\..\..\mDNSCore\mDNSClientAPI.h"\
+	"..\..\..\..\mDNSCore\mDNSDebug.h"\
+	
+
+!ELSEIF  "$(CFG)" == "Application - Win32 (WCE ARMV4) Release"
+
+DEP_CPP_DNSDI=\
+	"..\..\..\..\mDNSCore\DNSCommon.h"\
+	"..\..\..\..\mDNSCore\mDNSClientAPI.h"\
+	"..\..\..\..\mDNSCore\mDNSDebug.h"\
+	
+
+!ELSEIF  "$(CFG)" == "Application - Win32 (WCE ARMV4) Debug"
+
+DEP_CPP_DNSDI=\
+	"..\..\..\..\mDNSCore\DNSCommon.h"\
+	"..\..\..\..\mDNSCore\mDNSClientAPI.h"\
+	"..\..\..\..\mDNSCore\mDNSDebug.h"\
+	
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\DNSSD.c
+
+!IF  "$(CFG)" == "Application - Win32 (WCE emulator) Release"
+
+DEP_CPP_DNSSD=\
+	"..\..\..\CommonServices.h"\
+	"..\..\..\DebugServices.h"\
+	"..\..\..\DNSSD.h"\
+	"..\..\..\DNSSDDirect.h"\
+	"..\..\..\RMxClient.h"\
+	
+NODEP_CPP_DNSSD=\
+	"..\..\..\logLib.h"\
+	"..\..\..\vxWorks.h"\
+	
+
+!ELSEIF  "$(CFG)" == "Application - Win32 (WCE emulator) Debug"
+
+DEP_CPP_DNSSD=\
+	"..\..\..\CommonServices.h"\
+	"..\..\..\DebugServices.h"\
+	"..\..\..\DNSSD.h"\
+	"..\..\..\DNSSDDirect.h"\
+	"..\..\..\RMxClient.h"\
+	
+NODEP_CPP_DNSSD=\
+	"..\..\..\logLib.h"\
+	"..\..\..\vxWorks.h"\
+	
+
+!ELSEIF  "$(CFG)" == "Application - Win32 (WCE ARMV4) Release"
+
+DEP_CPP_DNSSD=\
+	"..\..\..\CommonServices.h"\
+	"..\..\..\DebugServices.h"\
+	"..\..\..\DNSSD.h"\
+	"..\..\..\DNSSDDirect.h"\
+	"..\..\..\RMxClient.h"\
+	
+NODEP_CPP_DNSSD=\
+	"..\..\..\logLib.h"\
+	"..\..\..\vxWorks.h"\
+	
+
+!ELSEIF  "$(CFG)" == "Application - Win32 (WCE ARMV4) Debug"
+
+DEP_CPP_DNSSD=\
+	"..\..\..\CommonServices.h"\
+	"..\..\..\DebugServices.h"\
+	"..\..\..\DNSSD.h"\
+	"..\..\..\DNSSDDirect.h"\
+	"..\..\..\RMxClient.h"\
+	
+NODEP_CPP_DNSSD=\
+	"..\..\..\logLib.h"\
+	"..\..\..\vxWorks.h"\
+	
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\DNSSD.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\DNSSDDirect.c
+
+!IF  "$(CFG)" == "Application - Win32 (WCE emulator) Release"
+
+DEP_CPP_DNSSDD=\
+	"..\..\..\..\mDNSCore\mDNSClientAPI.h"\
+	"..\..\..\..\mDNSCore\mDNSDebug.h"\
+	"..\..\..\CommonServices.h"\
+	"..\..\..\DebugServices.h"\
+	"..\..\..\DNSSD.h"\
+	"..\..\..\DNSSDDirect.h"\
+	
+NODEP_CPP_DNSSDD=\
+	"..\..\..\logLib.h"\
+	"..\..\..\vxWorks.h"\
+	
+
+!ELSEIF  "$(CFG)" == "Application - Win32 (WCE emulator) Debug"
+
+DEP_CPP_DNSSDD=\
+	"..\..\..\..\mDNSCore\mDNSClientAPI.h"\
+	"..\..\..\..\mDNSCore\mDNSDebug.h"\
+	"..\..\..\CommonServices.h"\
+	"..\..\..\DebugServices.h"\
+	"..\..\..\DNSSD.h"\
+	"..\..\..\DNSSDDirect.h"\
+	
+NODEP_CPP_DNSSDD=\
+	"..\..\..\logLib.h"\
+	"..\..\..\vxWorks.h"\
+	
+
+!ELSEIF  "$(CFG)" == "Application - Win32 (WCE ARMV4) Release"
+
+DEP_CPP_DNSSDD=\
+	"..\..\..\..\mDNSCore\mDNSClientAPI.h"\
+	"..\..\..\..\mDNSCore\mDNSDebug.h"\
+	"..\..\..\CommonServices.h"\
+	"..\..\..\DebugServices.h"\
+	"..\..\..\DNSSD.h"\
+	"..\..\..\DNSSDDirect.h"\
+	
+NODEP_CPP_DNSSDD=\
+	"..\..\..\logLib.h"\
+	"..\..\..\vxWorks.h"\
+	
+
+!ELSEIF  "$(CFG)" == "Application - Win32 (WCE ARMV4) Debug"
+
+DEP_CPP_DNSSDD=\
+	"..\..\..\..\mDNSCore\mDNSClientAPI.h"\
+	"..\..\..\..\mDNSCore\mDNSDebug.h"\
+	"..\..\..\CommonServices.h"\
+	"..\..\..\DebugServices.h"\
+	"..\..\..\DNSSD.h"\
+	"..\..\..\DNSSDDirect.h"\
+	
+NODEP_CPP_DNSSDD=\
+	"..\..\..\logLib.h"\
+	"..\..\..\vxWorks.h"\
+	
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\DNSSDDirect.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\DNSServices\DNSServices.c
+
+!IF  "$(CFG)" == "Application - Win32 (WCE emulator) Release"
+
+DEP_CPP_DNSSE=\
+	"..\..\..\..\mDNSCore\mDNSClientAPI.h"\
+	"..\..\..\..\mDNSCore\mDNSDebug.h"\
+	"..\..\..\DNSServices\DNSServices.h"\
+	
+
+!ELSEIF  "$(CFG)" == "Application - Win32 (WCE emulator) Debug"
+
+DEP_CPP_DNSSE=\
+	"..\..\..\..\mDNSCore\mDNSClientAPI.h"\
+	"..\..\..\..\mDNSCore\mDNSDebug.h"\
+	"..\..\..\DNSServices\DNSServices.h"\
+	
+
+!ELSEIF  "$(CFG)" == "Application - Win32 (WCE ARMV4) Release"
+
+DEP_CPP_DNSSE=\
+	"..\..\..\..\mDNSCore\mDNSClientAPI.h"\
+	"..\..\..\..\mDNSCore\mDNSDebug.h"\
+	"..\..\..\DNSServices\DNSServices.h"\
+	
+
+!ELSEIF  "$(CFG)" == "Application - Win32 (WCE ARMV4) Debug"
+
+DEP_CPP_DNSSE=\
+	"..\..\..\..\mDNSCore\mDNSClientAPI.h"\
+	"..\..\..\..\mDNSCore\mDNSDebug.h"\
+	"..\..\..\DNSServices\DNSServices.h"\
+	
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\DNSServices\DNSServices.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\..\mDNSCore\mDNS.c
+
+!IF  "$(CFG)" == "Application - Win32 (WCE emulator) Release"
+
+DEP_CPP_MDNS_=\
+	"..\..\..\..\mDNSCore\DNSCommon.h"\
+	"..\..\..\..\mDNSCore\mDNSClientAPI.h"\
+	"..\..\..\..\mDNSCore\mDNSDebug.h"\
+	"..\..\..\..\mDNSCore\uDNS.h"\
+	
+
+!ELSEIF  "$(CFG)" == "Application - Win32 (WCE emulator) Debug"
+
+DEP_CPP_MDNS_=\
+	"..\..\..\..\mDNSCore\DNSCommon.h"\
+	"..\..\..\..\mDNSCore\mDNSClientAPI.h"\
+	"..\..\..\..\mDNSCore\mDNSDebug.h"\
+	"..\..\..\..\mDNSCore\uDNS.h"\
+	
+
+!ELSEIF  "$(CFG)" == "Application - Win32 (WCE ARMV4) Release"
+
+DEP_CPP_MDNS_=\
+	"..\..\..\..\mDNSCore\DNSCommon.h"\
+	"..\..\..\..\mDNSCore\mDNSClientAPI.h"\
+	"..\..\..\..\mDNSCore\mDNSDebug.h"\
+	"..\..\..\..\mDNSCore\uDNS.h"\
+	
+
+!ELSEIF  "$(CFG)" == "Application - Win32 (WCE ARMV4) Debug"
+
+DEP_CPP_MDNS_=\
+	"..\..\..\..\mDNSCore\DNSCommon.h"\
+	"..\..\..\..\mDNSCore\mDNSClientAPI.h"\
+	"..\..\..\..\mDNSCore\mDNSDebug.h"\
+	"..\..\..\..\mDNSCore\uDNS.h"\
+	
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\..\mDNSCore\mDNSClientAPI.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\..\mDNSCore\mDNSDebug.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\mDNSWin32.c
+
+!IF  "$(CFG)" == "Application - Win32 (WCE emulator) Release"
+
+DEP_CPP_MDNSW=\
+	"..\..\..\..\mDNSCore\mDNSClientAPI.h"\
+	"..\..\..\..\mDNSCore\mDNSDebug.h"\
+	"..\..\..\CommonServices.h"\
+	"..\..\..\DebugServices.h"\
+	"..\..\..\mDNSWin32.h"\
+	{$(INCLUDE)}"ipexport.h"\
+	{$(INCLUDE)}"Iphlpapi.h"\
+	{$(INCLUDE)}"iptypes.h"\
+	
+NODEP_CPP_MDNSW=\
+	"..\..\..\logLib.h"\
+	"..\..\..\vxWorks.h"\
+	
+
+!ELSEIF  "$(CFG)" == "Application - Win32 (WCE emulator) Debug"
+
+DEP_CPP_MDNSW=\
+	"..\..\..\..\mDNSCore\mDNSClientAPI.h"\
+	"..\..\..\..\mDNSCore\mDNSDebug.h"\
+	"..\..\..\CommonServices.h"\
+	"..\..\..\DebugServices.h"\
+	"..\..\..\mDNSWin32.h"\
+	{$(INCLUDE)}"ipexport.h"\
+	{$(INCLUDE)}"Iphlpapi.h"\
+	{$(INCLUDE)}"iptypes.h"\
+	
+NODEP_CPP_MDNSW=\
+	"..\..\..\logLib.h"\
+	"..\..\..\vxWorks.h"\
+	
+# ADD CPP /W3
+
+!ELSEIF  "$(CFG)" == "Application - Win32 (WCE ARMV4) Release"
+
+DEP_CPP_MDNSW=\
+	"..\..\..\..\mDNSCore\mDNSClientAPI.h"\
+	"..\..\..\..\mDNSCore\mDNSDebug.h"\
+	"..\..\..\CommonServices.h"\
+	"..\..\..\DebugServices.h"\
+	"..\..\..\mDNSWin32.h"\
+	{$(INCLUDE)}"ipexport.h"\
+	{$(INCLUDE)}"Iphlpapi.h"\
+	{$(INCLUDE)}"iptypes.h"\
+	
+NODEP_CPP_MDNSW=\
+	"..\..\..\logLib.h"\
+	"..\..\..\vxWorks.h"\
+	
+
+!ELSEIF  "$(CFG)" == "Application - Win32 (WCE ARMV4) Debug"
+
+DEP_CPP_MDNSW=\
+	"..\..\..\..\mDNSCore\mDNSClientAPI.h"\
+	"..\..\..\..\mDNSCore\mDNSDebug.h"\
+	"..\..\..\CommonServices.h"\
+	"..\..\..\DebugServices.h"\
+	"..\..\..\mDNSWin32.h"\
+	{$(INCLUDE)}"ipexport.h"\
+	{$(INCLUDE)}"Iphlpapi.h"\
+	{$(INCLUDE)}"iptypes.h"\
+	
+NODEP_CPP_MDNSW=\
+	"..\..\..\logLib.h"\
+	"..\..\..\vxWorks.h"\
+	
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\mDNSWin32.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\..\mDNSCore\uDNS.c
+
+!IF  "$(CFG)" == "Application - Win32 (WCE emulator) Release"
+
+DEP_CPP_UDNS_=\
+	"..\..\..\..\mDNSCore\DNSCommon.h"\
+	"..\..\..\..\mDNSCore\mDNSClientAPI.h"\
+	"..\..\..\..\mDNSCore\mDNSDebug.h"\
+	"..\..\..\..\mDNSCore\uDNS.h"\
+	
+
+!ELSEIF  "$(CFG)" == "Application - Win32 (WCE emulator) Debug"
+
+DEP_CPP_UDNS_=\
+	"..\..\..\..\mDNSCore\DNSCommon.h"\
+	"..\..\..\..\mDNSCore\mDNSClientAPI.h"\
+	"..\..\..\..\mDNSCore\mDNSDebug.h"\
+	"..\..\..\..\mDNSCore\uDNS.h"\
+	
+
+!ELSEIF  "$(CFG)" == "Application - Win32 (WCE ARMV4) Release"
+
+DEP_CPP_UDNS_=\
+	"..\..\..\..\mDNSCore\DNSCommon.h"\
+	"..\..\..\..\mDNSCore\mDNSClientAPI.h"\
+	"..\..\..\..\mDNSCore\mDNSDebug.h"\
+	"..\..\..\..\mDNSCore\uDNS.h"\
+	
+
+!ELSEIF  "$(CFG)" == "Application - Win32 (WCE ARMV4) Debug"
+
+DEP_CPP_UDNS_=\
+	"..\..\..\..\mDNSCore\DNSCommon.h"\
+	"..\..\..\..\mDNSCore\mDNSClientAPI.h"\
+	"..\..\..\..\mDNSCore\mDNSDebug.h"\
+	"..\..\..\..\mDNSCore\uDNS.h"\
+	
+
+!ENDIF 
+
+# End Source File
+# Begin Source File
+
+SOURCE=..\..\..\..\mDNSCore\uDNS.h
+# End Source File
+# End Group
+# End Target
+# End Project
diff --git a/mdnsresponder/mDNSWindows/DNSServiceBrowser/WindowsCE/Application.vcw b/mdnsresponder/mDNSWindows/DNSServiceBrowser/WindowsCE/Application.vcw
new file mode 100644
index 0000000..11ef513
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DNSServiceBrowser/WindowsCE/Application.vcw
@@ -0,0 +1,29 @@
+Microsoft eMbedded Visual Tools Workspace File, Format Version 4.00
+# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
+
+###############################################################################
+
+Project: "Application"=.\Application.vcp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
+Global:
+
+Package=<5>
+{{{
+}}}
+
+Package=<3>
+{{{
+}}}
+
+###############################################################################
+
diff --git a/mdnsresponder/mDNSWindows/DNSServiceBrowser/WindowsCE/Resources/Application.ico b/mdnsresponder/mDNSWindows/DNSServiceBrowser/WindowsCE/Resources/Application.ico
new file mode 100644
index 0000000..50fb08f
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DNSServiceBrowser/WindowsCE/Resources/Application.ico
Binary files differ
diff --git a/mdnsresponder/mDNSWindows/DNSServiceBrowser/WindowsCE/Resources/Application.rc b/mdnsresponder/mDNSWindows/DNSServiceBrowser/WindowsCE/Resources/Application.rc
new file mode 100644
index 0000000..c153326
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DNSServiceBrowser/WindowsCE/Resources/Application.rc
@@ -0,0 +1,194 @@
+//Microsoft eMbedded Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+#include "newres.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE DISCARDABLE 
+BEGIN
+    "resource.h\0"
+END
+
+2 TEXTINCLUDE DISCARDABLE 
+BEGIN
+    "#include ""afxres.h""\r\n"
+    "#include ""newres.h""\r\n"
+    "\0"
+END
+
+3 TEXTINCLUDE DISCARDABLE 
+BEGIN
+    "#define _AFX_NO_SPLITTER_RESOURCES\r\n"
+    "#define _AFX_NO_OLE_RESOURCES\r\n"
+    "#define _AFX_NO_TRACKER_RESOURCES\r\n"
+    "#define _AFX_NO_PROPERTY_RESOURCES\r\n"
+    "\r\n"
+    "#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)\r\n"
+    "#ifdef _WIN32\r\n"
+    "LANGUAGE 9, 1\r\n"
+    "#pragma code_page(1252)\r\n"
+    "#endif //_WIN32\r\n"
+    "#include ""Resources\\Application.rc2""  // non-Microsoft eMbedded Visual C++ edited resources\r\n"
+    "#include ""afxres.rc""         // Standard components\r\n"
+    "#include ""wceres.rc""         // WCE-specific components\r\n"
+    "#endif\r\n"
+    "\0"
+END
+
+#endif    // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Icon
+//
+
+// Icon with lowest ID value placed first to ensure application icon
+// remains consistent on all systems.
+IDR_MAINFRAME           ICON    DISCARDABLE     "Application.ico"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Dialog
+//
+
+IDD_APPLICATION_DIALOG DIALOG DISCARDABLE  0, 0, 139, 153
+STYLE WS_POPUP | WS_VISIBLE | WS_CAPTION
+EXSTYLE WS_EX_APPWINDOW | 0x80000000L
+CAPTION "DNSServiceBrowser"
+FONT 8, "System"
+BEGIN
+    CONTROL         "List1",IDC_BROWSE_LIST,"SysListView32",LVS_REPORT | 
+                    LVS_SINGLESEL | LVS_SHOWSELALWAYS | LVS_NOCOLUMNHEADER | 
+                    WS_BORDER | WS_TABSTOP,7,7,125,141
+END
+
+
+#ifndef _MAC
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION 1,0,0,1
+ PRODUCTVERSION 1,0,0,1
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "040904b0"
+        BEGIN
+            VALUE "Comments", "\0"
+            VALUE "CompanyName", "Apple Computer, Inc.\0"
+            VALUE "FileDescription", "DNSServiceBrowser for Windows CE\0"
+            VALUE "FileVersion", "1, 0, 0, 1\0"
+            VALUE "InternalName", "Application\0"
+            VALUE "LegalCopyright", "Copyright (C) 2003-2004 Apple Computer, Inc.\0"
+            VALUE "LegalTrademarks", "\0"
+            VALUE "OriginalFilename", "Application.exe\0"
+            VALUE "PrivateBuild", "\0"
+            VALUE "ProductName", "DNSServiceBrowser for Windows CE\0"
+            VALUE "ProductVersion", "1, 0, 0, 1\0"
+            VALUE "SpecialBuild", "\0"
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x409, 1200
+    END
+END
+
+#endif    // !_MAC
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// DESIGNINFO
+//
+
+#ifdef APSTUDIO_INVOKED
+GUIDELINES DESIGNINFO DISCARDABLE 
+BEGIN
+    IDD_APPLICATION_DIALOG, DIALOG
+    BEGIN
+        LEFTMARGIN, 7
+        RIGHTMARGIN, 132
+        TOPMARGIN, 7
+        BOTTOMMARGIN, 146
+    END
+END
+#endif    // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE DISCARDABLE 
+BEGIN
+    IDP_SOCKETS_INIT_FAILED "Windows CE sockets initialization failed."
+    IDS_BROWSER_LIST_COLUMN_NAME "Name"
+END
+
+#endif    // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+#define _AFX_NO_SPLITTER_RESOURCES
+#define _AFX_NO_OLE_RESOURCES
+#define _AFX_NO_TRACKER_RESOURCES
+#define _AFX_NO_PROPERTY_RESOURCES
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE 9, 1
+#pragma code_page(1252)
+#endif //_WIN32
+#include "Resources\Application.rc2"  // non-Microsoft eMbedded Visual C++ edited resources
+#include "afxres.rc"         // Standard components
+#include "wceres.rc"         // WCE-specific components
+#endif
+
+/////////////////////////////////////////////////////////////////////////////
+#endif    // not APSTUDIO_INVOKED
+
diff --git a/mdnsresponder/mDNSWindows/DNSServiceBrowser/WindowsCE/Resources/Application.rc2 b/mdnsresponder/mDNSWindows/DNSServiceBrowser/WindowsCE/Resources/Application.rc2
new file mode 100644
index 0000000..29c9fe7
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DNSServiceBrowser/WindowsCE/Resources/Application.rc2
@@ -0,0 +1,13 @@
+//
+// APPLICATION.RC2 - resources Microsoft eMbedded Visual C++ does not edit directly
+//
+
+#ifdef APSTUDIO_INVOKED
+	#error this file is not editable by Microsoft eMbedded Visual C++
+#endif //APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+// Add manually edited resources here...
+
+/////////////////////////////////////////////////////////////////////////////
diff --git a/mdnsresponder/mDNSWindows/DNSServiceBrowser/WindowsCE/Resources/newres.h b/mdnsresponder/mDNSWindows/DNSServiceBrowser/WindowsCE/Resources/newres.h
new file mode 100644
index 0000000..31c3a43
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DNSServiceBrowser/WindowsCE/Resources/newres.h
@@ -0,0 +1,28 @@
+#ifndef __NEWRES_H__
+#define __NEWRES_H__
+
+#define  SHMENUBAR RCDATA
+#if !(defined(_WIN32_WCE_PSPC) && (_WIN32_WCE >= 300))
+	#undef HDS_HORZ  
+	#undef HDS_BUTTONS 
+	#undef HDS_HIDDEN 
+
+	#include <commctrl.h>
+	// for MenuBar
+	#define I_IMAGENONE		(-2)
+	#define NOMENU			0xFFFF
+	#define IDS_SHNEW		1
+	#define IDM_SHAREDNEW        10
+	#define IDM_SHAREDNEWDEFAULT 11
+
+	// for Tab Control
+	#define TCS_SCROLLOPPOSITE      0x0001   // assumes multiline tab
+	#define TCS_BOTTOM              0x0002
+	#define TCS_RIGHT               0x0002
+	#define TCS_VERTICAL            0x0080
+	#define TCS_MULTISELECT         0x0004  // allow multi-select in button mode
+	#define TCS_FLATBUTTONS         0x0008	
+#endif //_WIN32_WCE_PSPC
+
+
+#endif //__NEWRES_H__
diff --git a/mdnsresponder/mDNSWindows/DNSServiceBrowser/WindowsCE/Resources/resource.h b/mdnsresponder/mDNSWindows/DNSServiceBrowser/WindowsCE/Resources/resource.h
new file mode 100644
index 0000000..0337c56
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DNSServiceBrowser/WindowsCE/Resources/resource.h
@@ -0,0 +1,22 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft eMbedded Visual C++ generated include file.
+// Used by Application.rc
+//
+#define IDD_APPLICATION_DIALOG          102
+#define IDP_SOCKETS_INIT_FAILED         103
+#define IDS_BROWSER_LIST_COLUMN_NAME    104
+#define IDR_MAINFRAME                   128
+#define IDC_BROWSE_LIST                 1000
+#define IDC_IP_TEXT                     1003
+#define IDC_TXT_TEXT                    1004
+
+// Next default values for new objects
+// 
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE        129
+#define _APS_NEXT_COMMAND_VALUE         32771
+#define _APS_NEXT_CONTROL_VALUE         1005
+#define _APS_NEXT_SYMED_VALUE           101
+#endif
+#endif
diff --git a/mdnsresponder/mDNSWindows/DNSServiceBrowser/WindowsCE/Sources/Application.cpp b/mdnsresponder/mDNSWindows/DNSServiceBrowser/WindowsCE/Sources/Application.cpp
new file mode 100644
index 0000000..931cd95
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DNSServiceBrowser/WindowsCE/Sources/Application.cpp
@@ -0,0 +1,92 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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	"stdafx.h"
+
+#include	"DNSServices.h"
+
+#include	"BrowserDialog.h"
+
+#include	"Application.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+//===========================================================================================================================
+//	Message Map
+//===========================================================================================================================
+
+BEGIN_MESSAGE_MAP(Application, CWinApp)
+	//{{AFX_MSG_MAP(Application)
+		// NOTE - the ClassWizard will add and remove mapping macros here.
+		//    DO NOT EDIT what you see in these blocks of generated code!
+	//}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+//===========================================================================================================================
+//	Globals
+//===========================================================================================================================
+
+Application		gApp;
+
+//===========================================================================================================================
+//	Application
+//===========================================================================================================================
+
+Application::Application()
+	: CWinApp()
+{
+	//
+}
+
+//===========================================================================================================================
+//	InitInstance
+//===========================================================================================================================
+
+BOOL Application::InitInstance()
+{
+	DNSStatus			err;
+	BrowserDialog		dialog;
+	BOOL				dnsInitialized;
+	
+	dnsInitialized = FALSE;
+	
+	err = DNSServicesInitialize( kDNSFlagAdvertise, 0 );
+	if( err )
+	{
+		AfxMessageBox( IDP_SOCKETS_INIT_FAILED );
+		goto exit;
+	}
+	dnsInitialized = TRUE;
+
+	// Display the main browser dialog.
+	
+	m_pMainWnd = &dialog;
+	dialog.DoModal();
+
+	// Dialog has been closed. Return false to exit the app and not start the app's message pump.
+
+exit:
+	if( dnsInitialized )
+	{
+		DNSServicesFinalize();
+	}
+	return( FALSE );
+}
diff --git a/mdnsresponder/mDNSWindows/DNSServiceBrowser/WindowsCE/Sources/Application.h b/mdnsresponder/mDNSWindows/DNSServiceBrowser/WindowsCE/Sources/Application.h
new file mode 100644
index 0000000..cfd5429
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DNSServiceBrowser/WindowsCE/Sources/Application.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+#if !defined(AFX_APPLICATION_H__E2E51302_D643_458E_A7A5_5157233D1E5C__INCLUDED_)
+#define AFX_APPLICATION_H__E2E51302_D643_458E_A7A5_5157233D1E5C__INCLUDED_
+
+#if _MSC_VER >= 1000
+#pragma once
+#endif // _MSC_VER >= 1000
+
+#ifndef __AFXWIN_H__
+	#error include 'stdafx.h' before including this file for PCH
+#endif
+
+#include	"Resource.h"
+
+//===========================================================================================================================
+//	Application
+//===========================================================================================================================
+
+class	Application : public CWinApp
+{
+	public:
+		
+		Application();
+
+		// ClassWizard generated virtual function overrides
+		//{{AFX_VIRTUAL(Application)
+		public:
+		virtual BOOL InitInstance();
+		//}}AFX_VIRTUAL
+
+		//{{AFX_MSG(Application)
+			// NOTE - the ClassWizard will add and remove member functions here.
+			//    DO NOT EDIT what you see in these blocks of generated code !
+		//}}AFX_MSG
+		DECLARE_MESSAGE_MAP()
+};
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft eMbedded Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_APPLICATION_H__E2E51302_D643_458E_A7A5_5157233D1E5C__INCLUDED_)
diff --git a/mdnsresponder/mDNSWindows/DNSServiceBrowser/WindowsCE/Sources/BrowserDialog.cpp b/mdnsresponder/mDNSWindows/DNSServiceBrowser/WindowsCE/Sources/BrowserDialog.cpp
new file mode 100644
index 0000000..92cdeb3
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DNSServiceBrowser/WindowsCE/Sources/BrowserDialog.cpp
@@ -0,0 +1,394 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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	"stdafx.h"
+
+#include	"Application.h"
+
+#include	"DNSServices.h"
+
+#include	"BrowserDialog.h"
+
+#ifdef _DEBUG
+#define new DEBUG_NEW
+#undef THIS_FILE
+static char THIS_FILE[] = __FILE__;
+#endif
+
+//===========================================================================================================================
+//	Constants
+//===========================================================================================================================
+
+#define	WM_USER_SERVICE_ADD			( WM_USER + 0x100 )
+#define	WM_USER_SERVICE_REMOVE		( WM_USER + 0x101 )
+
+//===========================================================================================================================
+//	Message Map
+//===========================================================================================================================
+
+BEGIN_MESSAGE_MAP(BrowserDialog, CDialog)
+	//{{AFX_MSG_MAP(BrowserDialog)
+	ON_NOTIFY(NM_CLICK, IDC_BROWSE_LIST, OnBrowserListDoubleClick)
+	ON_MESSAGE( WM_USER_SERVICE_ADD, OnServiceAdd )
+	ON_MESSAGE( WM_USER_SERVICE_REMOVE, OnServiceRemove )
+	//}}AFX_MSG_MAP
+END_MESSAGE_MAP()
+
+static DWORD	UTF8StringToStringObject( const char *inUTF8, CString &inObject );
+
+//===========================================================================================================================
+//	BrowserDialog
+//===========================================================================================================================
+
+BrowserDialog::BrowserDialog( CWnd *inParent )
+	: CDialog( BrowserDialog::IDD, inParent )
+{
+	//{{AFX_DATA_INIT(BrowserDialog)
+		// Note: the ClassWizard will add member initialization here
+	//}}AFX_DATA_INIT
+	
+	// Note that LoadIcon does not require a subsequent DestroyIcon in Win32.
+	
+	mIcon = AfxGetApp()->LoadIcon( IDR_MAINFRAME );
+	ASSERT( mIcon );
+}
+
+//===========================================================================================================================
+//	DoDataExchange
+//===========================================================================================================================
+
+void	BrowserDialog::DoDataExchange( CDataExchange *pDX )
+{
+	CDialog::DoDataExchange(pDX);
+	//{{AFX_DATA_MAP(BrowserDialog)
+	DDX_Control(pDX, IDC_BROWSE_LIST, mBrowserList);
+	//}}AFX_DATA_MAP
+}
+
+//===========================================================================================================================
+//	OnInitDialog
+//===========================================================================================================================
+
+BOOL	BrowserDialog::OnInitDialog()
+{
+	CString		s;
+	
+	CDialog::OnInitDialog();
+
+	// Set the icon for this dialog. The framework does this automatically when the application's main window is not a dialog.
+	
+	SetIcon( mIcon, TRUE );		// Set big icon
+	SetIcon( mIcon, FALSE );	// Set small icon
+	
+	CenterWindow( GetDesktopWindow() );
+
+	// Set up the list.
+	
+	CRect		rect;
+	
+	s.LoadString( IDS_BROWSER_LIST_COLUMN_NAME );
+	mBrowserList.GetWindowRect( rect );
+	mBrowserList.InsertColumn( 0, s, LVCFMT_LEFT, rect.Width() - 8 );
+	
+	// Start browsing for services.
+
+	DNSStatus		err;
+
+	err = DNSBrowserCreate( 0, OnBrowserCallBack, this, &mBrowser );
+	if( err )
+	{
+		AfxMessageBox( IDP_SOCKETS_INIT_FAILED );
+		goto exit;
+	}
+	
+	err = DNSBrowserStartServiceSearch( mBrowser, kDNSBrowserFlagAutoResolve, "_http._tcp", NULL );
+	if( err )
+	{
+		AfxMessageBox( IDP_SOCKETS_INIT_FAILED );
+		goto exit;
+	}
+	
+exit:
+	return( TRUE );
+}
+
+
+//===========================================================================================================================
+//	OnBrowserListDoubleClick
+//===========================================================================================================================
+
+void	BrowserDialog::OnBrowserListDoubleClick( NMHDR *pNMHDR, LRESULT *pResult ) 
+{
+	int		selectedItem;
+
+	(void) pNMHDR;	// Unused
+
+	selectedItem = mBrowserList.GetNextItem( -1, LVNI_SELECTED );
+	if( selectedItem >= 0 )
+	{
+		BrowserEntry *		entry;
+		CString				temp;
+		CString				url;
+		
+		// Build the URL from the IP and optional TXT record.
+
+		entry = &mBrowserEntries[ selectedItem ];
+		url += "http://" + entry->ip;
+		temp = entry->text;
+		if( temp.Find( TEXT( "path=" ) ) == 0 )
+		{
+			temp.Delete( 0, 5 );
+		}
+		if( temp.Find( '/' ) != 0 )
+		{
+			url += '/';
+		}
+		url += temp;
+
+		// Let the system open the URL in the correct app.
+		
+		SHELLEXECUTEINFO		info;
+
+		info.cbSize			= sizeof( info );
+		info.fMask 			= 0;
+		info.hwnd 			= NULL;
+		info.lpVerb 		= NULL;
+		info.lpFile 		= url;
+		info.lpParameters 	= NULL;
+		info.lpDirectory 	= NULL;
+		info.nShow 			= SW_SHOWNORMAL;
+		info.hInstApp 		= NULL;
+
+		ShellExecuteEx( &info );
+	}
+	*pResult = 0;
+}
+
+//===========================================================================================================================
+//	OnBrowserCallBack [static]
+//===========================================================================================================================
+
+void
+	BrowserDialog::OnBrowserCallBack( 
+		void *					inContext, 
+		DNSBrowserRef			inRef, 
+		DNSStatus				inStatusCode,
+		const DNSBrowserEvent *	inEvent )
+{
+	BrowserDialog *		dialog;
+	BrowserEntry *		entry;
+	BOOL				posted;
+	
+	DNS_UNUSED( inStatusCode );
+	dialog = reinterpret_cast < BrowserDialog * > ( inContext );
+	ASSERT( dialog );
+	
+	switch( inEvent->type )
+	{
+		case kDNSBrowserEventTypeResolved:
+			if( inEvent->data.resolved->address.addressType == kDNSNetworkAddressTypeIPv4  )
+			{
+				char		ip[ 64 ];
+
+				sprintf( ip, "%u.%u.%u.%u:%u", 
+					inEvent->data.resolved->address.u.ipv4.addr.v8[ 0 ], 
+					inEvent->data.resolved->address.u.ipv4.addr.v8[ 1 ], 
+					inEvent->data.resolved->address.u.ipv4.addr.v8[ 2 ], 
+					inEvent->data.resolved->address.u.ipv4.addr.v8[ 3 ], 
+					( inEvent->data.resolved->address.u.ipv4.port.v8[ 0 ] << 8 ) | 
+					  inEvent->data.resolved->address.u.ipv4.port.v8[ 1 ] );
+				
+				entry = new BrowserEntry;
+				ASSERT( entry );
+				if( entry )
+				{
+					UTF8StringToStringObject( inEvent->data.resolved->name, entry->name );
+					UTF8StringToStringObject( ip, entry->ip );
+					UTF8StringToStringObject( inEvent->data.resolved->textRecord, entry->text );
+					
+					posted = ::PostMessage( dialog->GetSafeHwnd(), WM_USER_SERVICE_ADD, 0, (LPARAM) entry );
+					ASSERT( posted );
+					if( !posted )
+					{
+						delete entry;
+					}
+				}
+			}
+			break;
+
+		case kDNSBrowserEventTypeRemoveService:
+			entry = new BrowserEntry;
+			ASSERT( entry );
+			if( entry )
+			{
+				UTF8StringToStringObject( inEvent->data.removeService.name, entry->name );
+				
+				posted = ::PostMessage( dialog->GetSafeHwnd(), WM_USER_SERVICE_REMOVE, 0, (LPARAM) entry );
+				ASSERT( posted );
+				if( !posted )
+				{
+					delete entry;
+				}
+			}
+			break;
+		
+		default:
+			break;
+	}
+}
+
+//===========================================================================================================================
+//	BrowserAddService
+//===========================================================================================================================
+
+LONG	BrowserDialog::OnServiceAdd( WPARAM inWParam, LPARAM inLParam )
+{
+	BrowserEntry *		entry;
+	INT_PTR				lo;
+	INT_PTR				hi;
+	INT_PTR				mid;
+	int					result;
+	
+	(void) inWParam;	// Unused
+	
+	entry = reinterpret_cast < BrowserEntry * > ( inLParam );
+	ASSERT( entry );
+	
+	result 	= -1;
+	mid		= 0;
+	lo 		= 0;
+	hi 		= mBrowserEntries.GetSize() - 1;
+	while( lo <= hi )
+	{
+		mid = ( lo + hi ) / 2;
+		result = entry->name.CompareNoCase( mBrowserEntries[ mid ].name );
+		if( result == 0 )
+		{
+			break;
+		}
+		else if( result < 0 )
+		{
+			hi = mid - 1;
+		}
+		else
+		{
+			lo = mid + 1;
+		}
+	}
+	if( result == 0 )
+	{
+		mBrowserEntries[ mid ].ip	= entry->ip;
+		mBrowserEntries[ mid ].text	= entry->text;
+	}
+	else
+	{
+		if( result > 0 )
+		{
+			mid += 1;
+		}
+		mBrowserEntries.InsertAt( mid, *entry );
+		mBrowserList.InsertItem( mid, entry->name );
+	}
+	delete entry;
+	return( 0 );
+}
+
+//===========================================================================================================================
+//	OnServiceRemove
+//===========================================================================================================================
+
+LONG	BrowserDialog::OnServiceRemove( WPARAM inWParam, LPARAM inLParam )
+{
+	BrowserEntry *		entry;
+	INT_PTR				hi;
+	INT_PTR				lo;
+	INT_PTR				mid;
+	int					result;
+
+	(void) inWParam;	// Unused
+	
+	entry = reinterpret_cast < BrowserEntry * > ( inLParam );
+	ASSERT( entry );
+	
+	result 	= -1;
+	mid		= 0;
+	lo 		= 0;
+	hi 		= mBrowserEntries.GetSize() - 1;
+	while( lo <= hi )
+	{
+		mid = ( lo + hi ) / 2;
+		result = entry->name.CompareNoCase( mBrowserEntries[ mid ].name );
+		if( result == 0 )
+		{
+			break;
+		}
+		else if( result < 0 )
+		{
+			hi = mid - 1;
+		}
+		else
+		{
+			lo = mid + 1;
+		}
+	}
+	if( result == 0 )
+	{
+		mBrowserList.DeleteItem( mid );
+		mBrowserEntries.RemoveAt( mid );
+	}
+	delete entry;
+	return( 0 );
+}
+
+#if 0
+#pragma mark -
+#endif
+
+//===========================================================================================================================
+//	UTF8StringToStringObject
+//===========================================================================================================================
+
+static DWORD	UTF8StringToStringObject( const char *inUTF8, CString &inObject )
+{
+	DWORD			err;
+	int				n;
+	wchar_t *		unicode;
+	
+	unicode = NULL;
+	
+	n = MultiByteToWideChar( CP_UTF8, 0, inUTF8, -1, NULL, 0 );
+	if( n > 0 )
+	{
+		unicode = (wchar_t *) malloc( (size_t)( n * sizeof( wchar_t ) ) );
+		if( !unicode ) { err = ERROR_INSUFFICIENT_BUFFER; goto exit; };
+		
+		n = MultiByteToWideChar( CP_UTF8, 0, inUTF8, -1, unicode, n );
+		inObject = unicode;
+	}
+	else
+	{
+		inObject = "";
+	}
+	err = 0;
+	
+exit:
+	if( unicode )
+	{
+		free( unicode );
+	}
+	return( err );
+}
diff --git a/mdnsresponder/mDNSWindows/DNSServiceBrowser/WindowsCE/Sources/BrowserDialog.h b/mdnsresponder/mDNSWindows/DNSServiceBrowser/WindowsCE/Sources/BrowserDialog.h
new file mode 100644
index 0000000..a27df91
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DNSServiceBrowser/WindowsCE/Sources/BrowserDialog.h
@@ -0,0 +1,84 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+#if !defined(AFX_BROWSERDIALOG_H__DECC5C82_C1C6_4630_B8D5_E1DDE570A061__INCLUDED_)
+#define AFX_BROWSERDIALOG_H__DECC5C82_C1C6_4630_B8D5_E1DDE570A061__INCLUDED_
+
+#if _MSC_VER >= 1000
+#pragma once
+#endif // _MSC_VER >= 1000
+
+#include	"afxtempl.h"
+#include	"Resource.h"
+
+#include	"DNSServices.h"
+
+//===========================================================================================================================
+//	BrowserDialog
+//===========================================================================================================================
+
+class	BrowserDialog : public CDialog
+{
+	public:
+		
+		BrowserDialog( CWnd *inParent = NULL );
+		
+		//{{AFX_DATA(BrowserDialog)
+		enum { IDD = IDD_APPLICATION_DIALOG };
+		CListCtrl	mBrowserList;
+		//}}AFX_DATA
+
+		// ClassWizard generated virtual function overrides
+		//{{AFX_VIRTUAL(BrowserDialog)
+		protected:
+		virtual void DoDataExchange(CDataExchange* pDX);	// DDX/DDV support
+		//}}AFX_VIRTUAL
+		
+		static void
+			OnBrowserCallBack( 
+				void *					inContext, 
+				DNSBrowserRef			inRef, 
+				DNSStatus				inStatusCode,  
+				const DNSBrowserEvent *	inEvent );
+		
+	protected:
+		
+		struct	BrowserEntry
+		{
+			CString		name;
+			CString		ip;
+			CString		text;
+		};
+		
+		HICON										mIcon;
+		DNSBrowserRef								mBrowser;
+		CArray < BrowserEntry, BrowserEntry >		mBrowserEntries;
+		
+		// Generated message map functions
+		//{{AFX_MSG(BrowserDialog)
+		virtual BOOL OnInitDialog();
+		afx_msg void OnBrowserListDoubleClick(NMHDR* pNMHDR, LRESULT* pResult);
+		afx_msg LONG OnServiceAdd( WPARAM inWParam, LPARAM inLParam );
+		afx_msg LONG OnServiceRemove( WPARAM inWParam, LPARAM inLParam );
+		//}}AFX_MSG
+		DECLARE_MESSAGE_MAP()
+};
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft eMbedded Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_BROWSERDIALOG_H__DECC5C82_C1C6_4630_B8D5_E1DDE570A061__INCLUDED_)
diff --git a/mdnsresponder/mDNSWindows/DNSServiceBrowser/WindowsCE/Sources/StdAfx.cpp b/mdnsresponder/mDNSWindows/DNSServiceBrowser/WindowsCE/Sources/StdAfx.cpp
new file mode 100644
index 0000000..ae2ca2e
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DNSServiceBrowser/WindowsCE/Sources/StdAfx.cpp
@@ -0,0 +1,18 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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	"stdafx.h"
diff --git a/mdnsresponder/mDNSWindows/DNSServiceBrowser/WindowsCE/Sources/StdAfx.h b/mdnsresponder/mDNSWindows/DNSServiceBrowser/WindowsCE/Sources/StdAfx.h
new file mode 100644
index 0000000..4b14a0b
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/DNSServiceBrowser/WindowsCE/Sources/StdAfx.h
@@ -0,0 +1,46 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+#if !defined(AFX_STDAFX_H__7F91E52B_CF39_429D_837D_599CE0B2B3D6__INCLUDED_)
+#define AFX_STDAFX_H__7F91E52B_CF39_429D_837D_599CE0B2B3D6__INCLUDED_
+
+#if _MSC_VER >= 1000
+#pragma once
+#endif // _MSC_VER >= 1000
+
+
+
+#define VC_EXTRALEAN		// Exclude rarely-used stuff from Windows headers
+
+#include <afxwin.h>         // MFC core and standard components
+#include <afxext.h>         // MFC extensions
+
+#if defined(_AFXDLL)
+#include <afxdtctl.h>		// MFC support for Internet Explorer 4 Common Controls
+#endif
+
+#ifndef _AFX_NO_AFXCMN_SUPPORT
+#include <afxcmn.h>			// MFC support for Windows Common Controls
+#endif // _AFX_NO_AFXCMN_SUPPORT
+
+#include <winsock2.h>
+//#include <afxsock.h>		// MFC socket extensions
+
+//{{AFX_INSERT_LOCATION}}
+// Microsoft eMbedded Visual C++ will insert additional declarations immediately before the previous line.
+
+#endif // !defined(AFX_STDAFX_H__7F91E52B_CF39_429D_837D_599CE0B2B3D6__INCLUDED_)
diff --git a/mdnsresponder/mDNSWindows/Java/Java.vcproj b/mdnsresponder/mDNSWindows/Java/Java.vcproj
new file mode 100755
index 0000000..1707ac5
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/Java/Java.vcproj
@@ -0,0 +1,111 @@
+<?xml version="1.0" encoding="Windows-1252"?>

+<VisualStudioProject

+	ProjectType="Visual C++"

+	Version="8.00"

+	Name="Java"

+	ProjectGUID="{9CE2568A-3170-41C6-9F20-A0188A9EC114}"

+	Keyword="MakeFileProj"

+	>

+	<Platforms>

+		<Platform

+			Name="Win32"

+		/>

+		<Platform

+			Name="x64"

+		/>

+	</Platforms>

+	<ToolFiles>

+	</ToolFiles>

+	<Configurations>

+		<Configuration

+			Name="Debug|Win32"

+			OutputDirectory="Debug"

+			IntermediateDirectory="Debug"

+			ConfigurationType="0"

+			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"

+			>

+			<Tool

+				Name="VCNMakeTool"

+				BuildCommandLine="nmake /f makefile DEBUG=1"

+				ReBuildCommandLine="nmake /f makefile DEBUG=1"

+				CleanCommandLine="nmake /f makefile DEBUG=1 clean"

+				Output=""

+				PreprocessorDefinitions=""

+				IncludeSearchPath=""

+				ForcedIncludes=""

+				AssemblySearchPath=""

+				ForcedUsingAssemblies=""

+				CompileAsManaged=""

+			/>

+		</Configuration>

+		<Configuration

+			Name="Debug|x64"

+			OutputDirectory="$(PlatformName)\$(ConfigurationName)"

+			IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"

+			ConfigurationType="0"

+			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"

+			>

+			<Tool

+				Name="VCNMakeTool"

+				BuildCommandLine="nmake /f makefile64 DEBUG=1"

+				ReBuildCommandLine="nmake /f makefile64 DEBUG=1"

+				CleanCommandLine="nmake /f makefile64 DEBUG=1 clean"

+				Output=""

+				PreprocessorDefinitions=""

+				IncludeSearchPath=""

+				ForcedIncludes=""

+				AssemblySearchPath=""

+				ForcedUsingAssemblies=""

+				CompileAsManaged=""

+			/>

+		</Configuration>

+		<Configuration

+			Name="Release|Win32"

+			OutputDirectory="Release"

+			IntermediateDirectory="Release"

+			ConfigurationType="0"

+			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"

+			>

+			<Tool

+				Name="VCNMakeTool"

+				BuildCommandLine="nmake /f makefile"

+				ReBuildCommandLine="nmake /f makefile"

+				CleanCommandLine="nmake /f makefile clean"

+				Output=""

+				PreprocessorDefinitions=""

+				IncludeSearchPath=""

+				ForcedIncludes=""

+				AssemblySearchPath=""

+				ForcedUsingAssemblies=""

+				CompileAsManaged=""

+			/>

+		</Configuration>

+		<Configuration

+			Name="Release|x64"

+			OutputDirectory="$(PlatformName)\$(ConfigurationName)"

+			IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"

+			ConfigurationType="0"

+			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"

+			>

+			<Tool

+				Name="VCNMakeTool"

+				BuildCommandLine="nmake /f makefile64"

+				ReBuildCommandLine="nmake /f makefile64"

+				CleanCommandLine="nmake /f makefile64 clean"

+				Output=""

+				PreprocessorDefinitions=""

+				IncludeSearchPath=""

+				ForcedIncludes=""

+				AssemblySearchPath=""

+				ForcedUsingAssemblies=""

+				CompileAsManaged=""

+			/>

+		</Configuration>

+	</Configurations>

+	<References>

+	</References>

+	<Files>

+	</Files>

+	<Globals>

+	</Globals>

+</VisualStudioProject>

diff --git a/mdnsresponder/mDNSWindows/Java/jdns_sd.rc b/mdnsresponder/mDNSWindows/Java/jdns_sd.rc
new file mode 100644
index 0000000..79fdf14
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/Java/jdns_sd.rc
@@ -0,0 +1,62 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+#ifndef JDNS_SD_RC
+#define JDNS_SD_RC
+
+#include "afxres.h"
+#include "../WinVersRes.h"
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION MASTER_PROD_VERS
+ PRODUCTVERSION MASTER_PROD_VERS
+ FILEFLAGSMASK 0x17L
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "040904b0"
+        BEGIN
+            VALUE "CompanyName", MASTER_COMPANY_NAME
+            VALUE "FileDescription", MASTER_PROD_NAME " support for Java"
+            VALUE "FileVersion", MASTER_PROD_VERS_STR
+            VALUE "InternalName", "jdns_sd"
+            VALUE "LegalCopyright", MASTER_LEGAL_COPYRIGHT
+            VALUE "OriginalFilename", "jdns_sd.dll"
+            VALUE "ProductName", MASTER_PROD_NAME
+            VALUE "ProductVersion", MASTER_PROD_VERS_STR
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x409, 1200
+    END
+END
+
+#endif // JDNS_SD_RC
diff --git a/mdnsresponder/mDNSWindows/Java/makefile b/mdnsresponder/mDNSWindows/Java/makefile
new file mode 100644
index 0000000..2e4b6bd
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/Java/makefile
@@ -0,0 +1,143 @@
+# -*- tab-width: 4 -*-
+#
+# Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+#
+# Licensed 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.
+#
+# This Makefile builds a .jar file and accompanying JNI support library
+# containing the DNSSD implementation for Java and support classes.
+#
+# Prior to building Java support, you must build DNSSD.dll.
+#
+# nmake with no arguments builds all production targets.
+# 'nmake DEBUG=1' to build debugging targets.
+# 'nmake clean' or 'nmake clean DEBUG=1' to delete prod/debug objects & targets
+#
+# To run nmake, you may need to set up your PATH correctly, using a script 
+# such as: "\Program Files\Microsoft Visual Studio .NET\Vc7\bin\vcvars32.bat"
+# 
+# The default location of the JDK is \javasdk. You can override this on the
+# command line (e.g. 'nmake JDK=\j2dk1.4.2_03').
+
+############################################################################
+
+COREDIR = ..\..\mDNSCore
+SHAREDDIR = ..\..\mDNSShared
+
+JDK = $(JAVA_HOME)
+
+CC = cl
+RC = rc
+LD = ld
+CP = copy
+RM = del /Q
+RMDIR = rmdir /S /Q
+JAVAC = $(JDK)\bin\javac
+JAVAH = $(JDK)\bin\javah
+JAR = $(JDK)\bin\jar
+CFLAGS_COMMON = -LD -DAUTO_CALLBACKS=0 -I. -I..\.. \
+	-I$(COREDIR) -I$(SHAREDDIR) -I$(JDK)\include -I$(JDK)\include\win32
+
+# Set up diverging paths for debug vs. prod builds
+DEBUG=0
+!if $(DEBUG) == 1
+CFLAGS_DEBUG = -Zi -DMDNS_DEBUGMSGS=2 
+OBJDIR = objects\debug
+BUILDDIR = build\debug
+INSTALLDIR = root\"Program Files"\Bonjour
+LIBDIR = ..\DLL\Win32\Debug
+!else
+CFLAGS_DEBUG = -Os -DMDNS_DEBUGMSGS=0 
+OBJDIR = objects\prod
+BUILDDIR = build\prod
+INSTALLDIR = root\"Program Files"\Bonjour
+LIBDIR = ..\DLL\Win32\Release
+!endif
+
+CFLAGS = $(CFLAGS_COMMON) $(CFLAGS_DEBUG)
+JAVACFLAGS = $(CFLAGS) $(JAVACFLAGS_OS)
+
+#############################################################################
+
+all: setup Java postbuild
+
+# 'setup' sets up the build directory structure the way we want
+setup:
+	@if not exist objects		mkdir objects
+	@if not exist build			mkdir build
+	@if not exist $(OBJDIR)		mkdir $(OBJDIR)
+	@if not exist $(BUILDDIR)	mkdir $(BUILDDIR)
+
+postbuild:
+	@if not "%RC_XBS%"=="YES" GOTO CONT
+	@if not exist "$(DSTROOT)\WINDOWS\system32\Win32"	mkdir "$(DSTROOT)\WINDOWS\system32\Win32"
+	@if not exist "$(DSTROOT)\Program Files\Bonjour\Win32"	mkdir "$(DSTROOT)\Program Files\Bonjour\Win32"
+	@copy $(BUILDDIR)\jdns_sd.dll "$(DSTROOT)\WINDOWS\system32\Win32"
+	@copy $(BUILDDIR)\dns_sd.jar  "$(DSTROOT)\Program Files\Bonjour\Win32"
+	@:CONT
+	@if not exist root					mkdir root
+	@if not exist root\"Program Files"	mkdir root\"Program Files"
+	@if not exist $(INSTALLDIR)			mkdir $(INSTALLDIR)
+	copy $(BUILDDIR)\dns_sd.jar $(INSTALLDIR)
+	copy $(BUILDDIR)\jdns_sd.dll $(INSTALLDIR)
+
+# clean removes targets and objects
+clean:
+	@if exist $(OBJDIR)		$(RMDIR) $(OBJDIR)
+	@if exist $(BUILDDIR)	$(RMDIR) $(BUILDDIR)
+
+#############################################################################
+
+# The following targets build Java wrappers for the dns-sd.h API.
+
+Java: setup $(BUILDDIR)\dns_sd.jar $(BUILDDIR)\jdns_sd.dll postbuild
+	@echo "Java wrappers done"
+
+JAVASRC	= $(SHAREDDIR)\Java
+JARCONTENTS =	$(OBJDIR)\com\apple\dnssd\DNSSDService.class \
+				$(OBJDIR)\com\apple\dnssd\DNSSDException.class \
+				$(OBJDIR)\com\apple\dnssd\DNSRecord.class \
+				$(OBJDIR)\com\apple\dnssd\TXTRecord.class \
+				$(OBJDIR)\com\apple\dnssd\DNSSDRegistration.class \
+				$(OBJDIR)\com\apple\dnssd\DNSSDRecordRegistrar.class \
+				$(OBJDIR)\com\apple\dnssd\BaseListener.class \
+				$(OBJDIR)\com\apple\dnssd\BrowseListener.class \
+				$(OBJDIR)\com\apple\dnssd\ResolveListener.class \
+				$(OBJDIR)\com\apple\dnssd\RegisterListener.class \
+				$(OBJDIR)\com\apple\dnssd\RegisterRecordListener.class \
+				$(OBJDIR)\com\apple\dnssd\QueryListener.class \
+				$(OBJDIR)\com\apple\dnssd\DomainListener.class \
+				$(OBJDIR)\com\apple\dnssd\DNSSD.class
+
+$(BUILDDIR)\dns_sd.jar: $(JARCONTENTS)
+	$(JAR) -cf $@ -C $(OBJDIR) com
+
+$(BUILDDIR)\jdns_sd.dll: $(JAVASRC)\JNISupport.c $(OBJDIR)\DNSSD.java.h $(OBJDIR)\jdns_sd.RES
+	$(CC) -Fe$@ $(JAVASRC)\JNISupport.c $(CFLAGS) -I$(OBJDIR) \
+	$(LIBDIR)\DNSSD.lib $(JDK)\lib\jvm.lib ws2_32.lib iphlpapi.lib $(OBJDIR)\jdns_sd.RES /link /NXCOMPAT /DYNAMICBASE /SAFESEH
+
+.SUFFIXES : .java
+{$(JAVASRC)}.java{$(OBJDIR)\com\apple\dnssd}.class:	
+	$(JAVAC) -d $(OBJDIR) -classpath $(OBJDIR) $<
+
+$(OBJDIR)\DNSSD.java.h: $(OBJDIR)\com\apple\dnssd\DNSSD.class
+	$(JAVAH) -classpath $(OBJDIR) -o $@ \
+		com.apple.dnssd.AppleBrowser \
+		com.apple.dnssd.AppleResolver \
+		com.apple.dnssd.AppleRegistration \
+		com.apple.dnssd.AppleQuery \
+		com.apple.dnssd.AppleService 
+
+$(OBJDIR)\jdns_sd.RES: jdns_sd.rc
+	$(RC) /fo $@ $?
+
diff --git a/mdnsresponder/mDNSWindows/Java/makefile64 b/mdnsresponder/mDNSWindows/Java/makefile64
new file mode 100644
index 0000000..fb0ff9c
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/Java/makefile64
@@ -0,0 +1,143 @@
+# -*- tab-width: 4 -*-
+#
+# Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+#
+# Licensed 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.
+#
+# This Makefile builds a .jar file and accompanying JNI support library
+# containing the DNSSD implementation for Java and support classes.
+#
+# Prior to building Java support, you must build DNSSD.dll.
+#
+# nmake with no arguments builds all production targets.
+# 'nmake DEBUG=1' to build debugging targets.
+# 'nmake clean' or 'nmake clean DEBUG=1' to delete prod/debug objects & targets
+#
+# To run nmake, you may need to set up your PATH correctly, using a script 
+# such as: "\Program Files\Microsoft Visual Studio .NET\Vc7\bin\vcvars32.bat"
+# 
+# The default location of the JDK is \javasdk. You can override this on the
+# command line (e.g. 'nmake JDK=\j2dk1.4.2_03').
+
+############################################################################
+
+COREDIR = ..\..\mDNSCore
+SHAREDDIR = ..\..\mDNSShared
+
+JDK = $(JAVA_HOME)
+
+CC = cl
+RC = rc
+LD = ld
+CP = copy
+RM = del /Q
+RMDIR = rmdir /S /Q
+JAVAC = $(JDK)\bin\javac
+JAVAH = $(JDK)\bin\javah
+JAR = $(JDK)\bin\jar
+CFLAGS_COMMON = -LD -DAUTO_CALLBACKS=0 -I. -I..\.. \
+	-I$(COREDIR) -I$(SHAREDDIR) -I$(JDK)\include -I$(JDK)\include\win32
+
+# Set up diverging paths for debug vs. prod builds
+DEBUG=0
+!if $(DEBUG) == 1
+CFLAGS_DEBUG = -Zi -DMDNS_DEBUGMSGS=2 
+OBJDIR = objects\debug\x64
+BUILDDIR = build\debug\x64
+INSTALLDIR = root\"Program Files"\Bonjour
+LIBDIR = ..\DLL\x64\Debug
+!else
+CFLAGS_DEBUG = -Os -DMDNS_DEBUGMSGS=0 
+OBJDIR = objects\prod\x64
+BUILDDIR = build\prod\x64
+INSTALLDIR = root\"Program Files"\Bonjour
+LIBDIR = ..\DLL\x64\Release
+!endif
+
+CFLAGS = $(CFLAGS_COMMON) $(CFLAGS_DEBUG)
+JAVACFLAGS = $(CFLAGS) $(JAVACFLAGS_OS)
+
+#############################################################################
+
+all: setup Java postbuild
+
+# 'setup' sets up the build directory structure the way we want
+setup:
+	@if not exist objects		mkdir objects
+	@if not exist build			mkdir build
+	@if not exist $(OBJDIR)		mkdir $(OBJDIR)
+	@if not exist $(BUILDDIR)	mkdir $(BUILDDIR)
+
+postbuild:
+	@if not "%RC_XBS%"=="YES" GOTO CONT
+	@if not exist "$(DSTROOT)\WINDOWS\system32\x64"	mkdir "$(DSTROOT)\WINDOWS\system32\x64"
+	@if not exist "$(DSTROOT)\Program Files\Bonjour\x64"	mkdir "$(DSTROOT)\Program Files\Bonjour\x64"
+	@copy $(BUILDDIR)\jdns_sd.dll "$(DSTROOT)\WINDOWS\system32\x64"
+	@copy $(BUILDDIR)\dns_sd.jar  "$(DSTROOT)\Program Files\Bonjour\x64"
+	@:CONT
+	@if not exist root					mkdir root
+	@if not exist root\"Program Files"	mkdir root\"Program Files"
+	@if not exist $(INSTALLDIR)			mkdir $(INSTALLDIR)
+	copy $(BUILDDIR)\dns_sd.jar $(INSTALLDIR)
+	copy $(BUILDDIR)\jdns_sd.dll $(INSTALLDIR)
+
+# clean removes targets and objects
+clean:
+	@if exist $(OBJDIR)		$(RMDIR) $(OBJDIR)
+	@if exist $(BUILDDIR)	$(RMDIR) $(BUILDDIR)
+
+#############################################################################
+
+# The following targets build Java wrappers for the dns-sd.h API.
+
+Java: setup $(BUILDDIR)\dns_sd.jar $(BUILDDIR)\jdns_sd.dll postbuild
+	@echo "Java wrappers done"
+
+JAVASRC	= $(SHAREDDIR)\Java
+JARCONTENTS =	$(OBJDIR)\com\apple\dnssd\DNSSDService.class \
+				$(OBJDIR)\com\apple\dnssd\DNSSDException.class \
+				$(OBJDIR)\com\apple\dnssd\DNSRecord.class \
+				$(OBJDIR)\com\apple\dnssd\TXTRecord.class \
+				$(OBJDIR)\com\apple\dnssd\DNSSDRegistration.class \
+				$(OBJDIR)\com\apple\dnssd\DNSSDRecordRegistrar.class \
+				$(OBJDIR)\com\apple\dnssd\BaseListener.class \
+				$(OBJDIR)\com\apple\dnssd\BrowseListener.class \
+				$(OBJDIR)\com\apple\dnssd\ResolveListener.class \
+				$(OBJDIR)\com\apple\dnssd\RegisterListener.class \
+				$(OBJDIR)\com\apple\dnssd\RegisterRecordListener.class \
+				$(OBJDIR)\com\apple\dnssd\QueryListener.class \
+				$(OBJDIR)\com\apple\dnssd\DomainListener.class \
+				$(OBJDIR)\com\apple\dnssd\DNSSD.class
+
+$(BUILDDIR)\dns_sd.jar: $(JARCONTENTS)
+	$(JAR) -cf $@ -C $(OBJDIR) com
+
+$(BUILDDIR)\jdns_sd.dll: $(JAVASRC)\JNISupport.c $(OBJDIR)\DNSSD.java.h $(OBJDIR)\jdns_sd.RES
+	$(CC) -Fe$@ $(JAVASRC)\JNISupport.c $(CFLAGS) -I$(OBJDIR) \
+	$(LIBDIR)\DNSSD.lib $(JDK)\lib\jvm.lib ws2_32.lib iphlpapi.lib $(OBJDIR)\jdns_sd.RES /link /NXCOMPAT /DYNAMICBASE
+
+.SUFFIXES : .java
+{$(JAVASRC)}.java{$(OBJDIR)\com\apple\dnssd}.class:	
+	$(JAVAC) -d $(OBJDIR) -classpath $(OBJDIR) $<
+
+$(OBJDIR)\DNSSD.java.h: $(OBJDIR)\com\apple\dnssd\DNSSD.class
+	$(JAVAH) -classpath $(OBJDIR) -o $@ \
+		com.apple.dnssd.AppleBrowser \
+		com.apple.dnssd.AppleResolver \
+		com.apple.dnssd.AppleRegistration \
+		com.apple.dnssd.AppleQuery \
+		com.apple.dnssd.AppleService 
+
+$(OBJDIR)\jdns_sd.RES: jdns_sd.rc
+	$(RC) /fo $@ $?
+
diff --git a/mdnsresponder/mDNSWindows/NSPTool/NSPTool.aps b/mdnsresponder/mDNSWindows/NSPTool/NSPTool.aps
new file mode 100644
index 0000000..313f306
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/NSPTool/NSPTool.aps
Binary files differ
diff --git a/mdnsresponder/mDNSWindows/NSPTool/NSPTool.c b/mdnsresponder/mDNSWindows/NSPTool/NSPTool.c
new file mode 100644
index 0000000..f3052d1
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/NSPTool/NSPTool.c
@@ -0,0 +1,581 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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	<stdio.h>
+#include	<stdlib.h>
+
+#include	"CommonServices.h"
+#include	"DebugServices.h"
+
+#include	<guiddef.h>
+#include	<ws2spi.h>
+
+//===========================================================================================================================
+//	Prototypes
+//===========================================================================================================================
+
+int  					main( int argc, char *argv[] );
+DEBUG_LOCAL void		Usage( void );
+DEBUG_LOCAL int			ProcessArgs( int argc, char *argv[] );
+DEBUG_LOCAL OSStatus	InstallNSP( const char *inName, const char *inGUID, const char *inPath );
+DEBUG_LOCAL OSStatus	RemoveNSP( const char *inGUID );
+DEBUG_LOCAL OSStatus	EnableNSP( const char *inGUID, BOOL inEnable );
+DEBUG_LOCAL OSStatus	ListNameSpaces( void );
+DEBUG_LOCAL OSStatus	ReorderNameSpaces( void );
+
+DEBUG_LOCAL WCHAR *		CharToWCharString( const char *inCharString, WCHAR *outWCharString );
+DEBUG_LOCAL char *		GUIDtoString( const GUID *inGUID, char *outString );
+DEBUG_LOCAL OSStatus	StringToGUID( const char *inCharString, GUID *outGUID );
+
+DEBUG_LOCAL BOOL gToolQuietMode = FALSE;
+
+//===========================================================================================================================
+//	main
+//===========================================================================================================================
+
+int main( int argc, char *argv[] )
+{
+	OSStatus		err;
+	
+	debug_initialize( kDebugOutputTypeMetaConsole );
+	debug_set_property( kDebugPropertyTagPrintLevel, kDebugLevelVerbose );
+	
+	err = ProcessArgs( argc, argv );
+	return( (int) err );
+}
+
+//===========================================================================================================================
+//	Usage
+//===========================================================================================================================
+
+DEBUG_LOCAL void	Usage( void )
+{
+	fprintf( stderr, "\n" );
+	fprintf( stderr, "NSP Tool 1.0d1\n" );
+	fprintf( stderr, "  Name Space Provider Tool\n" );
+	fprintf( stderr, "\n" );
+	
+	fprintf( stderr, "  -install <name> <guid> <path>   - Installs a Name Space Provider\n" );
+	fprintf( stderr, "\n" );
+	fprintf( stderr, "      <name> Name of the NSP\n" );
+	fprintf( stderr, "      <guid> GUID of the NSP\n" );
+	fprintf( stderr, "      <path> Path to the NSP file\n" );
+	fprintf( stderr, "\n" );
+	
+	fprintf( stderr, "  -remove <guid>                  - Removes a Name Space Provider\n" );
+	fprintf( stderr, "\n" );
+	fprintf( stderr, "      <guid> GUID of the NSP\n" );
+	fprintf( stderr, "\n" );
+	
+	fprintf( stderr, "  -enable/-disable <guid>         - Enables or Disables a Name Space Provider\n" );
+	fprintf( stderr, "\n" );
+	fprintf( stderr, "      <guid> GUID of the NSP\n" );
+	fprintf( stderr, "\n" );
+	
+	fprintf( stderr, "  -list                           - Lists Name Space Providers\n" );	
+	fprintf( stderr, "  -reorder                        - Reorders Name Space Providers\n" );
+	fprintf( stderr, "  -q                              - Enable quiet mode\n" );
+	fprintf( stderr, "  -h[elp]                         - Help\n" );
+	fprintf( stderr, "\n" );
+}
+
+//===========================================================================================================================
+//	ProcessArgs
+//===========================================================================================================================
+
+DEBUG_LOCAL int ProcessArgs( int argc, char* argv[] )
+{	
+	OSStatus			err;
+	int					i;
+	const char *		name;
+	const char *		guid;
+	const char *		path;
+			
+	if( argc <= 1 )
+	{
+		Usage();
+		err = 0;
+		goto exit;
+	}
+	for( i = 1; i < argc; ++i )
+	{
+		if( strcmp( argv[ i ], "-install" ) == 0 )
+		{
+			// Install
+			
+			if( argc <= ( i + 3 ) )
+			{
+				fprintf( stderr, "\n### ERROR: missing arguments for %s\n\n", argv[ i ] );
+				Usage();
+				err = kParamErr;
+				goto exit;
+			}
+			name = argv[ ++i ];
+			guid = argv[ ++i ];
+			path = argv[ ++i ];
+			
+			if( *name == '\0' )
+			{
+				name = "DotLocalNSP";
+			}
+			if( *guid == '\0' )
+			{
+				guid = "B600E6E9-553B-4a19-8696-335E5C896153";
+			}
+			
+			err = InstallNSP( name, guid, path );
+			require_noerr( err, exit );
+		}
+		else if( strcmp( argv[ i ], "-remove" ) == 0 )
+		{
+			// Remove
+			
+			if( argc <= ( i + 1 ) )
+			{
+				fprintf( stderr, "\n### ERROR: missing arguments for %s\n\n", argv[ i ] );
+				Usage();
+				err = kParamErr;
+				goto exit;
+			}
+			guid = argv[ ++i ];
+			if( *guid == '\0' )
+			{
+				guid = "B600E6E9-553B-4a19-8696-335E5C896153";
+			}
+			
+			err = RemoveNSP( guid );
+			require_noerr( err, exit );
+		}
+		else if( ( strcmp( argv[ i ], "-enable" )  == 0 ) || 
+				 ( strcmp( argv[ i ], "-disable" ) == 0 ) )
+		{
+			BOOL		enable;
+			
+			// Enable/Disable
+			
+			enable = ( strcmp( argv[ i ], "-enable" ) == 0 );
+			if( argc <= ( i + 1 ) )
+			{
+				fprintf( stderr, "\n### ERROR: missing arguments for %s\n\n", argv[ i ] );
+				Usage();
+				err = kParamErr;
+				goto exit;
+			}
+			guid = argv[ ++i ];
+			
+			err = EnableNSP( guid, enable );
+			require_noerr( err, exit );
+		}
+		else if( strcmp( argv[ i ], "-list" ) == 0 )
+		{
+			// List
+						
+			err = ListNameSpaces();
+			require_noerr( err, exit );
+		}
+		else if( strcmp( argv[ i ], "-reorder" ) == 0 )
+		{
+			// Reorder
+			
+			err = ReorderNameSpaces();
+			require_noerr( err, exit );
+		}
+		else if( strcmp( argv[ i ], "-q" ) == 0 )
+		{
+			gToolQuietMode = TRUE;
+		}
+		else if( ( strcmp( argv[ i ], "-help" ) == 0 ) || 
+				 ( strcmp( argv[ i ], "-h" ) == 0 ) )
+		{
+			// Help
+			
+			Usage();
+			err = 0;
+			goto exit;
+		}
+		else
+		{
+			fprintf( stderr, "\n### ERROR: unknown argment: \"%s\"\n\n", argv[ i ] );
+			Usage();
+			err = kParamErr;
+			goto exit;
+		}
+	}
+	err = kNoErr;
+	
+exit:
+	return( err );
+}
+
+#if 0
+#pragma mark -
+#endif
+
+//===========================================================================================================================
+//	InstallNSP
+//===========================================================================================================================
+
+OSStatus	InstallNSP( const char *inName, const char *inGUID, const char *inPath )
+{
+	OSStatus		err;
+	size_t			size;
+	WSADATA			wsd;
+	WCHAR			name[ 256 ];
+	GUID			guid;
+	WCHAR			path[ MAX_PATH ];
+	
+	require_action( inName && ( *inName != '\0' ), exit, err = kParamErr );
+	require_action( inGUID && ( *inGUID != '\0' ), exit, err = kParamErr );
+	require_action( inPath && ( *inPath != '\0' ), exit, err = kParamErr );
+	
+	size = strlen( inName );
+	require_action( size < sizeof_array( name ), exit, err = kSizeErr );
+	CharToWCharString( inName, name );
+	
+	err = StringToGUID( inGUID, &guid );
+	require_noerr( err, exit );
+	
+	size = strlen( inPath );
+	require_action( size < sizeof_array( path ), exit, err = kSizeErr );
+	CharToWCharString( inPath, path );
+	
+	err = WSAStartup( MAKEWORD( 2, 2 ), &wsd );
+	err = translate_errno( err == 0, errno_compat(), WSAEINVAL );
+	require_noerr( err, exit );
+	
+	err = WSCInstallNameSpace( name, path, NS_DNS, 1, &guid );
+	err = translate_errno( err == 0, errno_compat(), WSAEINVAL );
+	WSACleanup();
+	require_noerr( err, exit );
+	
+	if (!gToolQuietMode)
+	{
+		fprintf( stderr, "Installed NSP \"%s\" (%s) at %s\n", inName, inGUID, inPath );
+	}
+	
+exit:
+	if( err != kNoErr )
+	{
+		fprintf( stderr, "### FAILED (%d) to install \"%s\" (%s) Name Space Provider at %s\n", err, inName, inGUID, inPath );
+	}
+	return( err );
+}
+
+//===========================================================================================================================
+//	RemoveNSP
+//===========================================================================================================================
+
+DEBUG_LOCAL OSStatus	RemoveNSP( const char *inGUID )
+{
+	OSStatus		err;
+	WSADATA			wsd;
+	GUID			guid;
+	
+	require_action( inGUID && ( *inGUID != '\0' ), exit, err = kParamErr );
+	
+	err = StringToGUID( inGUID, &guid );
+	require_noerr( err, exit );
+	
+	err = WSAStartup( MAKEWORD( 2, 2 ), &wsd );
+	err = translate_errno( err == 0, errno_compat(), WSAEINVAL );
+	require_noerr( err, exit );
+	
+	err = WSCUnInstallNameSpace( &guid );
+	err = translate_errno( err == 0, errno_compat(), WSAEINVAL );
+	WSACleanup();
+	require_noerr( err, exit );
+	
+	if (!gToolQuietMode)
+	{
+		fprintf( stderr, "Removed NSP %s\n", inGUID );
+	}
+		
+exit:
+	if( err != kNoErr )
+	{
+		fprintf( stderr, "### FAILED (%d) to remove %s Name Space Provider\n", err, inGUID );
+	}
+	return( err );
+}
+
+//===========================================================================================================================
+//	EnableNSP
+//===========================================================================================================================
+
+DEBUG_LOCAL OSStatus	EnableNSP( const char *inGUID, BOOL inEnable )
+{
+	OSStatus		err;
+	WSADATA			wsd;
+	GUID			guid;
+	
+	require_action( inGUID && ( *inGUID != '\0' ), exit, err = kParamErr );
+	
+	err = StringToGUID( inGUID, &guid );
+	require_noerr( err, exit );
+	
+	err = WSAStartup( MAKEWORD( 2, 2 ), &wsd );
+	err = translate_errno( err == 0, errno_compat(), WSAEINVAL );
+	require_noerr( err, exit );
+	
+	err = WSCEnableNSProvider( &guid, inEnable );
+	err = translate_errno( err == 0, errno_compat(), WSAEINVAL );
+	WSACleanup();
+	require_noerr( err, exit );
+	
+	if (!gToolQuietMode)
+	{
+		fprintf( stderr, "Removed NSP %s\n", inGUID );
+	}
+		
+exit:
+	if( err != kNoErr )
+	{
+		fprintf( stderr, "### FAILED (%d) to remove %s Name Space Provider\n", err, inGUID );
+	}
+	return( err );
+}
+
+//===========================================================================================================================
+//	ListNameSpaces
+//===========================================================================================================================
+
+DEBUG_LOCAL OSStatus	ListNameSpaces( void )
+{
+	OSStatus				err;
+	WSADATA					wsd;
+	bool					started;
+	int						n;
+	int						i;
+	DWORD					size;
+	WSANAMESPACE_INFO *		array;
+	char					s[ 256 ];
+	
+	array 	= NULL;
+	started	= false;
+	
+	err = WSAStartup( MAKEWORD( 2, 2 ), &wsd );
+	err = translate_errno( err == 0, errno_compat(), WSAEINVAL );
+	require_noerr( err, exit );
+	started = true;
+	
+	// Build an array of all the NSPs. Call it first with NULL to get the size, allocate a buffer, then get them into it.
+	
+	size = 0;
+	n = WSAEnumNameSpaceProviders( &size, NULL );
+	err = translate_errno( n != SOCKET_ERROR, (OSStatus) GetLastError(), kUnknownErr );
+	require_action( err == WSAEFAULT, exit, err = kUnknownErr );
+	
+	array = (WSANAMESPACE_INFO *) malloc( size );
+	require_action( array, exit, err = kNoMemoryErr );
+	
+	n = WSAEnumNameSpaceProviders( &size, array );
+	err = translate_errno( n != SOCKET_ERROR, (OSStatus) GetLastError(), kUnknownErr );
+	require_noerr( err, exit );
+	
+	fprintf( stdout, "\n" );
+	for( i = 0; i < n; ++i )
+	{
+		fprintf( stdout, "Name Space %d\n", i + 1 );
+		fprintf( stdout, "    NSProviderId:   %s\n", GUIDtoString( &array[ i ].NSProviderId, s ) );
+		fprintf( stdout, "    dwNameSpace:    %d\n", array[ i ].dwNameSpace );
+		fprintf( stdout, "    fActive:        %s\n", array[ i ].fActive ? "YES" : "NO" );
+		fprintf( stdout, "    dwVersion:      %d\n", array[ i ].dwVersion );
+		fprintf( stdout, "    lpszIdentifier: \"%s\"\n", array[ i ].lpszIdentifier );
+		fprintf( stdout, "\n" );
+	}
+	err = kNoErr;
+	
+exit:
+	if( array )
+	{
+		free( array );
+	}
+	if( started )
+	{
+		WSACleanup();
+	}
+	if( err != kNoErr )
+	{
+		fprintf( stderr, "### FAILED (%d) to list Name Space Providers\n", err );
+	}
+	return( err );
+}
+
+//===========================================================================================================================
+//	ReorderNameSpaces
+//===========================================================================================================================
+
+DEBUG_LOCAL OSStatus	ReorderNameSpaces( void )
+{
+	OSStatus				err;
+	WSADATA					wsd;
+	bool					started;
+	int						n;
+	int						i;
+	DWORD					size;
+	WSANAMESPACE_INFO *		array;
+	WCHAR					name[ 256 ];
+	WCHAR					path[ MAX_PATH ];
+	
+	array 	= NULL;
+	started	= false;
+		
+	err = WSAStartup( MAKEWORD( 2, 2 ), &wsd );
+	err = translate_errno( err == 0, errno_compat(), WSAEINVAL );
+	require_noerr( err, exit );
+	started = true;
+	
+	// Build an array of all the NSPs. Call it first with NULL to get the size, allocate a buffer, then get them into it.
+	
+	size = 0;
+	n = WSAEnumNameSpaceProviders( &size, NULL );
+	err = translate_errno( n != SOCKET_ERROR, (OSStatus) GetLastError(), kUnknownErr );
+	require_action( err == WSAEFAULT, exit, err = kUnknownErr );
+	
+	array = (WSANAMESPACE_INFO *) malloc( size );
+	require_action( array, exit, err = kNoMemoryErr );
+	
+	n = WSAEnumNameSpaceProviders( &size, array );
+	err = translate_errno( n != SOCKET_ERROR, (OSStatus) GetLastError(), kUnknownErr );
+	require_noerr( err, exit );
+	
+	// Find the "Tcpip" NSP.
+	
+	for( i = 0; i < n; ++i )
+	{
+		if( strcmp( array[ i ].lpszIdentifier, "Tcpip" ) == 0 )
+		{
+			break;
+		}
+	}
+	require_action( i < n, exit, err = kNotFoundErr );
+	
+	// Uninstall it then re-install it to move it to the end.
+	
+	size = (DWORD) strlen( array[ i ].lpszIdentifier );
+	require_action( size < sizeof_array( name ), exit, err = kSizeErr );
+	CharToWCharString( array[ i ].lpszIdentifier, name );
+	
+	size = (DWORD) strlen( "%SystemRoot%\\System32\\mswsock.dll" );
+	require_action( size < sizeof_array( path ), exit, err = kSizeErr );
+	CharToWCharString( "%SystemRoot%\\System32\\mswsock.dll", path );
+	
+	err = WSCUnInstallNameSpace( &array[ i ].NSProviderId );
+	err = translate_errno( err == 0, errno_compat(), WSAEINVAL );
+	require_noerr( err, exit );
+	
+	err = WSCInstallNameSpace( name, path, NS_DNS, array[ i ].dwVersion, &array[ i ].NSProviderId );
+	err = translate_errno( err == 0, errno_compat(), WSAEINVAL );
+	require_noerr( err, exit );
+		
+	// Success!
+	
+	fprintf( stderr, "Reordered \"Tcpip\" NSP to to the bottom of the NSP chain\n" );	
+	err = kNoErr;
+	
+exit:
+	if( array )
+	{
+		free( array );
+	}
+	if( started )
+	{
+		WSACleanup();
+	}
+	if( err != kNoErr )
+	{
+		fprintf( stderr, "### FAILED (%d) to reorder Name Space Providers\n", err );
+	}
+	return( err );
+}
+
+#if 0
+#pragma mark -
+#endif
+
+//===========================================================================================================================
+//	CharToWCharString
+//===========================================================================================================================
+
+DEBUG_LOCAL WCHAR *	CharToWCharString( const char *inCharString, WCHAR *outWCharString )
+{
+	const char *		src;
+	WCHAR *				dst;
+	char				c;
+	
+	check( inCharString );
+	check( outWCharString );
+	
+	src = inCharString;
+	dst = outWCharString;
+	do
+	{
+		c = *src++;
+		*dst++ = (WCHAR) c;
+	
+	}	while( c != '\0' );
+	
+	return( outWCharString );
+}
+
+//===========================================================================================================================
+//	GUIDtoString
+//===========================================================================================================================
+
+DEBUG_LOCAL char *	GUIDtoString( const GUID *inGUID, char *outString )
+{
+	check( inGUID );
+	check( outString );
+	
+	sprintf( outString, "%08x-%04x-%04x-%02x%02x-%02x%02x%02x%02x%02x%02x", 
+		inGUID->Data1, inGUID->Data2, inGUID->Data3, 
+		inGUID->Data4[ 0 ], inGUID->Data4[ 1 ], inGUID->Data4[ 2 ], inGUID->Data4[ 3 ], 
+		inGUID->Data4[ 4 ], inGUID->Data4[ 5 ], inGUID->Data4[ 6 ], inGUID->Data4[ 7 ] );
+	return( outString );
+}
+
+//===========================================================================================================================
+//	StringToGUID
+//===========================================================================================================================
+
+DEBUG_LOCAL OSStatus	StringToGUID( const char *inCharString, GUID *outGUID )
+{
+	OSStatus			err;
+	int					n;
+	unsigned int		v[ 8 ];
+	
+	check( inCharString );
+	check( outGUID );
+	
+	n = sscanf( inCharString, "%lX-%hX-%hX-%02X%02X-%02X%02X%02X%02X%02X%02X", 
+		&outGUID->Data1, &outGUID->Data2, &outGUID->Data3, 
+		&v[ 0 ], &v[ 1 ], &v[ 2 ], &v[ 3 ], &v[ 4 ], &v[ 5 ], &v[ 6 ], &v[ 7 ] );
+	require_action( n == 11, exit, err = kFormatErr );
+	
+	outGUID->Data4[ 0 ] = (unsigned char) v[ 0 ];
+	outGUID->Data4[ 1 ] = (unsigned char) v[ 1 ];
+	outGUID->Data4[ 2 ] = (unsigned char) v[ 2 ];
+	outGUID->Data4[ 3 ] = (unsigned char) v[ 3 ];
+	outGUID->Data4[ 4 ] = (unsigned char) v[ 4 ];
+	outGUID->Data4[ 5 ] = (unsigned char) v[ 5 ];
+	outGUID->Data4[ 6 ] = (unsigned char) v[ 6 ];
+	outGUID->Data4[ 7 ] = (unsigned char) v[ 7 ];
+	err = kNoErr;
+
+exit:
+	return( err );
+}
diff --git a/mdnsresponder/mDNSWindows/NSPTool/NSPTool.rc b/mdnsresponder/mDNSWindows/NSPTool/NSPTool.rc
new file mode 100644
index 0000000..72a6b36
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/NSPTool/NSPTool.rc
@@ -0,0 +1,103 @@
+// Microsoft Visual C++ generated resource script.

+//

+#include "resource.h"

+

+#define APSTUDIO_READONLY_SYMBOLS

+/////////////////////////////////////////////////////////////////////////////

+//

+// Generated from the TEXTINCLUDE 2 resource.

+//

+#include "afxres.h"

+#include "WinVersRes.h"

+

+/////////////////////////////////////////////////////////////////////////////

+#undef APSTUDIO_READONLY_SYMBOLS

+

+/////////////////////////////////////////////////////////////////////////////

+// English (U.S.) resources

+

+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)

+#ifdef _WIN32

+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US

+#pragma code_page(1252)

+#endif //_WIN32

+

+#ifdef APSTUDIO_INVOKED

+/////////////////////////////////////////////////////////////////////////////

+//

+// TEXTINCLUDE

+//

+

+1 TEXTINCLUDE 

+BEGIN

+    "resource.h\0"

+END

+

+2 TEXTINCLUDE 

+BEGIN

+    "#include ""afxres.h""\r\n"

+    "\0"

+END

+

+3 TEXTINCLUDE 

+BEGIN

+    "\r\n"

+    "\0"

+END

+

+#endif    // APSTUDIO_INVOKED

+

+

+/////////////////////////////////////////////////////////////////////////////

+//

+// Version

+//

+

+VS_VERSION_INFO VERSIONINFO

+ FILEVERSION MASTER_PROD_VERS

+ PRODUCTVERSION MASTER_PROD_VERS

+ FILEFLAGSMASK 0x17L

+#ifdef _DEBUG

+ FILEFLAGS 0x1L

+#else

+ FILEFLAGS 0x0L

+#endif

+ FILEOS 0x4L

+ FILETYPE 0x1L

+ FILESUBTYPE 0x0L

+BEGIN

+    BLOCK "StringFileInfo"

+    BEGIN

+        BLOCK "040904b0"

+        BEGIN

+            VALUE "CompanyName", MASTER_COMPANY_NAME

+            VALUE "FileDescription", "NSPTool Application"

+            VALUE "FileVersion", MASTER_PROD_VERS_STR

+            VALUE "InternalName", "NSPTool"

+            VALUE "LegalCopyright", MASTER_LEGAL_COPYRIGHT

+            VALUE "OriginalFilename", "NSPTool.exe"

+            VALUE "ProductName", MASTER_PROD_NAME

+            VALUE "ProductVersion", MASTER_PROD_VERS_STR

+        END

+    END

+    BLOCK "VarFileInfo"

+    BEGIN

+        VALUE "Translation", 0x409, 1200

+    END

+END

+

+#endif    // English (U.S.) resources

+/////////////////////////////////////////////////////////////////////////////

+

+

+

+#ifndef APSTUDIO_INVOKED

+/////////////////////////////////////////////////////////////////////////////

+//

+// Generated from the TEXTINCLUDE 3 resource.

+//

+

+

+/////////////////////////////////////////////////////////////////////////////

+#endif    // not APSTUDIO_INVOKED

+

diff --git a/mdnsresponder/mDNSWindows/NSPTool/NSPTool.vcproj b/mdnsresponder/mDNSWindows/NSPTool/NSPTool.vcproj
new file mode 100644
index 0000000..bed3203
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/NSPTool/NSPTool.vcproj
@@ -0,0 +1,409 @@
+<?xml version="1.0" encoding="Windows-1252"?>

+<VisualStudioProject

+	ProjectType="Visual C++"

+	Version="8.00"

+	Name="NSPTool"

+	ProjectGUID="{208B3A9F-1CA0-4D1D-9D6C-C61616F94705}"

+	Keyword="Win32Proj"

+	>

+	<Platforms>

+		<Platform

+			Name="Win32"

+		/>

+		<Platform

+			Name="x64"

+		/>

+	</Platforms>

+	<ToolFiles>

+	</ToolFiles>

+	<Configurations>

+		<Configuration

+			Name="Debug|Win32"

+			OutputDirectory="$(PlatformName)\$(ConfigurationName)"

+			IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"

+			ConfigurationType="1"

+			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"

+			CharacterSet="2"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				Optimization="0"

+				AdditionalIncludeDirectories=".;..;../../mDNSShared"

+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;WIN32_LEAN_AND_MEAN;_CRT_SECURE_NO_DEPRECATE"

+				StringPooling="true"

+				MinimalRebuild="true"

+				ExceptionHandling="0"

+				BasicRuntimeChecks="3"

+				SmallerTypeCheck="true"

+				RuntimeLibrary="1"

+				BufferSecurityCheck="true"

+				EnableFunctionLevelLinking="false"

+				UsePrecompiledHeader="0"

+				AssemblerListingLocation="$(IntDir)\"

+				WarningLevel="4"

+				Detect64BitPortabilityProblems="true"

+				DebugInformationFormat="4"

+				CallingConvention="0"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+				AdditionalIncludeDirectories="..\"

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				AdditionalDependencies="ws2_32.lib iphlpapi.lib"

+				OutputFile="$(OutDir)/NSPTool.exe"

+				LinkIncremental="2"

+				GenerateDebugInformation="true"

+				ProgramDatabaseFile="$(OutDir)/NSPTool.pdb"

+				SubSystem="1"

+				TargetMachine="1"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+			/>

+		</Configuration>

+		<Configuration

+			Name="Debug|x64"

+			OutputDirectory="$(PlatformName)\$(ConfigurationName)"

+			IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"

+			ConfigurationType="1"

+			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"

+			CharacterSet="2"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+				TargetEnvironment="3"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				Optimization="0"

+				AdditionalIncludeDirectories=".;..;../../mDNSShared"

+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;WIN32_LEAN_AND_MEAN;_CRT_SECURE_NO_DEPRECATE"

+				StringPooling="true"

+				MinimalRebuild="true"

+				ExceptionHandling="0"

+				BasicRuntimeChecks="3"

+				SmallerTypeCheck="true"

+				RuntimeLibrary="1"

+				BufferSecurityCheck="true"

+				EnableFunctionLevelLinking="false"

+				UsePrecompiledHeader="0"

+				AssemblerListingLocation="$(IntDir)\"

+				WarningLevel="4"

+				Detect64BitPortabilityProblems="true"

+				DebugInformationFormat="3"

+				CallingConvention="0"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+				AdditionalIncludeDirectories="..\"

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				AdditionalDependencies="ws2_32.lib iphlpapi.lib"

+				OutputFile="$(OutDir)/NSPTool.exe"

+				LinkIncremental="2"

+				GenerateDebugInformation="true"

+				ProgramDatabaseFile="$(OutDir)/NSPTool.pdb"

+				SubSystem="1"

+				TargetMachine="17"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+			/>

+		</Configuration>

+		<Configuration

+			Name="Release|Win32"

+			OutputDirectory="$(PlatformName)\$(ConfigurationName)"

+			IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"

+			ConfigurationType="1"

+			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"

+			CharacterSet="2"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				AdditionalIncludeDirectories=".;..;../../mDNSShared"

+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;WIN32_LEAN_AND_MEAN;_CRT_SECURE_NO_DEPRECATE"

+				RuntimeLibrary="0"

+				UsePrecompiledHeader="0"

+				AssemblerListingLocation="$(IntDir)\"

+				WarningLevel="4"

+				Detect64BitPortabilityProblems="true"

+				DebugInformationFormat="3"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+				AdditionalIncludeDirectories="..\"

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				AdditionalDependencies="ws2_32.lib iphlpapi.lib"

+				OutputFile="$(OutDir)/NSPTool.exe"

+				LinkIncremental="1"

+				GenerateDebugInformation="true"

+				ProgramDatabaseFile="$(IntDir)/$(ProjectName).pdb"

+				SubSystem="1"

+				OptimizeReferences="2"

+				EnableCOMDATFolding="2"

+				TargetMachine="1"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+			/>

+		</Configuration>

+		<Configuration

+			Name="Release|x64"

+			OutputDirectory="$(PlatformName)\$(ConfigurationName)"

+			IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"

+			ConfigurationType="1"

+			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"

+			CharacterSet="2"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+				TargetEnvironment="3"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				AdditionalIncludeDirectories=".;..;../../mDNSShared"

+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;WIN32_LEAN_AND_MEAN;_CRT_SECURE_NO_DEPRECATE"

+				RuntimeLibrary="0"

+				UsePrecompiledHeader="0"

+				AssemblerListingLocation="$(IntDir)\"

+				WarningLevel="4"

+				Detect64BitPortabilityProblems="true"

+				DebugInformationFormat="3"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+				AdditionalIncludeDirectories="..\"

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				AdditionalDependencies="ws2_32.lib iphlpapi.lib"

+				OutputFile="$(OutDir)/NSPTool.exe"

+				LinkIncremental="1"

+				GenerateDebugInformation="true"

+				ProgramDatabaseFile="$(IntDir)/$(ProjectName).pdb"

+				SubSystem="1"

+				OptimizeReferences="2"

+				EnableCOMDATFolding="2"

+				TargetMachine="17"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+			/>

+		</Configuration>

+	</Configurations>

+	<References>

+	</References>

+	<Files>

+		<Filter

+			Name="Source Files"

+			Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"

+			UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"

+			>

+			<File

+				RelativePath="..\..\mDNSShared\DebugServices.c"

+				>

+			</File>

+			<File

+				RelativePath=".\NSPTool.c"

+				>

+			</File>

+		</Filter>

+		<Filter

+			Name="Header Files"

+			Filter="h;hpp;hxx;hm;inl;inc;xsd"

+			UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"

+			>

+			<File

+				RelativePath="..\..\mDNSShared\CommonServices.h"

+				>

+			</File>

+			<File

+				RelativePath="..\..\mDNSShared\DebugServices.h"

+				>

+			</File>

+			<File

+				RelativePath=".\resource.h"

+				>

+			</File>

+		</Filter>

+		<Filter

+			Name="Resource Files"

+			Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"

+			UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"

+			>

+			<File

+				RelativePath=".\NSPTool.rc"

+				>

+			</File>

+		</Filter>

+	</Files>

+	<Globals>

+	</Globals>

+</VisualStudioProject>

diff --git a/mdnsresponder/mDNSWindows/NSPTool/Prefix.h b/mdnsresponder/mDNSWindows/NSPTool/Prefix.h
new file mode 100644
index 0000000..3aa1cee
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/NSPTool/Prefix.h
@@ -0,0 +1,28 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+#ifndef __PREFIX__
+#define __PREFIX__
+
+#if( defined( _DEBUG ) )
+	#define	DEBUG				1
+	#define	MDNS_DEBUGMSGS		1
+#else
+	#define	DEBUG				0
+#endif
+
+#endif	// __PREFIX__
diff --git a/mdnsresponder/mDNSWindows/NSPTool/resource.h b/mdnsresponder/mDNSWindows/NSPTool/resource.h
new file mode 100644
index 0000000..78c1d91
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/NSPTool/resource.h
@@ -0,0 +1,27 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by NSPTool.rc
+//
+#define IDS_PROJNAME                    100
+#define IDR_WMDMLOGGER                  101
+#define IDS_LOG_SEV_INFO                201
+#define IDS_LOG_SEV_WARN                202
+#define IDS_LOG_SEV_ERROR               203
+#define IDS_LOG_DATETIME                204
+#define IDS_LOG_SRCNAME                 205
+#define IDS_DEF_LOGFILE                 301
+#define IDS_DEF_MAXSIZE                 302
+#define IDS_DEF_SHRINKTOSIZE            303
+#define IDS_DEF_LOGENABLED              304
+#define IDS_MUTEX_TIMEOUT               401
+
+// Next default values for new objects
+// 
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE        201
+#define _APS_NEXT_COMMAND_VALUE         32768
+#define _APS_NEXT_CONTROL_VALUE         201
+#define _APS_NEXT_SYMED_VALUE           101
+#endif
+#endif
diff --git a/mdnsresponder/mDNSWindows/PosixCompat.c b/mdnsresponder/mDNSWindows/PosixCompat.c
new file mode 100755
index 0000000..506c82b
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/PosixCompat.c
@@ -0,0 +1,128 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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 "PosixCompat.h"
+#include <DebugServices.h>
+
+
+typedef PCHAR (WINAPI * if_indextoname_funcptr_t)(ULONG index, PCHAR name);
+typedef ULONG (WINAPI * if_nametoindex_funcptr_t)(PCSTR name);
+
+
+unsigned
+if_nametoindex( const char * ifname )
+{
+	HMODULE library;
+	unsigned index = 0;
+
+	check( ifname );
+
+	// Try and load the IP helper library dll
+	if ((library = LoadLibrary(TEXT("Iphlpapi")) ) != NULL )
+	{
+		if_nametoindex_funcptr_t if_nametoindex_funcptr;
+
+		// On Vista and above there is a Posix like implementation of if_nametoindex
+		if ((if_nametoindex_funcptr = (if_nametoindex_funcptr_t) GetProcAddress(library, "if_nametoindex")) != NULL )
+		{
+			index = if_nametoindex_funcptr(ifname);
+		}
+
+		FreeLibrary(library);
+	}
+
+	return index;
+}
+
+
+char*
+if_indextoname( unsigned ifindex, char * ifname )
+{
+	HMODULE library;
+	char * name = NULL;
+
+	check( ifname );
+	*ifname = '\0';
+
+	// Try and load the IP helper library dll
+	if ((library = LoadLibrary(TEXT("Iphlpapi")) ) != NULL )
+	{
+		if_indextoname_funcptr_t if_indextoname_funcptr;
+
+		// On Vista and above there is a Posix like implementation of if_indextoname
+		if ((if_indextoname_funcptr = (if_indextoname_funcptr_t) GetProcAddress(library, "if_indextoname")) != NULL )
+		{
+			name = if_indextoname_funcptr(ifindex, ifname);
+		}
+
+		FreeLibrary(library);
+	}
+
+	return name;
+}
+
+
+int
+inet_pton( int family, const char * addr, void * dst )
+{
+	struct sockaddr_storage ss;
+	int sslen = sizeof( ss );
+
+	ZeroMemory( &ss, sizeof( ss ) );
+	ss.ss_family = family;
+
+	if ( WSAStringToAddressA( ( LPSTR ) addr, family, NULL, ( struct sockaddr* ) &ss, &sslen ) == 0 )
+	{
+		if ( family == AF_INET ) { memcpy( dst, &( ( struct sockaddr_in* ) &ss)->sin_addr, sizeof( IN_ADDR ) ); return 1; }
+		else if ( family == AF_INET6 ) { memcpy( dst, &( ( struct sockaddr_in6* ) &ss)->sin6_addr, sizeof( IN6_ADDR ) ); return 1; }
+		else return 0;
+	}
+    else return 0;
+}
+

+
+int
+gettimeofday( struct timeval * tv, struct timezone * tz )
+{

+#define EPOCHFILETIME (116444736000000000i64)

+

+	if ( tv != NULL )

+	{

+		FILETIME        ft;

+		LARGE_INTEGER   li;

+		__int64         t;

+

+		GetSystemTimeAsFileTime(&ft);

+		li.LowPart  = ft.dwLowDateTime;

+		li.HighPart = ft.dwHighDateTime;

+		t  = li.QuadPart;	/* In 100-nanosecond intervals */

+		t -= EPOCHFILETIME;	/* Offset to the Epoch time */

+		t /= 10;			/* In microseconds */

+		tv->tv_sec  = ( long )( t / 1000000 );

+		tv->tv_usec = ( long )( t % 1000000 );

+	}

+

+	return 0;
+}
+
+
+extern struct tm*
+localtime_r( const time_t * clock, struct tm * result )
+{
+	localtime_s( result, clock );
+	return result;
+}
diff --git a/mdnsresponder/mDNSWindows/PosixCompat.h b/mdnsresponder/mDNSWindows/PosixCompat.h
new file mode 100755
index 0000000..9f9d005
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/PosixCompat.h
@@ -0,0 +1,70 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+#pragma once
+
+#include "CommonServices.h"
+#include <winsock2.h>
+#include <time.h>
+
+
+/* 
+ * Posix process compatibility
+ */
+typedef int pid_t;
+#if !defined( getpid )
+#	define getpid _getpid
+#endif
+
+
+/* 
+ * Posix networking compatibility
+ */
+extern unsigned
+if_nametoindex( const char * ifname );
+
+
+extern char*
+if_indextoname( unsigned ifindex, char * ifname );
+
+
+extern int
+inet_pton( int family, const char * addr, void * dst );
+
+
+/* 
+ * Posix time compatibility
+ */
+extern int
+gettimeofday( struct timeval * tv, struct timezone * tz );
+
+
+extern struct tm*
+localtime_r( const time_t * clock, struct tm * result );
+
+
+/* 
+ * Posix string compatibility
+ */
+#if !defined( strcasecmp )
+#	define strcasecmp	_stricmp
+#endif
+
+#if !defined( snprintf )
+#	define snprint		_snprintf
+#endif
+
diff --git a/mdnsresponder/mDNSWindows/README.txt b/mdnsresponder/mDNSWindows/README.txt
new file mode 100644
index 0000000..8d8ab4b
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/README.txt
@@ -0,0 +1,85 @@
+This directory contains support files for running mDNS on Microsoft Windows 
+and Windows CE/PocketPC. Building this code requires the Windows SDK 2003
+or later. The CodeWarrior builds require CodeWarrior 8 or later and if using
+CodeWarrior 8, the newer Windows headers from the Mac CodeWarrior 9 need to
+be used.
+
+mDNSWin32.c/.h
+	
+	Platform Support files that go below mDNS Core. These work on both Windows 
+	and Windows CE/PocketPC.
+
+DNSSD.c/.h
+
+	High-level implementation of the DNS-SD API. This supports both "direct"
+	(compiled-in mDNSCore) and "client" (IPC to service) usage. Conditionals
+	can exclude either "direct" or "client" to reduce code size.
+
+DNSSDDirect.c/.h
+
+	Portable implementation of the DNS-SD API. This interacts with mDNSCore
+	to perform all the real work of the DNS-SD API. This code does not rely
+	on any platform-specifics so it should run on any platform with an mDNS
+	platform plugin available. Software that cannot or does not want to use
+	the IPC mechanism (e.g. Windows CE, VxWorks, etc.) can use this code 
+	directly without any of the IPC pieces.
+
+RMxClient.c/.h
+	
+	Client-side implementation of the DNS-SD IPC API. This handles sending 
+	and receiving messages from the service to perform DNS-SD operations 
+	and get DNS-SD responses.
+
+RMxCommon.c/.h
+
+	Common code between the RMxClient and RMxServer. This handles establishing
+	and accepting connections, the underying message sending and receiving, 
+	portable data packing and unpacking, and shared utility routines.
+
+RMxServer.c/.h
+
+	Server-side implementation of the DNS-SD IPC API. This listens for 
+	and accepts connections from IPC clients, starts server sessions, and 
+	acts as a mediator between the "direct" (compiled-in mDNSCore) code
+	and the IPC client.
+
+DNSServices is an obsolete higher-level API for using mDNS. New code should 
+use the DNS-SD APIs.
+
+DNSServiceDiscovery is an obsolete emulation layer that sits on top of 
+DNSServices and provides the Mac OS X DNS Service Discovery API's on any 
+platform. New code should use the DNS-SD APIs.
+
+Tool.c is an example client that uses the services of mDNS Core.
+
+ToolWin32.mcp is a CodeWarrior project (CodeWarrior for Windows version 8). 
+ToolWin32.vcproj is a Visual Studio .NET 7 project. These projects build 
+Tool.c to make DNSServiceTest.exe, a small Windows command-line tool to do all 
+the standard DNS-SD stuff on Windows. It has the following features:
+
+- Browse for browsing and/or registration domains.
+- Browse for services.
+- Lookup Service Instances.
+- Register domains for browsing and/or registration.
+- Register services.
+
+For example, if you have a Windows machine running a Web server,
+then you can make it advertise that it is offering HTTP on port 80
+with the following command:
+
+DNSServiceTest -rs "Windows Web Server" "_http._tcp." "local." 80 ""
+
+To search for AFP servers, use this:
+
+DNSServiceTest -bs "_afpovertcp._tcp." "local."
+
+You can also do multiple things at once (e.g. register a service and
+browse for it so one instance of the app can be used for testing).
+Multiple instances can also be run on the same machine to discover each
+other. There is a -help command to show all the commands, their
+parameters, and some examples of using it.
+
+DNSServiceBrowser contains the source code for a graphical browser application 
+for Windows CE/PocketPC. The Windows CE/PocketPC version requires Microsoft 
+eMbedded C++ 4.0 with SP2 installed and the PocketPC 2003 SDK.
+
diff --git a/mdnsresponder/mDNSWindows/RegNames.h b/mdnsresponder/mDNSWindows/RegNames.h
new file mode 100644
index 0000000..bc885d6
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/RegNames.h
@@ -0,0 +1,57 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+//----------------------------------------------------------------------------------------
+//	Registry Constants
+//----------------------------------------------------------------------------------------
+
+#if defined(UNICODE)
+
+#	define kServiceParametersSoftware			L"SOFTWARE"
+#	define kServiceParametersAppleComputer		L"Apple Computer, Inc."
+#	define kServiceParametersBonjour			L"Bonjour"
+#	define kServiceParametersNode				L"SOFTWARE\\Apple Inc.\\Bonjour"
+#	define kServiceName							L"Bonjour Service"
+#	define kServiceDynDNSBrowseDomains			L"BrowseDomains"
+#	define kServiceDynDNSHostNames				L"HostNames"
+#	define kServiceDynDNSRegistrationDomains	L"RegistrationDomains"
+#	define kServiceDynDNSDomains				L"Domains"	// value is comma separated list of domains
+#	define kServiceDynDNSEnabled				L"Enabled"
+#	define kServiceDynDNSStatus					L"Status"
+#	define kServiceManageLLRouting				L"ManageLLRouting"
+#	define kServiceCacheEntryCount				L"CacheEntryCount"
+#	define kServiceManageFirewall				L"ManageFirewall"
+#	define kServiceAdvertisedServices			L"Services"
+
+# else
+
+#	define kServiceParametersSoftware			"SOFTWARE"
+#	define kServiceParametersAppleComputer		"Apple Computer, Inc."
+#	define kServiceParametersBonjour			"Bonjour"
+#	define kServiceParametersNode				"SOFTWARE\\Apple Inc.\\Bonjour"
+#	define kServiceName							"Bonjour Service"
+#	define kServiceDynDNSBrowseDomains			"BrowseDomains"
+#	define kServiceDynDNSHostNames				"HostNames"
+#	define kServiceDynDNSRegistrationDomains	"RegistrationDomains"
+#	define kServiceDynDNSDomains				"Domains"	// value is comma separated list of domains
+#	define kServiceDynDNSEnabled				"Enabled"
+#	define kServiceDynDNSStatus					"Status"
+#	define kServiceManageLLRouting				"ManageLLRouting"
+#	define kServiceCacheEntryCount				"CacheEntryCount"
+#	define kServiceManageFirewall				"ManageFirewall"
+
+#endif
diff --git a/mdnsresponder/mDNSWindows/Secret.c b/mdnsresponder/mDNSWindows/Secret.c
new file mode 100644
index 0000000..5abd28b
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/Secret.c
@@ -0,0 +1,338 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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 "Secret.h"
+#include <stdarg.h>
+#include <stddef.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <winsock2.h>
+#include <ws2tcpip.h>
+#include <windows.h>
+#include <process.h>
+#include <ntsecapi.h>
+#include <lm.h>
+#include "DebugServices.h"
+
+
+mDNSlocal OSStatus MakeLsaStringFromUTF8String( PLSA_UNICODE_STRING output, const char * input );
+mDNSlocal OSStatus MakeUTF8StringFromLsaString( char * output, size_t len, PLSA_UNICODE_STRING input );
+
+
+BOOL
+LsaGetSecret( const char * inDomain, char * outDomain, unsigned outDomainSize, char * outKey, unsigned outKeySize, char * outSecret, unsigned outSecretSize )
+{
+	PLSA_UNICODE_STRING		domainLSA;
+	PLSA_UNICODE_STRING		keyLSA;
+	PLSA_UNICODE_STRING		secretLSA;
+	size_t					i;
+	size_t					dlen;
+	LSA_OBJECT_ATTRIBUTES	attrs;
+	LSA_HANDLE				handle = NULL;
+	NTSTATUS				res;
+	OSStatus				err;
+
+	check( inDomain );
+	check( outDomain );
+	check( outKey );
+	check( outSecret );
+
+	// Initialize
+
+	domainLSA	= NULL;
+	keyLSA		= NULL;
+	secretLSA	= NULL;
+
+	// Make sure we have enough space to add trailing dot
+
+	dlen = strlen( inDomain );
+	err = strcpy_s( outDomain, outDomainSize - 2, inDomain );
+	require_noerr( err, exit );
+
+	// If there isn't a trailing dot, add one because the mDNSResponder
+	// presents names with the trailing dot.
+
+	if ( outDomain[ dlen - 1 ] != '.' )
+	{
+		outDomain[ dlen++ ] = '.';
+		outDomain[ dlen ] = '\0';
+	}
+
+	// Canonicalize name by converting to lower case (keychain and some name servers are case sensitive)
+
+	for ( i = 0; i < dlen; i++ )
+	{
+		outDomain[i] = (char) tolower( outDomain[i] );  // canonicalize -> lower case
+	}
+
+	// attrs are reserved, so initialize to zeroes.
+
+	ZeroMemory( &attrs, sizeof( attrs ) );
+
+	// Get a handle to the Policy object on the local system
+
+	res = LsaOpenPolicy( NULL, &attrs, POLICY_GET_PRIVATE_INFORMATION, &handle );
+	err = translate_errno( res == 0, LsaNtStatusToWinError( res ), kUnknownErr );
+	require_noerr( err, exit );
+
+	// Get the encrypted data
+
+	domainLSA = ( PLSA_UNICODE_STRING ) malloc( sizeof( LSA_UNICODE_STRING ) );
+	require_action( domainLSA != NULL, exit, err = mStatus_NoMemoryErr );
+	err = MakeLsaStringFromUTF8String( domainLSA, outDomain );
+	require_noerr( err, exit );
+
+	// Retrieve the key
+
+	res = LsaRetrievePrivateData( handle, domainLSA, &keyLSA );
+	err = translate_errno( res == 0, LsaNtStatusToWinError( res ), kUnknownErr );
+	require_noerr_quiet( err, exit );
+
+	// <rdar://problem/4192119> Lsa secrets use a flat naming space.  Therefore, we will prepend "$" to the keyname to
+	// make sure it doesn't conflict with a zone name.	
+	// Strip off the "$" prefix.
+
+	err = MakeUTF8StringFromLsaString( outKey, outKeySize, keyLSA );
+	require_noerr( err, exit );
+	require_action( outKey[0] == '$', exit, err = kUnknownErr );
+	memcpy( outKey, outKey + 1, strlen( outKey ) );
+
+	// Retrieve the secret
+
+	res = LsaRetrievePrivateData( handle, keyLSA, &secretLSA );
+	err = translate_errno( res == 0, LsaNtStatusToWinError( res ), kUnknownErr );
+	require_noerr_quiet( err, exit );
+
+	// Convert the secret to UTF8 string
+
+	err = MakeUTF8StringFromLsaString( outSecret, outSecretSize, secretLSA );
+	require_noerr( err, exit );
+
+exit:
+
+	if ( domainLSA != NULL )
+	{
+		if ( domainLSA->Buffer != NULL )
+		{
+			free( domainLSA->Buffer );
+		}
+
+		free( domainLSA );
+	}
+
+	if ( keyLSA != NULL )
+	{
+		LsaFreeMemory( keyLSA );
+	}
+
+	if ( secretLSA != NULL )
+	{
+		LsaFreeMemory( secretLSA );
+	}
+
+	if ( handle )
+	{
+		LsaClose( handle );
+		handle = NULL;
+	}
+
+	return ( !err ) ? TRUE : FALSE;
+}
+
+
+mDNSBool
+LsaSetSecret( const char * inDomain, const char * inKey, const char * inSecret )
+{
+	size_t					inDomainLength;
+	size_t					inKeyLength;
+	char					domain[ 1024 ];
+	char					key[ 1024 ];
+	LSA_OBJECT_ATTRIBUTES	attrs;
+	LSA_HANDLE				handle = NULL;
+	NTSTATUS				res;
+	LSA_UNICODE_STRING		lucZoneName;
+	LSA_UNICODE_STRING		lucKeyName;
+	LSA_UNICODE_STRING		lucSecretName;
+	BOOL					ok = TRUE;
+	OSStatus				err;
+
+	require_action( inDomain != NULL, exit, ok = FALSE );
+	require_action( inKey != NULL, exit, ok = FALSE );
+	require_action( inSecret != NULL, exit, ok = FALSE );
+
+	// If there isn't a trailing dot, add one because the mDNSResponder
+	// presents names with the trailing dot.
+
+	ZeroMemory( domain, sizeof( domain ) );
+	inDomainLength = strlen( inDomain );
+	require_action( inDomainLength > 0, exit, ok = FALSE );
+	err = strcpy_s( domain, sizeof( domain ) - 2, inDomain );
+	require_action( !err, exit, ok = FALSE );
+
+	if ( domain[ inDomainLength - 1 ] != '.' )
+	{
+		domain[ inDomainLength++ ] = '.';
+		domain[ inDomainLength ] = '\0';
+	}
+
+	// <rdar://problem/4192119>
+	//
+	// Prepend "$" to the key name, so that there will
+	// be no conflict between the zone name and the key
+	// name
+
+	ZeroMemory( key, sizeof( key ) );
+	inKeyLength = strlen( inKey );
+	require_action( inKeyLength > 0 , exit, ok = FALSE );
+	key[ 0 ] = '$';
+	err = strcpy_s( key + 1, sizeof( key ) - 3, inKey );
+	require_action( !err, exit, ok = FALSE );
+	inKeyLength++;
+
+	if ( key[ inKeyLength - 1 ] != '.' )
+	{
+		key[ inKeyLength++ ] = '.';
+		key[ inKeyLength ] = '\0';
+	}
+
+	// attrs are reserved, so initialize to zeroes.
+
+	ZeroMemory( &attrs, sizeof( attrs ) );
+
+	// Get a handle to the Policy object on the local system
+
+	res = LsaOpenPolicy( NULL, &attrs, POLICY_ALL_ACCESS, &handle );
+	err = translate_errno( res == 0, LsaNtStatusToWinError( res ), kUnknownErr );
+	require_noerr( err, exit );
+
+	// Intializing PLSA_UNICODE_STRING structures
+
+	err = MakeLsaStringFromUTF8String( &lucZoneName, domain );
+	require_noerr( err, exit );
+
+	err = MakeLsaStringFromUTF8String( &lucKeyName, key );
+	require_noerr( err, exit );
+
+	err = MakeLsaStringFromUTF8String( &lucSecretName, inSecret );
+	require_noerr( err, exit );
+
+	// Store the private data.
+
+	res = LsaStorePrivateData( handle, &lucZoneName, &lucKeyName );
+	err = translate_errno( res == 0, LsaNtStatusToWinError( res ), kUnknownErr );
+	require_noerr( err, exit );
+
+	res = LsaStorePrivateData( handle, &lucKeyName, &lucSecretName );
+	err = translate_errno( res == 0, LsaNtStatusToWinError( res ), kUnknownErr );
+	require_noerr( err, exit );
+
+exit:
+
+	if ( handle )
+	{
+		LsaClose( handle );
+		handle = NULL;
+	}
+
+	return ok;
+}
+
+
+//===========================================================================================================================
+//	MakeLsaStringFromUTF8String
+//===========================================================================================================================
+
+mDNSlocal OSStatus
+MakeLsaStringFromUTF8String( PLSA_UNICODE_STRING output, const char * input )
+{
+	int			size;
+	OSStatus	err;
+	
+	check( input );
+	check( output );
+
+	output->Buffer = NULL;
+
+	size = MultiByteToWideChar( CP_UTF8, 0, input, -1, NULL, 0 );
+	err = translate_errno( size > 0, GetLastError(), kUnknownErr );
+	require_noerr( err, exit );
+
+	output->Length = (USHORT)( size * sizeof( wchar_t ) );
+	output->Buffer = (PWCHAR) malloc( output->Length );
+	require_action( output->Buffer, exit, err = mStatus_NoMemoryErr );
+	size = MultiByteToWideChar( CP_UTF8, 0, input, -1, output->Buffer, size );
+	err = translate_errno( size > 0, GetLastError(), kUnknownErr );
+	require_noerr( err, exit );
+
+	// We're going to subtrace one wchar_t from the size, because we didn't
+	// include it when we encoded the string
+
+	output->MaximumLength = output->Length;
+	output->Length		-= sizeof( wchar_t );
+	
+exit:
+
+	if ( err && output->Buffer )
+	{
+		free( output->Buffer );
+		output->Buffer = NULL;
+	}
+
+	return( err );
+}
+
+
+
+//===========================================================================================================================
+//	MakeUTF8StringFromLsaString
+//===========================================================================================================================
+
+mDNSlocal OSStatus
+MakeUTF8StringFromLsaString( char * output, size_t len, PLSA_UNICODE_STRING input )
+{
+	size_t		size;
+	OSStatus	err = kNoErr;
+
+	// The Length field of this structure holds the number of bytes,
+	// but WideCharToMultiByte expects the number of wchar_t's. So
+	// we divide by sizeof(wchar_t) to get the correct number.
+
+	size = (size_t) WideCharToMultiByte(CP_UTF8, 0, input->Buffer, ( input->Length / sizeof( wchar_t ) ), NULL, 0, NULL, NULL);
+	err = translate_errno( size != 0, GetLastError(), kUnknownErr );
+	require_noerr( err, exit );
+	
+	// Ensure that we have enough space (Add one for trailing '\0')
+
+	require_action( ( size + 1 ) <= len, exit, err = mStatus_NoMemoryErr );
+
+	// Convert the string
+
+	size = (size_t) WideCharToMultiByte( CP_UTF8, 0, input->Buffer, ( input->Length / sizeof( wchar_t ) ), output, (int) size, NULL, NULL);	
+	err = translate_errno( size != 0, GetLastError(), kUnknownErr );
+	require_noerr( err, exit );
+
+	// have to add the trailing 0 because WideCharToMultiByte doesn't do it,
+	// although it does return the correct size
+
+	output[size] = '\0';
+
+exit:
+
+	return err;
+}
+
diff --git a/mdnsresponder/mDNSWindows/Secret.h b/mdnsresponder/mDNSWindows/Secret.h
new file mode 100644
index 0000000..79643d6
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/Secret.h
@@ -0,0 +1,42 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+#ifndef _Secret_h
+#define _Secret_h
+
+#include "mDNSEmbeddedAPI.h"
+
+
+#if defined(__cplusplus )
+extern "C" {
+#endif
+
+
+extern mDNSBool
+LsaGetSecret( const char * inDomain, char * outDomain, unsigned outDomainLength, char * outKey, unsigned outKeyLength, char * outSecret, unsigned outSecretLength );
+
+
+extern mDNSBool
+LsaSetSecret( const char * inDomain, const char * inKey, const char * inSecret );
+
+
+#if defined(__cplusplus)
+}
+#endif
+
+
+#endif
\ No newline at end of file
diff --git a/mdnsresponder/mDNSWindows/SystemService/EventLog.mc b/mdnsresponder/mDNSWindows/SystemService/EventLog.mc
new file mode 100644
index 0000000..248e6c1
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/SystemService/EventLog.mc
@@ -0,0 +1,11 @@
+MessageIdTypedef=WORD
+LanguageNames=(English=0x409:MSG00409)
+
+MessageId=100
+SymbolicName=MDNSRESPONDER_LOG
+Severity=Success
+Facility=Application
+Language=English
+%1
+.
+
diff --git a/mdnsresponder/mDNSWindows/SystemService/EventLogMessages.bin b/mdnsresponder/mDNSWindows/SystemService/EventLogMessages.bin
new file mode 100644
index 0000000..e74c67e
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/SystemService/EventLogMessages.bin
Binary files differ
diff --git a/mdnsresponder/mDNSWindows/SystemService/Firewall.cpp b/mdnsresponder/mDNSWindows/SystemService/Firewall.cpp
new file mode 100755
index 0000000..c7c96d0
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/SystemService/Firewall.cpp
@@ -0,0 +1,484 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+// <rdar://problem/4278931> Doesn't compile correctly with latest Platform SDK
+
+#if !defined(_WIN32_DCOM)
+#	define _WIN32_DCOM 
+#endif
+
+
+#include "Firewall.h"
+#include <windows.h>
+#include <crtdbg.h>
+#include <netfw.h>
+#include <objbase.h>
+#include <oleauto.h>
+
+
+static const int kMaxTries			= 30;
+static const int kRetrySleepPeriod	= 1 * 1000; // 1 second
+
+
+static OSStatus
+mDNSFirewallInitialize(OUT INetFwProfile ** fwProfile)
+{
+	INetFwMgr		*	fwMgr		= NULL;
+	INetFwPolicy	*	fwPolicy	= NULL;
+	int					numRetries	= 0;
+	HRESULT				err			= kNoErr;
+    
+	_ASSERT(fwProfile != NULL);
+
+    *fwProfile = NULL;
+
+	// Use COM to get a reference to the firewall settings manager.  This
+	// call will fail on anything other than XP SP2
+
+	err = CoCreateInstance( __uuidof(NetFwMgr), NULL, CLSCTX_INPROC_SERVER, __uuidof(INetFwMgr), (void**)&fwMgr );
+	require(SUCCEEDED(err) && ( fwMgr != NULL ), exit);
+
+	// Use the reference to get the local firewall policy
+
+	err = fwMgr->get_LocalPolicy(&fwPolicy);
+	require(SUCCEEDED(err) && ( fwPolicy != NULL ), exit);
+
+	// Use the reference to get the extant profile. Empirical evidence
+	// suggests that there is the potential for a race condition when a system
+	// service whose startup type is automatic calls this method.
+	// This is true even when the service declares itself to be dependent
+	// on the firewall service. Re-trying the method will succeed within
+	// a few seconds.
+
+	do
+	{
+    	err = fwPolicy->get_CurrentProfile(fwProfile);
+
+		if (err)
+		{
+			Sleep(kRetrySleepPeriod);
+		}
+	}
+	while (err && (numRetries++ < kMaxTries));
+
+	require(SUCCEEDED(err), exit);
+
+	err = kNoErr;
+
+exit:
+
+	// Release temporary COM objects
+
+    if (fwPolicy != NULL)
+    {
+        fwPolicy->Release();
+    }
+
+    if (fwMgr != NULL)
+    {
+        fwMgr->Release();
+    }
+
+    return err;
+}
+
+
+static void
+mDNSFirewallCleanup
+			(
+			IN INetFwProfile	*	fwProfile
+			)
+{
+	// Call Release on the COM reference.
+
+    if (fwProfile != NULL)
+    {
+        fwProfile->Release();
+    }
+}
+
+
+static OSStatus
+mDNSFirewallAppIsEnabled
+			(
+			IN INetFwProfile	*	fwProfile,
+			IN const wchar_t	*	fwProcessImageFileName,
+			OUT BOOL			*	fwAppEnabled    
+			)
+{
+	BSTR							fwBstrProcessImageFileName = NULL;
+	VARIANT_BOOL					fwEnabled;
+	INetFwAuthorizedApplication	*	fwApp	= NULL;
+	INetFwAuthorizedApplications*	fwApps	= NULL;
+	OSStatus						err		= kNoErr;
+    
+	_ASSERT(fwProfile != NULL);
+	_ASSERT(fwProcessImageFileName != NULL);
+	_ASSERT(fwAppEnabled != NULL);
+
+    *fwAppEnabled = FALSE;
+
+	// Get the list of authorized applications
+
+	err = fwProfile->get_AuthorizedApplications(&fwApps);
+	require(SUCCEEDED(err) && ( fwApps != NULL ), exit);
+
+    fwBstrProcessImageFileName = SysAllocString(fwProcessImageFileName);
+	require_action( ( fwProcessImageFileName != NULL ) && ( SysStringLen(fwBstrProcessImageFileName) > 0 ), exit, err = kNoMemoryErr);
+
+	// Look for us
+
+    err = fwApps->Item(fwBstrProcessImageFileName, &fwApp);
+	
+    if (SUCCEEDED(err) && ( fwApp != NULL ) )
+    {
+        // It's listed, but is it enabled?
+
+		err = fwApp->get_Enabled(&fwEnabled);
+		require(SUCCEEDED(err), exit);
+
+        if (fwEnabled != VARIANT_FALSE)
+        {
+			// Yes, it's enabled
+
+            *fwAppEnabled = TRUE;
+		}
+	}
+
+	err = kNoErr;
+
+exit:
+
+	// Deallocate the BSTR
+
+	if ( fwBstrProcessImageFileName != NULL )
+	{
+		SysFreeString(fwBstrProcessImageFileName);
+	}
+
+	// Release the COM objects
+
+    if (fwApp != NULL)
+    {
+        fwApp->Release();
+    }
+
+    if (fwApps != NULL)
+    {
+        fwApps->Release();
+    }
+
+    return err;
+}
+
+
+static OSStatus
+mDNSFirewallAddApp
+			(
+            IN INetFwProfile	*	fwProfile,
+            IN const wchar_t	*	fwProcessImageFileName,
+            IN const wchar_t	*	fwName
+            )
+{
+	BOOL							fwAppEnabled;
+	BSTR							fwBstrName = NULL;
+	BSTR							fwBstrProcessImageFileName = NULL;
+	INetFwAuthorizedApplication	*	fwApp = NULL;
+	INetFwAuthorizedApplications*	fwApps = NULL;
+	OSStatus						err = S_OK;
+    
+	_ASSERT(fwProfile != NULL);
+    _ASSERT(fwProcessImageFileName != NULL);
+    _ASSERT(fwName != NULL);
+
+    // First check to see if the application is already authorized.
+	err = mDNSFirewallAppIsEnabled( fwProfile, fwProcessImageFileName, &fwAppEnabled );
+	require_noerr(err, exit);
+
+	// Only add the application if it isn't enabled
+
+	if (!fwAppEnabled)
+	{
+		// Get the list of authorized applications
+
+        err = fwProfile->get_AuthorizedApplications(&fwApps);
+		require(SUCCEEDED(err) && ( fwApps != NULL ), exit);
+
+        // Create an instance of an authorized application.
+
+		err = CoCreateInstance( __uuidof(NetFwAuthorizedApplication), NULL, CLSCTX_INPROC_SERVER, __uuidof(INetFwAuthorizedApplication), (void**)&fwApp );
+		require(SUCCEEDED(err) && ( fwApp != NULL ), exit);
+
+        fwBstrProcessImageFileName = SysAllocString(fwProcessImageFileName);
+		require_action(( fwProcessImageFileName != NULL ) && ( SysStringLen(fwBstrProcessImageFileName) > 0 ), exit, err = kNoMemoryErr);
+
+		// Set the executable file name
+
+		err = fwApp->put_ProcessImageFileName(fwBstrProcessImageFileName);
+		require(SUCCEEDED(err), exit);
+
+		fwBstrName = SysAllocString(fwName);
+		require_action( ( fwBstrName != NULL ) && ( SysStringLen(fwBstrName) > 0 ), exit, err = kNoMemoryErr);
+
+		// Set the friendly name
+
+        err = fwApp->put_Name(fwBstrName);
+		require(SUCCEEDED(err), exit);
+
+		// Now add the application
+
+        err = fwApps->Add(fwApp);
+		require(SUCCEEDED(err), exit);
+	}
+
+	err = kNoErr;
+
+exit:
+
+	// Deallocate the BSTR objects
+
+	if ( fwBstrName != NULL )
+	{
+		SysFreeString(fwBstrName);
+	}
+
+	if ( fwBstrProcessImageFileName != NULL )
+	{
+		SysFreeString(fwBstrProcessImageFileName);
+	}
+
+    // Release the COM objects
+
+    if (fwApp != NULL)
+    {
+        fwApp->Release();
+    }
+
+    if (fwApps != NULL)
+    {
+        fwApps->Release();
+    }
+
+    return err;
+}
+
+
+
+
+
+static OSStatus
+
+mDNSFirewallIsFileAndPrintSharingEnabled
+
+	(
+
+	IN INetFwProfile	* fwProfile,
+
+	OUT BOOL			* fwServiceEnabled
+
+	)
+
+{
+
+    VARIANT_BOOL fwEnabled;
+
+    INetFwService* fwService = NULL;
+
+    INetFwServices* fwServices = NULL;
+
+	OSStatus err = S_OK;
+
+
+
+    _ASSERT(fwProfile != NULL);
+
+    _ASSERT(fwServiceEnabled != NULL);
+
+
+
+    *fwServiceEnabled = FALSE;
+
+
+
+    // Retrieve the globally open ports collection.
+
+    err = fwProfile->get_Services(&fwServices);
+
+	require( SUCCEEDED( err ), exit );
+
+
+
+    // Attempt to retrieve the globally open port.
+
+    err = fwServices->Item(NET_FW_SERVICE_FILE_AND_PRINT, &fwService);
+
+	require( SUCCEEDED( err ), exit );
+
+	
+
+	// Find out if the globally open port is enabled.
+
+    err = fwService->get_Enabled(&fwEnabled);
+
+	require( SUCCEEDED( err ), exit );
+
+	if (fwEnabled != VARIANT_FALSE)
+
+	{
+
+		*fwServiceEnabled = TRUE;
+
+	}
+
+
+
+exit:
+
+
+
+    // Release the globally open port.
+
+    if (fwService != NULL)
+
+    {
+
+        fwService->Release();
+
+    }
+
+
+
+    // Release the globally open ports collection.
+
+    if (fwServices != NULL)
+
+    {
+
+        fwServices->Release();
+
+    }
+
+
+
+    return err;
+
+}
+
+
+OSStatus
+mDNSAddToFirewall
+		(
+		LPWSTR	executable,
+		LPWSTR	name
+		)
+{
+	INetFwProfile	*	fwProfile	= NULL;
+	HRESULT				comInit		= E_FAIL;
+	OSStatus			err			= kNoErr;
+
+	// Initialize COM.
+
+	comInit = CoInitializeEx( 0, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE );
+
+	// Ignore this case. RPC_E_CHANGED_MODE means that COM has already been
+	// initialized with a different mode.
+
+	if (comInit != RPC_E_CHANGED_MODE)
+	{
+		err = comInit;
+		require(SUCCEEDED(err), exit);
+	}
+
+	// Connect to the firewall
+
+	err = mDNSFirewallInitialize(&fwProfile);
+	require( SUCCEEDED( err ) && ( fwProfile != NULL ), exit);
+
+	// Add us to the list of exempt programs
+
+	err = mDNSFirewallAddApp( fwProfile, executable, name );
+	require_noerr(err, exit);
+
+exit:
+
+	// Disconnect from the firewall
+
+	if ( fwProfile != NULL )
+	{
+		mDNSFirewallCleanup(fwProfile);
+	}
+
+	// De-initialize COM
+
+	if (SUCCEEDED(comInit))
+    {
+        CoUninitialize();
+    }
+
+	return err;
+}
+
+
+BOOL
+mDNSIsFileAndPrintSharingEnabled( BOOL * retry )
+{
+	INetFwProfile	*	fwProfile					= NULL;
+	HRESULT				comInit						= E_FAIL;
+	BOOL				enabled						= FALSE;
+	OSStatus			err							= kNoErr;
+
+	// Initialize COM.
+
+	*retry = FALSE;
+	comInit = CoInitializeEx( 0, COINIT_APARTMENTTHREADED | COINIT_DISABLE_OLE1DDE );
+
+	// Ignore this case. RPC_E_CHANGED_MODE means that COM has already been
+	// initialized with a different mode.
+
+	if (comInit != RPC_E_CHANGED_MODE)
+	{
+		*retry = TRUE;
+		err = comInit;
+		require(SUCCEEDED(err), exit);
+	}
+
+	// Connect to the firewall
+
+	err = mDNSFirewallInitialize(&fwProfile);
+	require( SUCCEEDED( err ) && ( fwProfile != NULL ), exit);
+
+	err = mDNSFirewallIsFileAndPrintSharingEnabled( fwProfile, &enabled );
+	require_noerr( err, exit );
+
+exit:
+
+	// Disconnect from the firewall
+
+	if ( fwProfile != NULL )
+	{
+		mDNSFirewallCleanup(fwProfile);
+	}
+
+	// De-initialize COM
+
+	if (SUCCEEDED(comInit))
+    {
+        CoUninitialize();
+    }
+
+	return enabled;
+}
diff --git a/mdnsresponder/mDNSWindows/SystemService/Firewall.h b/mdnsresponder/mDNSWindows/SystemService/Firewall.h
new file mode 100755
index 0000000..3d7d532
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/SystemService/Firewall.h
@@ -0,0 +1,79 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+
+
+#ifndef _Firewall_h
+
+#define _Firewall_h
+
+
+
+
+
+#include "CommonServices.h"
+
+#include "DebugServices.h"
+
+
+
+
+
+#if defined(__cplusplus)
+
+extern "C"
+
+{
+
+#endif
+
+
+
+
+
+OSStatus
+
+mDNSAddToFirewall
+
+		(
+
+		LPWSTR	executable,
+
+		LPWSTR	name
+
+		);
+
+
+BOOL
+mDNSIsFileAndPrintSharingEnabled( BOOL * retry );
+
+
+
+
+
+#if defined(__cplusplus)
+
+}
+
+#endif
+
+
+
+
+
+#endif
+
diff --git a/mdnsresponder/mDNSWindows/SystemService/Prefix.h b/mdnsresponder/mDNSWindows/SystemService/Prefix.h
new file mode 100644
index 0000000..61381d0
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/SystemService/Prefix.h
@@ -0,0 +1,30 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+#ifndef __PREFIX__
+#define __PREFIX__
+
+#if( defined( _DEBUG ) )
+	#define	DEBUG					1
+	#define	MDNS_DEBUGMSGS			1
+#else
+	#define	DEBUG					0
+#endif
+
+#define	DNS_SD_CLIENT_ENABLED		0
+
+#endif	// __PREFIX__
diff --git a/mdnsresponder/mDNSWindows/SystemService/Service.aps b/mdnsresponder/mDNSWindows/SystemService/Service.aps
new file mode 100644
index 0000000..41f2556
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/SystemService/Service.aps
Binary files differ
diff --git a/mdnsresponder/mDNSWindows/SystemService/Service.c b/mdnsresponder/mDNSWindows/SystemService/Service.c
new file mode 100644
index 0000000..8b4c2c6
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/SystemService/Service.c
@@ -0,0 +1,2669 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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	<stdio.h>
+#include	<stdlib.h>
+#include	<crtdbg.h>
+#include	<stdarg.h>
+#include	<stddef.h>
+
+
+#include	"CommonServices.h"
+#include	"DebugServices.h"
+#include	"RegNames.h"
+
+#include	"uds_daemon.h"
+#include	"GenLinkedList.h"
+#include	"Service.h"
+#include	"EventLog.h"
+
+#include	"Resource.h"
+
+#include	"mDNSEmbeddedAPI.h"
+#include	"uDNS.h"
+#include	"mDNSWin32.h"
+
+#include	"Firewall.h"
+
+#if( !TARGET_OS_WINDOWS_CE )
+	#include	<mswsock.h>
+	#include	<process.h>
+	#include	<ipExport.h>
+	#include	<ws2def.h>
+	#include	<ws2ipdef.h>
+	#include	<iphlpapi.h>
+	#include	<netioapi.h>
+	#include	<iptypes.h>
+	#include	<powrprof.h>
+#endif
+
+#ifndef HeapEnableTerminationOnCorruption
+#	define HeapEnableTerminationOnCorruption (HEAP_INFORMATION_CLASS)1
+#endif
+
+#if 0
+#pragma mark == Constants ==
+#endif
+
+//===========================================================================================================================
+//	Constants
+//===========================================================================================================================
+
+#define	DEBUG_NAME							"[mDNSWin32] "
+#define kServiceFirewallName				L"Bonjour"
+#define	kServiceDependencies				TEXT("Tcpip\0\0")
+#define	kDNSServiceCacheEntryCountDefault	512
+#define kRetryFirewallPeriod				30 * 1000
+#define kDefValueSize						MAX_PATH + 1
+#define kZeroIndex							0
+#define kDefaultRouteMetric					399
+#define kSecondsTo100NSUnits				( 10 * 1000 * 1000 )
+#define kSPSMaintenanceWakePeriod			-30
+
+#define RR_CACHE_SIZE 500
+static CacheEntity gRRCache[RR_CACHE_SIZE];
+#if 0
+#pragma mark == Structures ==
+#endif
+
+//===========================================================================================================================
+//	Structures
+//===========================================================================================================================
+
+typedef struct EventSource
+{
+	HANDLE							event;
+	void						*	context;
+	RegisterWaitableEventHandler	handler;
+	struct EventSource			*	next;
+} EventSource;
+
+static BOOL											gEventSourceListChanged = FALSE;
+static EventSource								*	gEventSourceList = NULL;
+static EventSource								*	gCurrentSource = NULL;
+static int											gEventSources = 0;
+
+#define	kWaitListStopEvent							( WAIT_OBJECT_0 + 0 )
+#define	kWaitListInterfaceListChangedEvent			( WAIT_OBJECT_0 + 1 )
+#define kWaitListComputerDescriptionEvent			( WAIT_OBJECT_0 + 2 )
+#define kWaitListTCPIPEvent							( WAIT_OBJECT_0 + 3 )
+#define kWaitListDynDNSEvent						( WAIT_OBJECT_0 + 4 )
+#define kWaitListFileShareEvent						( WAIT_OBJECT_0 + 5 )
+#define kWaitListFirewallEvent						( WAIT_OBJECT_0 + 6 )
+#define kWaitListAdvertisedServicesEvent			( WAIT_OBJECT_0 + 7 )
+#define kWaitListSPSWakeupEvent						( WAIT_OBJECT_0 + 8 )
+#define kWaitListSPSSleepEvent						( WAIT_OBJECT_0 + 9 )
+#define	kWaitListFixedItemCount						10
+
+
+#if 0
+#pragma mark == Prototypes ==
+#endif
+
+//===========================================================================================================================
+//	Prototypes
+//===========================================================================================================================
+static void			Usage( void );
+static BOOL WINAPI	ConsoleControlHandler( DWORD inControlEvent );
+static OSStatus		InstallService( LPCTSTR inName, LPCTSTR inDisplayName, LPCTSTR inDescription, LPCTSTR inPath );
+static OSStatus		RemoveService( LPCTSTR inName );
+static OSStatus		SetServiceParameters();
+static OSStatus		GetServiceParameters();
+static OSStatus		CheckFirewall();
+static OSStatus		SetServiceInfo( SC_HANDLE inSCM, LPCTSTR inServiceName, LPCTSTR inDescription );
+static void			ReportStatus( int inType, const char *inFormat, ... );
+
+static void WINAPI	ServiceMain( DWORD argc, LPTSTR argv[] );
+static OSStatus		ServiceSetupEventLogging( void );
+static DWORD WINAPI	ServiceControlHandler( DWORD inControl, DWORD inEventType, LPVOID inEventData, LPVOID inContext );
+
+static OSStatus		ServiceRun( int argc, LPTSTR argv[] );
+static void			ServiceStop( void );
+
+static OSStatus		ServiceSpecificInitialize( int argc, LPTSTR  argv[] );
+static OSStatus		ServiceSpecificRun( int argc, LPTSTR argv[] );
+static OSStatus		ServiceSpecificStop( void );
+static void			ServiceSpecificFinalize( int argc, LPTSTR argv[] );
+static mStatus		SetupNotifications();
+static mStatus		TearDownNotifications();
+static mStatus		RegisterWaitableEvent( mDNS * const inMDNS, HANDLE event, void * context, RegisterWaitableEventHandler handler );
+static void			UnregisterWaitableEvent( mDNS * const inMDNS, HANDLE event );
+static mStatus		SetupWaitList( mDNS * const inMDNS, HANDLE **outWaitList, int *outWaitListCount );
+static void			UDSCanAccept( mDNS * const inMDNS, HANDLE event, void * context );
+static void			UDSCanRead( TCPSocket * sock );
+static void			HandlePowerSuspend( void * v );
+static void			HandlePowerResumeSuspend( void * v );
+static void			CoreCallback(mDNS * const inMDNS, mStatus result);
+static mDNSu8		SystemWakeForNetworkAccess( LARGE_INTEGER * timeout );
+static OSStatus		GetRouteDestination(DWORD * ifIndex, DWORD * address);
+static OSStatus		SetLLRoute( mDNS * const inMDNS );
+static bool			HaveRoute( PMIB_IPFORWARDROW rowExtant, unsigned long addr, unsigned long metric );
+static bool			IsValidAddress( const char * addr );
+static bool			IsNortelVPN( IP_ADAPTER_INFO * pAdapter );
+static bool			IsJuniperVPN( IP_ADAPTER_INFO * pAdapter );
+static bool			IsCiscoVPN( IP_ADAPTER_INFO * pAdapter );
+static const char * strnistr( const char * string, const char * subString, size_t max );
+
+#if defined(UNICODE)
+#	define StrLen(X)	wcslen(X)
+#	define StrCmp(X,Y)	wcscmp(X,Y)
+#else
+#	define StrLen(X)	strlen(X)
+#	define StrCmp(X,Y)	strcmp(X,Y)
+#endif
+
+
+#define kLLNetworkAddr      "169.254.0.0"
+#define kLLNetworkAddrMask  "255.255.0.0"
+
+
+#include	"mDNSEmbeddedAPI.h"
+
+#if 0
+#pragma mark == Globals ==
+#endif
+
+//===========================================================================================================================
+//	Globals
+//===========================================================================================================================
+#define gMDNSRecord mDNSStorage
+DEBUG_LOCAL	mDNS_PlatformSupport		gPlatformStorage;
+DEBUG_LOCAL BOOL						gServiceQuietMode		= FALSE;
+DEBUG_LOCAL SERVICE_TABLE_ENTRY			gServiceDispatchTable[] = 
+{
+	{ kServiceName,	ServiceMain }, 
+	{ NULL, 		NULL }
+};
+DEBUG_LOCAL SOCKET						gInterfaceListChangedSocket	= INVALID_SOCKET;
+DEBUG_LOCAL HANDLE						gInterfaceListChangedEvent	= NULL;
+DEBUG_LOCAL HKEY						gDescKey					= NULL;
+DEBUG_LOCAL HANDLE						gDescChangedEvent			= NULL;	// Computer description changed event
+DEBUG_LOCAL HKEY						gTcpipKey					= NULL;
+DEBUG_LOCAL HANDLE						gTcpipChangedEvent			= NULL;	// TCP/IP config changed
+DEBUG_LOCAL HKEY						gDdnsKey					= NULL;
+DEBUG_LOCAL HANDLE						gDdnsChangedEvent			= NULL;	// DynDNS config changed
+DEBUG_LOCAL HKEY						gFileSharingKey				= NULL;
+DEBUG_LOCAL HANDLE						gFileSharingChangedEvent	= NULL;	// File Sharing changed
+DEBUG_LOCAL HKEY						gFirewallKey				= NULL;
+DEBUG_LOCAL HANDLE						gFirewallChangedEvent		= NULL;	// Firewall changed
+DEBUG_LOCAL HKEY						gAdvertisedServicesKey		= NULL;
+DEBUG_LOCAL HANDLE						gAdvertisedServicesChangedEvent	= NULL; // Advertised services changed
+DEBUG_LOCAL SERVICE_STATUS				gServiceStatus;
+DEBUG_LOCAL SERVICE_STATUS_HANDLE		gServiceStatusHandle 	= NULL;
+DEBUG_LOCAL HANDLE						gServiceEventSource		= NULL;
+DEBUG_LOCAL bool						gServiceAllowRemote		= false;
+DEBUG_LOCAL int							gServiceCacheEntryCount	= 0;	// 0 means to use the DNS-SD default.
+DEBUG_LOCAL bool						gServiceManageLLRouting = true;
+DEBUG_LOCAL int							gWaitCount				= 0;
+DEBUG_LOCAL HANDLE					*	gWaitList				= NULL;
+DEBUG_LOCAL HANDLE						gStopEvent				= NULL;
+DEBUG_LOCAL HANDLE						gSPSWakeupEvent			= NULL;
+DEBUG_LOCAL HANDLE						gSPSSleepEvent			= NULL;
+DEBUG_LOCAL HANDLE						gUDSEvent				= NULL;
+DEBUG_LOCAL SocketRef					gUDSSocket				= 0;
+DEBUG_LOCAL udsEventCallback			gUDSCallback			= NULL;
+DEBUG_LOCAL BOOL						gRetryFirewall			= FALSE;
+DEBUG_LOCAL DWORD						gOSMajorVersion;
+DEBUG_LOCAL DWORD						gOSMinorVersion;
+
+typedef DWORD ( WINAPI * GetIpInterfaceEntryFunctionPtr )( PMIB_IPINTERFACE_ROW );
+mDNSlocal HMODULE								gIPHelperLibraryInstance		= NULL;
+mDNSlocal GetIpInterfaceEntryFunctionPtr		gGetIpInterfaceEntryFunctionPtr	= NULL;
+
+
+#if 0
+#pragma mark -
+#endif
+
+//===========================================================================================================================
+//	Main
+//===========================================================================================================================
+int	Main( int argc, LPTSTR argv[] )
+{
+	OSStatus		err;
+	BOOL			ok;
+	BOOL			start;
+	int				i;
+
+	HeapSetInformation( NULL, HeapEnableTerminationOnCorruption, NULL, 0 );
+
+	debug_initialize( kDebugOutputTypeMetaConsole );
+	debug_set_property( kDebugPropertyTagPrintLevel, kDebugLevelVerbose );
+
+	// Default to automatically starting the service dispatcher if no extra arguments are specified.
+	
+	start = ( argc <= 1 );
+	
+	// Parse arguments.
+	
+	for( i = 1; i < argc; ++i )
+	{
+		if( StrCmp( argv[ i ], TEXT("-install") ) == 0 )			// Install
+		{
+			TCHAR desc[ 256 ];
+			
+			desc[ 0 ] = 0;
+			LoadString( GetModuleHandle( NULL ), IDS_SERVICE_DESCRIPTION, desc, sizeof( desc ) );
+			err = InstallService( kServiceName, kServiceName, desc, argv[0] );
+			if( err )
+			{
+				ReportStatus( EVENTLOG_ERROR_TYPE, "install service failed (%d)\n", err );
+				goto exit;
+			}
+		}
+		else if( StrCmp( argv[ i ], TEXT("-remove") ) == 0 )		// Remove
+		{
+			err = RemoveService( kServiceName );
+			if( err )
+			{
+				ReportStatus( EVENTLOG_ERROR_TYPE, "remove service failed (%d)\n", err );
+				goto exit;
+			}
+		}
+		else if( StrCmp( argv[ i ], TEXT("-start") ) == 0 )		// Start
+		{
+			start = TRUE;
+		}
+		else if( StrCmp( argv[ i ], TEXT("-server") ) == 0 )		// Server
+		{
+			err = RunDirect( argc, argv );
+			if( err )
+			{
+				ReportStatus( EVENTLOG_ERROR_TYPE, "run service directly failed (%d)\n", err );
+			}
+			goto exit;
+		}
+		else if( StrCmp( argv[ i ], TEXT("-q") ) == 0 )			// Quiet Mode (toggle)
+		{
+			gServiceQuietMode = !gServiceQuietMode;
+		}
+		else if( ( StrCmp( argv[ i ], TEXT("-help") ) == 0 ) || 	// Help
+				 ( StrCmp( argv[ i ], TEXT("-h") ) == 0 ) )
+		{
+			Usage();
+			err = 0;
+			break;
+		}
+		else
+		{
+			Usage();
+			err = kParamErr;
+			break;
+		}
+	}
+	
+	// Start the service dispatcher if requested. This does not return until all services have terminated. If any 
+	// global initialization is needed, it should be done before starting the service dispatcher, but only if it 
+	// will take less than 30 seconds. Otherwise, use a separate thread for it and start the dispatcher immediately.
+	
+	if( start )
+	{
+		ok = StartServiceCtrlDispatcher( gServiceDispatchTable );
+		err = translate_errno( ok, (OSStatus) GetLastError(), kInUseErr );
+		if( err != kNoErr )
+		{
+			ReportStatus( EVENTLOG_ERROR_TYPE, "start service dispatcher failed (%d)\n", err );
+			goto exit;
+		}
+	}
+	err = 0;
+	
+exit:
+	dlog( kDebugLevelTrace, DEBUG_NAME "exited (%d %m)\n", err, err );
+	_CrtDumpMemoryLeaks();
+	return( (int) err );
+}
+
+//===========================================================================================================================
+//	Usage
+//===========================================================================================================================
+
+static void	Usage( void )
+{
+	fprintf( stderr, "\n" );
+	fprintf( stderr, "mDNSResponder 1.0d1\n" );
+	fprintf( stderr, "\n" );
+	fprintf( stderr, "    <no args>    Runs the service normally\n" );
+	fprintf( stderr, "    -install     Creates the service and starts it\n" );
+	fprintf( stderr, "    -remove      Stops the service and deletes it\n" );
+	fprintf( stderr, "    -start       Starts the service dispatcher after processing all other arguments\n" );
+	fprintf( stderr, "    -server      Runs the service directly as a server (for debugging)\n" );
+	fprintf( stderr, "    -q           Toggles Quiet Mode (no events or output)\n" );
+	fprintf( stderr, "    -remote      Allow remote connections\n" );
+	fprintf( stderr, "    -cache n     Number of mDNS cache entries (defaults to %d)\n", kDNSServiceCacheEntryCountDefault );
+	fprintf( stderr, "    -h[elp]      Display Help/Usage\n" );
+	fprintf( stderr, "\n" );
+}
+
+//===========================================================================================================================
+//	ConsoleControlHandler
+//===========================================================================================================================
+
+static BOOL WINAPI	ConsoleControlHandler( DWORD inControlEvent )
+{
+	BOOL			handled;
+	OSStatus		err;
+	
+	handled = FALSE;
+	switch( inControlEvent )
+	{
+		case CTRL_C_EVENT:
+		case CTRL_BREAK_EVENT:
+		case CTRL_CLOSE_EVENT:
+		case CTRL_LOGOFF_EVENT:
+		case CTRL_SHUTDOWN_EVENT:
+			err = ServiceSpecificStop();
+			require_noerr( err, exit );
+			
+			handled = TRUE;
+			break;
+		
+		default:
+			break;
+	}
+	
+exit:
+	return( handled );
+}
+
+//===========================================================================================================================
+//	InstallService
+//===========================================================================================================================
+
+static OSStatus	InstallService( LPCTSTR inName, LPCTSTR inDisplayName, LPCTSTR inDescription, LPCTSTR inPath )
+{
+	OSStatus		err;
+	SC_HANDLE		scm;
+	SC_HANDLE		service;
+	BOOL			ok;
+	TCHAR			fullPath[ MAX_PATH ];
+	TCHAR *			namePtr;
+	DWORD			size;
+	
+	scm		= NULL;
+	service = NULL;
+	
+	// Get a full path to the executable since a relative path may have been specified.
+	
+	size = GetFullPathName( inPath, MAX_PATH, fullPath, &namePtr );
+	err = translate_errno( size > 0, (OSStatus) GetLastError(), kPathErr );
+	require_noerr( err, exit );
+	
+	// Create the service and start it.
+	
+	scm = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );
+	err = translate_errno( scm, (OSStatus) GetLastError(), kOpenErr );
+	require_noerr( err, exit );
+	
+	service = CreateService( scm, inName, inDisplayName, SERVICE_ALL_ACCESS, SERVICE_WIN32_SHARE_PROCESS, 
+							 SERVICE_AUTO_START, SERVICE_ERROR_NORMAL, fullPath, NULL, NULL, kServiceDependencies, 
+							 NULL, NULL );
+	err = translate_errno( service, (OSStatus) GetLastError(), kDuplicateErr );
+	require_noerr( err, exit );
+
+	err = SetServiceParameters();
+	check_noerr( err );
+	
+	if( inDescription )
+	{
+		err = SetServiceInfo( scm, inName, inDescription );
+		check_noerr( err );
+	}
+
+	ok = StartService( service, 0, NULL );
+	err = translate_errno( ok, (OSStatus) GetLastError(), kInUseErr );
+	require_noerr( err, exit );
+	
+	ReportStatus( EVENTLOG_SUCCESS, "installed service\n" );
+	err = kNoErr;
+	
+exit:
+	if( service )
+	{
+		CloseServiceHandle( service );
+	}
+	if( scm )
+	{
+		CloseServiceHandle( scm );
+	}
+	return( err );
+}
+
+//===========================================================================================================================
+//	RemoveService
+//===========================================================================================================================
+
+static OSStatus	RemoveService( LPCTSTR inName )
+{
+	OSStatus			err;
+	SC_HANDLE			scm;
+	SC_HANDLE			service;
+	BOOL				ok;
+	SERVICE_STATUS		status;
+	
+	scm		= NULL;
+	service = NULL;
+	
+	// Open a connection to the service.
+	
+	scm = OpenSCManager( 0, 0, SC_MANAGER_ALL_ACCESS );
+	err = translate_errno( scm, (OSStatus) GetLastError(), kOpenErr );
+	require_noerr( err, exit );
+	
+	service = OpenService( scm, inName, SERVICE_STOP | SERVICE_QUERY_STATUS | DELETE );
+	err = translate_errno( service, (OSStatus) GetLastError(), kNotFoundErr );
+	require_noerr( err, exit );
+	
+	// Stop the service, if it is not already stopped, then delete it.
+	
+	ok = QueryServiceStatus( service, &status );
+	err = translate_errno( ok, (OSStatus) GetLastError(), kAuthenticationErr );
+	require_noerr( err, exit );
+	
+	if( status.dwCurrentState != SERVICE_STOPPED )
+	{
+		ok = ControlService( service, SERVICE_CONTROL_STOP, &status );
+		check_translated_errno( ok, (OSStatus) GetLastError(), kAuthenticationErr );
+	}
+	
+	ok = DeleteService( service );
+	err = translate_errno( ok, (OSStatus) GetLastError(), kDeletedErr );
+	require_noerr( err, exit );
+		
+	ReportStatus( EVENTLOG_SUCCESS, "Removed service\n" );
+	err = ERROR_SUCCESS;
+	
+exit:
+	if( service )
+	{
+		CloseServiceHandle( service );
+	}
+	if( scm )
+	{
+		CloseServiceHandle( scm );
+	}
+	return( err );
+}
+
+
+
+//===========================================================================================================================
+//	SetServiceParameters
+//===========================================================================================================================
+
+static OSStatus SetServiceParameters()
+{
+	DWORD 			value;
+	DWORD			valueLen = sizeof(DWORD);
+	DWORD			type;
+	OSStatus		err;
+	HKEY			key;
+
+	key = NULL;
+
+	//
+	// Add/Open Parameters section under service entry in registry
+	//
+	err = RegCreateKey( HKEY_LOCAL_MACHINE, kServiceParametersNode, &key );
+	require_noerr( err, exit );
+	
+	//
+	// If the value isn't already there, then we create it
+	//
+	err = RegQueryValueEx(key, kServiceManageLLRouting, 0, &type, (LPBYTE) &value, &valueLen);
+
+	if (err != ERROR_SUCCESS)
+	{
+		value = 1;
+
+		err = RegSetValueEx( key, kServiceManageLLRouting, 0, REG_DWORD, (const LPBYTE) &value, sizeof(DWORD) );
+		require_noerr( err, exit );
+	}
+
+exit:
+
+	if ( key )
+	{
+		RegCloseKey( key );
+	}
+
+	return( err );
+}
+
+
+
+//===========================================================================================================================
+//	GetServiceParameters
+//===========================================================================================================================
+
+static OSStatus GetServiceParameters()
+{
+	DWORD 			value;
+	DWORD			valueLen;
+	DWORD			type;
+	OSStatus		err;
+	HKEY			key;
+
+	key = NULL;
+
+	//
+	// Add/Open Parameters section under service entry in registry
+	//
+	err = RegCreateKey( HKEY_LOCAL_MACHINE, kServiceParametersNode, &key );
+	require_noerr( err, exit );
+	
+	valueLen = sizeof(DWORD);
+	err = RegQueryValueEx(key, kServiceManageLLRouting, 0, &type, (LPBYTE) &value, &valueLen);
+	if (err == ERROR_SUCCESS)
+	{
+		gServiceManageLLRouting = (value) ? true : false;
+	}
+
+	valueLen = sizeof(DWORD);
+	err = RegQueryValueEx(key, kServiceCacheEntryCount, 0, &type, (LPBYTE) &value, &valueLen);
+	if (err == ERROR_SUCCESS)
+	{
+		gServiceCacheEntryCount = value;
+	}
+
+exit:
+
+	if ( key )
+	{
+		RegCloseKey( key );
+	}
+
+	return( err );
+}
+
+
+//===========================================================================================================================
+//	CheckFirewall
+//===========================================================================================================================
+
+static OSStatus CheckFirewall()
+{
+	DWORD 					value;
+	DWORD					valueLen;
+	DWORD					type;
+	ENUM_SERVICE_STATUS	*	lpService = NULL;
+	SC_HANDLE				sc = NULL;
+	HKEY					key = NULL;
+	BOOL					ok;
+	DWORD					bytesNeeded = 0;
+	DWORD					srvCount;
+	DWORD					resumeHandle = 0;
+	DWORD					srvType;
+	DWORD					srvState;
+	DWORD					dwBytes = 0;
+	DWORD					i;
+	BOOL					isRunning = FALSE;
+	OSStatus				err = kUnknownErr;
+	
+	// Check to see if the firewall service is running.  If it isn't, then
+	// we want to return immediately
+
+	sc = OpenSCManager( NULL, NULL, SC_MANAGER_ENUMERATE_SERVICE );
+	err = translate_errno( sc, GetLastError(), kUnknownErr );
+	require_noerr( err, exit );
+
+	srvType		=	SERVICE_WIN32;
+	srvState	=	SERVICE_STATE_ALL;
+
+	for ( ;; )
+	{
+		// Call EnumServicesStatus using the handle returned by OpenSCManager
+
+		ok = EnumServicesStatus ( sc, srvType, srvState, lpService, dwBytes, &bytesNeeded, &srvCount, &resumeHandle );
+
+		if ( ok || ( GetLastError() != ERROR_MORE_DATA ) )
+		{
+			break;
+		}
+
+		if ( lpService )
+		{
+			free( lpService );
+		}
+
+		dwBytes = bytesNeeded;
+
+		lpService = ( ENUM_SERVICE_STATUS* ) malloc( dwBytes );
+		require_action( lpService, exit, err = mStatus_NoMemoryErr );
+	}
+
+	err = translate_errno( ok, GetLastError(), kUnknownErr );
+	require_noerr( err, exit );
+
+	for ( i = 0; i < srvCount; i++ )
+	{
+		if ( wcscmp( lpService[i].lpServiceName, L"SharedAccess" ) == 0 )
+		{
+			if ( lpService[i].ServiceStatus.dwCurrentState == SERVICE_RUNNING )
+			{
+				isRunning = TRUE;
+			}
+
+			break;
+		}
+	}
+
+	require_action( isRunning, exit, err = kUnknownErr );
+
+	// Check to see if we've managed the firewall.
+	// This package might have been installed, then
+	// the OS was upgraded to SP2 or above.  If that's
+	// the case, then we need to manipulate the firewall
+	// so networking works correctly.
+
+	err = RegCreateKey( HKEY_LOCAL_MACHINE, kServiceParametersNode, &key );
+	require_noerr( err, exit );
+
+	valueLen = sizeof(DWORD);
+	err = RegQueryValueEx(key, kServiceManageFirewall, 0, &type, (LPBYTE) &value, &valueLen);
+	
+	if ((err != ERROR_SUCCESS) || (value == 0))
+	{
+		wchar_t	fullPath[ MAX_PATH ];
+		DWORD	size;
+
+		// Get a full path to the executable
+
+		size = GetModuleFileNameW( NULL, fullPath, MAX_PATH );
+		err = translate_errno( size > 0, (OSStatus) GetLastError(), kPathErr );
+		require_noerr( err, exit );
+
+		err = mDNSAddToFirewall(fullPath, kServiceFirewallName);
+		require_noerr( err, exit );
+
+		value = 1;
+		err = RegSetValueEx( key, kServiceManageFirewall, 0, REG_DWORD, (const LPBYTE) &value, sizeof( DWORD ) );
+		require_noerr( err, exit );
+	}
+	
+exit:
+
+	if ( key )
+	{
+		RegCloseKey( key );
+	}
+	
+	if ( lpService )
+	{
+		free( lpService );
+	}
+
+	if ( sc )
+	{
+		CloseServiceHandle ( sc );
+	}
+
+	return( err );
+}
+
+
+
+//===========================================================================================================================
+//	SetServiceInfo
+//===========================================================================================================================
+
+static OSStatus	SetServiceInfo( SC_HANDLE inSCM, LPCTSTR inServiceName, LPCTSTR inDescription )
+{
+	OSStatus				err;
+	SC_LOCK					lock;
+	SC_HANDLE				service;
+	SERVICE_DESCRIPTION		description;
+	SERVICE_FAILURE_ACTIONS	actions;
+	SC_ACTION				action;
+	BOOL					ok;
+	
+	check( inServiceName );
+	check( inDescription );
+	
+	lock 	= NULL;
+	service	= NULL;
+	
+	// Open the database (if not provided) and lock it to prevent other access while re-configuring.
+	
+	if( !inSCM )
+	{
+		inSCM = OpenSCManager( NULL, NULL, SC_MANAGER_ALL_ACCESS );
+		err = translate_errno( inSCM, (OSStatus) GetLastError(), kOpenErr );
+		require_noerr( err, exit );
+	}
+	
+	lock = LockServiceDatabase( inSCM );
+	err = translate_errno( lock, (OSStatus) GetLastError(), kInUseErr );
+	require_noerr( err, exit );
+	
+	// Open a handle to the service. 
+
+	service = OpenService( inSCM, inServiceName, SERVICE_CHANGE_CONFIG|SERVICE_START );
+	err = translate_errno( service, (OSStatus) GetLastError(), kNotFoundErr );
+	require_noerr( err, exit );
+	
+	// Change the description.
+	
+	description.lpDescription = (LPTSTR) inDescription;
+	ok = ChangeServiceConfig2( service, SERVICE_CONFIG_DESCRIPTION, &description );
+	err = translate_errno( ok, (OSStatus) GetLastError(), kParamErr );
+	require_noerr( err, exit );
+	
+	actions.dwResetPeriod	=	INFINITE;
+	actions.lpRebootMsg		=	NULL;
+	actions.lpCommand		=	NULL;
+	actions.cActions		=	1;
+	actions.lpsaActions		=	&action;
+	action.Delay			=	500;
+	action.Type				=	SC_ACTION_RESTART;
+
+	ok = ChangeServiceConfig2( service, SERVICE_CONFIG_FAILURE_ACTIONS, &actions );
+	err = translate_errno( ok, (OSStatus) GetLastError(), kParamErr );
+	require_noerr( err, exit );
+	
+	err = ERROR_SUCCESS;
+	
+exit:
+	// Close the service and release the lock.
+	
+	if( service )
+	{
+		CloseServiceHandle( service );
+	}
+	if( lock )
+	{
+		UnlockServiceDatabase( lock ); 
+	}
+	return( err );
+}
+
+//===========================================================================================================================
+//	ReportStatus
+//===========================================================================================================================
+
+static void	ReportStatus( int inType, const char *inFormat, ... )
+{
+	if( !gServiceQuietMode )
+	{
+		va_list		args;
+		
+		va_start( args, inFormat );
+		if( gServiceEventSource )
+		{
+			char				s[ 1024 ];
+			BOOL				ok;
+			const char *		array[ 1 ];
+			
+			vsprintf( s, inFormat, args );
+			array[ 0 ] = s;
+			ok = ReportEventA( gServiceEventSource, (WORD) inType, 0, MDNSRESPONDER_LOG, NULL, 1, 0, array, NULL );
+			check_translated_errno( ok, GetLastError(), kUnknownErr );
+		}
+		else
+		{
+			int		n;
+			
+			n = vfprintf( stderr, inFormat, args );
+			check( n >= 0 );
+		}
+		va_end( args );
+	}
+}
+
+//===========================================================================================================================
+//	RunDirect
+//===========================================================================================================================
+
+int	RunDirect( int argc, LPTSTR argv[] )
+{
+	OSStatus		err;
+	BOOL			initialized;
+   BOOL        ok;
+	
+	initialized = FALSE;
+
+	// Install a Console Control Handler to handle things like control-c signals.
+	
+	ok = SetConsoleCtrlHandler( ConsoleControlHandler, TRUE );
+	err = translate_errno( ok, (OSStatus) GetLastError(), kUnknownErr );
+	require_noerr( err, exit );
+	
+	err = ServiceSpecificInitialize( argc, argv );
+	require_noerr( err, exit );
+	initialized = TRUE;
+	
+	// Run the service. This does not return until the service quits or is stopped.
+	
+	ReportStatus( EVENTLOG_INFORMATION_TYPE, "Running service directly\n" );
+	
+	err = ServiceSpecificRun( argc, argv );
+	require_noerr( err, exit );
+	
+	// Clean up.
+	
+exit:
+	if( initialized )
+	{
+		ServiceSpecificFinalize( argc, argv );
+	}
+	return( err );
+}
+
+#if 0
+#pragma mark -
+#endif
+
+//===========================================================================================================================
+//	ServiceMain
+//===========================================================================================================================
+
+static void WINAPI ServiceMain( DWORD argc, LPTSTR argv[] )
+{
+	OSStatus		err;
+	BOOL			ok;
+	
+	err = ServiceSetupEventLogging();
+	check_noerr( err );
+
+	err = GetServiceParameters();
+	check_noerr( err );
+	
+	// Initialize the service status and register the service control handler with the name of the service.
+	
+	gServiceStatus.dwServiceType 				= SERVICE_WIN32_SHARE_PROCESS;
+	gServiceStatus.dwCurrentState 				= 0;
+	gServiceStatus.dwControlsAccepted 			= SERVICE_ACCEPT_STOP|SERVICE_ACCEPT_SHUTDOWN|SERVICE_ACCEPT_POWEREVENT;
+	gServiceStatus.dwWin32ExitCode 				= NO_ERROR;
+	gServiceStatus.dwServiceSpecificExitCode 	= NO_ERROR;
+	gServiceStatus.dwCheckPoint 				= 0;
+	gServiceStatus.dwWaitHint 					= 0;
+	
+	gServiceStatusHandle = RegisterServiceCtrlHandlerEx( argv[ 0 ], ServiceControlHandler, NULL );
+	err = translate_errno( gServiceStatusHandle, (OSStatus) GetLastError(), kInUseErr );
+	require_noerr( err, exit );
+	
+	// Mark the service as starting.
+
+	gServiceStatus.dwCurrentState 	= SERVICE_START_PENDING;
+	gServiceStatus.dwCheckPoint	 	= 0;
+	gServiceStatus.dwWaitHint 		= 5000;	// 5 seconds
+	ok = SetServiceStatus( gServiceStatusHandle, &gServiceStatus );
+	check_translated_errno( ok, GetLastError(), kParamErr );
+	
+	// Run the service. This does not return until the service quits or is stopped.
+	
+	err = ServiceRun( (int) argc, argv );
+	if( err != kNoErr )
+	{
+		gServiceStatus.dwWin32ExitCode				= ERROR_SERVICE_SPECIFIC_ERROR;
+		gServiceStatus.dwServiceSpecificExitCode 	= (DWORD) err;
+	}
+	
+	// Service-specific work is done so mark the service as stopped.
+	
+	gServiceStatus.dwCurrentState = SERVICE_STOPPED;
+	ok = SetServiceStatus( gServiceStatusHandle, &gServiceStatus );
+	check_translated_errno( ok, GetLastError(), kParamErr );
+	
+	// Note: The service status handle should not be closed according to Microsoft documentation.
+	
+exit:
+	if( gServiceEventSource )
+	{
+		ok = DeregisterEventSource( gServiceEventSource );
+		check_translated_errno( ok, GetLastError(), kUnknownErr );
+		gServiceEventSource = NULL;
+	}
+}
+
+//===========================================================================================================================
+//	ServiceSetupEventLogging
+//===========================================================================================================================
+
+static OSStatus	ServiceSetupEventLogging( void )
+{
+	OSStatus			err;
+	HKEY				key;
+	LPCTSTR				s;
+	DWORD				typesSupported;
+	TCHAR				path[ MAX_PATH ];
+	DWORD 				n;
+	
+	key = NULL;
+	
+	// Add/Open source name as a sub-key under the Application key in the EventLog registry key.
+
+	s = TEXT("SYSTEM\\CurrentControlSet\\Services\\EventLog\\Application\\") kServiceName;
+	err = RegCreateKey( HKEY_LOCAL_MACHINE, s, &key );
+	require_noerr( err, exit );
+	
+	// Add the name to the EventMessageFile subkey.
+
+	path[ 0 ] = '\0';
+	GetModuleFileName( NULL, path, MAX_PATH );
+	n = (DWORD) ( ( StrLen( path ) + 1 ) * sizeof( TCHAR ) );
+	err = RegSetValueEx( key, TEXT("EventMessageFile"), 0, REG_EXPAND_SZ, (const LPBYTE) path, n );
+	require_noerr( err, exit );
+	
+	// Set the supported event types in the TypesSupported subkey.
+	
+	typesSupported = 0 
+					 | EVENTLOG_SUCCESS
+					 | EVENTLOG_ERROR_TYPE
+					 | EVENTLOG_WARNING_TYPE
+					 | EVENTLOG_INFORMATION_TYPE
+					 | EVENTLOG_AUDIT_SUCCESS
+					 | EVENTLOG_AUDIT_FAILURE; 
+	err = RegSetValueEx( key, TEXT("TypesSupported"), 0, REG_DWORD, (const LPBYTE) &typesSupported, sizeof( DWORD ) );
+	require_noerr( err, exit );
+	
+	// Set up the event source.
+	
+	gServiceEventSource = RegisterEventSource( NULL, kServiceName );
+	err = translate_errno( gServiceEventSource, (OSStatus) GetLastError(), kParamErr );
+	require_noerr( err, exit );
+		
+exit:
+	if( key )
+	{
+		RegCloseKey( key );
+	}
+	return( err );
+}
+
+//===========================================================================================================================
+//	HandlePowerSuspend
+//===========================================================================================================================
+
+static void HandlePowerSuspend( void * v )
+{
+	LARGE_INTEGER	timeout;
+	BOOL			ok;
+
+	( void ) v;
+
+	dlog( kDebugLevelInfo, DEBUG_NAME "HandlePowerSuspend\n" );
+
+	gMDNSRecord.SystemWakeOnLANEnabled = SystemWakeForNetworkAccess( &timeout );
+				
+	if ( gMDNSRecord.SystemWakeOnLANEnabled )
+	{
+		ok = SetWaitableTimer( gSPSWakeupEvent, &timeout, 0, NULL, NULL, TRUE );
+		check( ok );
+	}
+
+	mDNSCoreMachineSleep(&gMDNSRecord, TRUE);
+}
+
+
+//===========================================================================================================================
+//	HandlePowerResumeSuspend
+//===========================================================================================================================
+
+static void HandlePowerResumeSuspend( void * v )
+{
+	( void ) v;
+
+	dlog( kDebugLevelInfo, DEBUG_NAME "HandlePowerResumeSuspend\n" );
+
+	if ( gSPSWakeupEvent )
+	{
+		CancelWaitableTimer( gSPSWakeupEvent );
+	}
+
+	if ( gSPSSleepEvent )
+	{
+		CancelWaitableTimer( gSPSSleepEvent );
+	}
+
+	mDNSCoreMachineSleep(&gMDNSRecord, FALSE);
+}
+
+
+//===========================================================================================================================
+//	ServiceControlHandler
+//===========================================================================================================================
+
+static DWORD WINAPI	ServiceControlHandler( DWORD inControl, DWORD inEventType, LPVOID inEventData, LPVOID inContext )
+{
+	BOOL		setStatus;
+	BOOL		ok;
+
+	DEBUG_UNUSED( inEventData );
+	DEBUG_UNUSED( inContext );
+	
+	setStatus = TRUE;
+	switch( inControl )
+	{
+		case SERVICE_CONTROL_STOP:
+		case SERVICE_CONTROL_SHUTDOWN:
+			dlog( kDebugLevelInfo, DEBUG_NAME "ServiceControlHandler: SERVICE_CONTROL_STOP|SERVICE_CONTROL_SHUTDOWN\n" );
+			
+			ServiceStop();
+			setStatus = FALSE;
+			break;
+		
+		case SERVICE_CONTROL_POWEREVENT:
+
+			if (inEventType == PBT_APMSUSPEND)
+			{
+				dlog( kDebugLevelInfo, DEBUG_NAME "ServiceControlHandler: PBT_APMSUSPEND\n" );
+
+				QueueUserAPC( ( PAPCFUNC ) HandlePowerSuspend, gMDNSRecord.p->mainThread, ( ULONG_PTR ) NULL );
+			}
+			else if (inEventType == PBT_APMRESUMESUSPEND)
+			{
+				dlog( kDebugLevelInfo, DEBUG_NAME "ServiceControlHandler: PBT_APMRESUMESUSPEND\n" );
+
+				QueueUserAPC( ( PAPCFUNC ) HandlePowerResumeSuspend, gMDNSRecord.p->mainThread, ( ULONG_PTR ) NULL );
+			}
+		
+			break;
+
+		default:
+			dlog( kDebugLevelNotice, DEBUG_NAME "ServiceControlHandler: event (0x%08X)\n", inControl );
+			break;
+	}
+	
+	if( setStatus && gServiceStatusHandle )
+	{
+		ok = SetServiceStatus( gServiceStatusHandle, &gServiceStatus );
+		check_translated_errno( ok, GetLastError(), kUnknownErr );
+	}
+
+	return NO_ERROR;
+}
+
+//===========================================================================================================================
+//	ServiceRun
+//===========================================================================================================================
+
+static OSStatus	ServiceRun( int argc, LPTSTR argv[] )
+{
+	OSStatus		err;
+	BOOL			initialized;
+	BOOL			ok;
+	
+	DEBUG_UNUSED( argc );
+	DEBUG_UNUSED( argv );
+	
+	initialized = FALSE;
+	
+	// <rdar://problem/5727548> Make the service as running before we call ServiceSpecificInitialize. We've
+	// had reports that some machines with McAfee firewall installed cause a problem with iTunes installation.
+	// We think that the firewall product is interferring with code in ServiceSpecificInitialize. So as a
+	// simple workaround, we'll mark us as running *before* we call ServiceSpecificInitialize. This will unblock
+	// any installers that are waiting for our state to change.
+
+	gServiceStatus.dwCurrentState = SERVICE_RUNNING;
+	ok = SetServiceStatus( gServiceStatusHandle, &gServiceStatus );
+	check_translated_errno( ok, GetLastError(), kParamErr );
+
+	// Initialize the service-specific stuff
+	
+	err = ServiceSpecificInitialize( argc, argv );
+	require_noerr( err, exit );
+	initialized = TRUE;
+	
+	err = CheckFirewall();
+	check_noerr( err );
+
+	if ( err )
+	{
+		gRetryFirewall = TRUE;
+	}
+	
+	// Run the service-specific stuff. This does not return until the service quits or is stopped.
+	
+	ReportStatus( EVENTLOG_INFORMATION_TYPE, "Service started\n" );
+	err = ServiceSpecificRun( argc, argv );
+	ReportStatus( EVENTLOG_INFORMATION_TYPE, "Service stopped (%d)\n", err );
+	require_noerr( err, exit );
+	
+	// Service stopped. Clean up and we're done.
+	
+exit:
+	if( initialized )
+	{
+		ServiceSpecificFinalize( argc, argv );
+	}
+	return( err );
+}
+
+//===========================================================================================================================
+//	ServiceStop
+//===========================================================================================================================
+
+static void	ServiceStop( void )
+{
+	BOOL			ok;
+	OSStatus		err;
+	
+	// Signal the event to cause the service to exit.
+	
+	if( gServiceStatusHandle )
+	{
+		gServiceStatus.dwCurrentState = SERVICE_STOP_PENDING;
+		ok = SetServiceStatus( gServiceStatusHandle, &gServiceStatus );
+		check_translated_errno( ok, GetLastError(), kParamErr );
+	}
+		
+	err = ServiceSpecificStop();
+	check_noerr( err );
+}
+
+#if 0
+#pragma mark -
+#pragma mark == Service Specific ==
+#endif
+
+//===========================================================================================================================
+//	ServiceSpecificInitialize
+//===========================================================================================================================
+
+static OSStatus	ServiceSpecificInitialize( int argc, LPTSTR argv[] )
+{
+	OSVERSIONINFO osInfo;
+	OSStatus err;
+	BOOL ok;
+	
+	DEBUG_UNUSED( argc );
+	DEBUG_UNUSED( argv );
+	
+	mDNSPlatformMemZero( &gMDNSRecord, sizeof gMDNSRecord);
+	mDNSPlatformMemZero( &gPlatformStorage, sizeof gPlatformStorage);
+
+	gPlatformStorage.registerWaitableEventFunc = RegisterWaitableEvent;
+	gPlatformStorage.unregisterWaitableEventFunc = UnregisterWaitableEvent;
+	gPlatformStorage.reportStatusFunc = ReportStatus;
+
+	err = mDNS_Init( &gMDNSRecord, &gPlatformStorage, gRRCache, RR_CACHE_SIZE, mDNS_Init_AdvertiseLocalAddresses, CoreCallback, mDNS_Init_NoInitCallbackContext); 
+	require_noerr( err, exit);
+
+	err = SetupNotifications();
+	check_noerr( err );
+
+	err = udsserver_init(mDNSNULL, 0);
+	require_noerr( err, exit);
+
+	//
+	// Get the version of Windows that we're running on
+	//
+	osInfo.dwOSVersionInfoSize = sizeof( OSVERSIONINFO );
+	ok = GetVersionEx( &osInfo );
+	err = translate_errno( ok, (OSStatus) GetLastError(), kUnknownErr );
+	require_noerr( err, exit );
+	gOSMajorVersion = osInfo.dwMajorVersion;
+	gOSMinorVersion = osInfo.dwMinorVersion;
+
+	SetLLRoute( &gMDNSRecord );
+
+exit:
+	if( err != kNoErr )
+	{
+		ServiceSpecificFinalize( argc, argv );
+	}
+	return( err );
+}
+
+//===========================================================================================================================
+//	ServiceSpecificRun
+//===========================================================================================================================
+
+static OSStatus	ServiceSpecificRun( int argc, LPTSTR argv[] )
+{
+	HANDLE	*	waitList;
+	int			waitListCount;
+	DWORD		timeout;
+	DWORD		result;
+	BOOL		done;
+	mStatus		err;
+	
+	DEBUG_UNUSED( argc );
+	DEBUG_UNUSED( argv );
+
+	timeout = ( gRetryFirewall ) ? kRetryFirewallPeriod : INFINITE;
+
+	err = SetupInterfaceList( &gMDNSRecord );
+	check( !err );
+
+	err = uDNS_SetupDNSConfig( &gMDNSRecord );
+	check( !err );
+
+	done = FALSE;
+
+	// Main event loop.
+
+	while( !done )
+	{
+		waitList		= NULL;
+		waitListCount	= 0;
+
+		err = SetupWaitList( &gMDNSRecord, &waitList, &waitListCount );
+		require_noerr( err, exit );
+
+		gEventSourceListChanged = FALSE;
+
+		while ( !gEventSourceListChanged )
+		{
+			static mDNSs32 RepeatedBusy = 0;	
+			mDNSs32 nextTimerEvent;
+
+			// Give the mDNS core a chance to do its work and determine next event time.
+
+			nextTimerEvent = udsserver_idle( mDNS_Execute( &gMDNSRecord ) - mDNS_TimeNow( &gMDNSRecord ) );
+
+			if      ( nextTimerEvent < 0)					nextTimerEvent = 0;
+			else if ( nextTimerEvent > (0x7FFFFFFF / 1000))	nextTimerEvent = 0x7FFFFFFF / mDNSPlatformOneSecond;
+			else											nextTimerEvent = ( nextTimerEvent * 1000) / mDNSPlatformOneSecond;
+
+			// Debugging sanity check, to guard against CPU spins
+			
+			if ( nextTimerEvent > 0 )
+			{
+				RepeatedBusy = 0;
+			}
+			else
+			{
+				nextTimerEvent = 1;
+
+				if ( ++RepeatedBusy >= mDNSPlatformOneSecond )
+				{
+					ShowTaskSchedulingError( &gMDNSRecord );
+					RepeatedBusy = 0;
+				}
+			}
+
+			if ( gMDNSRecord.ShutdownTime )
+			{
+				mDNSs32 now = mDNS_TimeNow( &gMDNSRecord );
+
+				if ( mDNS_ExitNow( &gMDNSRecord, now ) )
+				{
+					mDNS_FinalExit( &gMDNSRecord );
+					done = TRUE;
+					break;
+				}
+
+				if ( nextTimerEvent - gMDNSRecord.ShutdownTime >= 0 )
+				{
+					nextTimerEvent = gMDNSRecord.ShutdownTime;
+				}
+			}
+
+			// Wait until something occurs (e.g. cancel, incoming packet, or timeout).
+			//
+			// Note: There seems to be a bug in WinSock with respect to Alertable I/O. According
+			// to MSDN <http://msdn.microsoft.com/en-us/library/aa363772(VS.85).aspx>, Alertable I/O
+			// callbacks will only be invoked during the following calls (when the caller sets
+			// the appropriate flag):
+			//
+			// - SleepEx
+			// - WaitForSingleObjectEx
+			// - WaitForMultipleObjectsEx
+			// - SignalObjectAndWait
+			// - MsgWaitForMultipleObjectsEx
+			//
+			// However, we have seen callbacks be invoked during calls to bind() (and maybe others). If there
+			// weren't a bug, then socket events would only be queued during the call to WaitForMultipleObjects() and
+			// we'd only have to check them once afterwards. However since that doesn't seem to be the case, we'll
+			// check the queue both before we call WaitForMultipleObjects() and after.
+
+			DispatchSocketEvents( &gMDNSRecord );
+			result = WaitForMultipleObjectsEx( ( DWORD ) waitListCount, waitList, FALSE, (DWORD) nextTimerEvent, TRUE );
+			check( result != WAIT_FAILED );
+			DispatchSocketEvents( &gMDNSRecord );
+
+			if ( result != WAIT_FAILED )
+			{
+				if ( result == WAIT_TIMEOUT )
+				{
+					// Next task timeout occurred. Loop back up to give mDNS core a chance to work.
+					
+					dlog( kDebugLevelChatty - 1, DEBUG_NAME "timeout\n" );
+					continue;
+				}
+				else if ( result == WAIT_IO_COMPLETION )
+				{
+					dlog( kDebugLevelChatty - 1, DEBUG_NAME "i/o completion\n" );
+					continue;
+				}
+				else if ( result == kWaitListStopEvent )
+				{
+					// Stop event. Set the done flag and break to exit.
+					
+					dlog( kDebugLevelVerbose, DEBUG_NAME "stopping...\n" );
+					udsserver_exit();
+					mDNS_StartExit( &gMDNSRecord );
+					break;
+				}
+				else if( result == kWaitListInterfaceListChangedEvent )
+				{
+					int		inBuffer;
+					int		outBuffer;
+					DWORD	outSize;
+
+					// It would be nice to come up with a more elegant solution to this, but it seems that
+					// GetAdaptersAddresses doesn't always stay in sync after network changed events.  So as
+					// as a simple workaround, we'll pause for a couple of seconds before processing the change.
+
+					// We arrived at 2 secs by trial and error. We could reproduce the problem after sleeping
+					// for 500 msec and 750 msec, but couldn't after sleeping for 1 sec.  We added another
+					// second on top of that to account for machine load or some other exigency.
+
+					Sleep( 2000 );
+
+					// Interface list changed event. Break out of the inner loop to re-setup the wait list.
+					
+					InterfaceListDidChange( &gMDNSRecord );
+
+					// reset the event handler
+					inBuffer	= 0;
+					outBuffer	= 0;
+					err = WSAIoctl( gInterfaceListChangedSocket, SIO_ADDRESS_LIST_CHANGE, &inBuffer, 0, &outBuffer, 0, &outSize, NULL, NULL );
+					if( err < 0 )
+					{
+						check( errno_compat() == WSAEWOULDBLOCK );
+					}
+				}
+				else if ( result == kWaitListComputerDescriptionEvent )
+				{
+					// The computer description might have changed
+					
+					ComputerDescriptionDidChange( &gMDNSRecord );
+					udsserver_handle_configchange( &gMDNSRecord );
+
+					// and reset the event handler
+					if ( ( gDescKey != NULL ) && ( gDescChangedEvent != NULL ) )
+					{
+						err = RegNotifyChangeKeyValue( gDescKey, TRUE, REG_NOTIFY_CHANGE_LAST_SET, gDescChangedEvent, TRUE);
+						check_noerr( err );
+					}
+				}
+				else if ( result == kWaitListTCPIPEvent )
+				{	
+					// The TCP/IP might have changed
+
+					TCPIPConfigDidChange( &gMDNSRecord );
+					udsserver_handle_configchange( &gMDNSRecord );
+
+					// and reset the event handler
+
+					if ( ( gTcpipKey != NULL ) && ( gTcpipChangedEvent ) )
+					{
+						err = RegNotifyChangeKeyValue( gTcpipKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, gTcpipChangedEvent, TRUE );
+						check_noerr( err );
+					}
+				}
+				else if ( result == kWaitListDynDNSEvent )
+				{
+					// The DynDNS config might have changed
+
+					DynDNSConfigDidChange( &gMDNSRecord );
+					udsserver_handle_configchange( &gMDNSRecord );
+
+					// and reset the event handler
+
+					if ((gDdnsKey != NULL) && (gDdnsChangedEvent))
+					{
+						err = RegNotifyChangeKeyValue(gDdnsKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, gDdnsChangedEvent, TRUE);
+						check_noerr( err );
+					}
+				}
+				else if ( result == kWaitListFileShareEvent )
+				{
+					// File sharing changed
+
+					FileSharingDidChange( &gMDNSRecord );
+
+					// and reset the event handler
+
+					if ((gFileSharingKey != NULL) && (gFileSharingChangedEvent))
+					{
+						err = RegNotifyChangeKeyValue(gFileSharingKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, gFileSharingChangedEvent, TRUE);
+						check_noerr( err );
+					}
+				}
+				else if ( result == kWaitListFirewallEvent )
+				{
+					// Firewall configuration changed
+
+					FirewallDidChange( &gMDNSRecord );
+
+					// and reset the event handler
+
+					if ((gFirewallKey != NULL) && (gFirewallChangedEvent))
+					{
+						err = RegNotifyChangeKeyValue(gFirewallKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, gFirewallChangedEvent, TRUE);
+						check_noerr( err );
+					}
+				}
+				else if ( result == kWaitListAdvertisedServicesEvent )
+				{
+					// Ultimately we'll want to manage multiple services, but right now the only service
+					// we'll be managing is SMB.
+
+					FileSharingDidChange( &gMDNSRecord );
+
+					// and reset the event handler
+
+					if ( ( gAdvertisedServicesKey != NULL ) && ( gAdvertisedServicesChangedEvent ) )
+					{
+						err = RegNotifyChangeKeyValue(gAdvertisedServicesKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, gAdvertisedServicesChangedEvent, TRUE);
+						check_noerr( err );
+					}
+				}
+				else if ( result == kWaitListSPSWakeupEvent )
+				{
+					LARGE_INTEGER timeout;
+
+					ReportStatus( EVENTLOG_INFORMATION_TYPE, "Maintenance wake" );
+
+					timeout.QuadPart  = kSPSMaintenanceWakePeriod;
+					timeout.QuadPart *= kSecondsTo100NSUnits;
+
+					SetWaitableTimer( gSPSSleepEvent, &timeout, 0, NULL, NULL, TRUE );
+				}
+				else if ( result == kWaitListSPSSleepEvent )
+				{
+					ReportStatus( EVENTLOG_INFORMATION_TYPE, "Returning to sleep after maintenance wake" );
+
+					// Calling SetSuspendState() doesn't invoke our sleep handlers, so we'll
+					// call HandlePowerSuspend() explicity.  This will reset the 
+					// maintenance wake timers.
+
+					HandlePowerSuspend( NULL );
+					SetSuspendState( FALSE, FALSE, FALSE );
+				}
+				else
+				{
+					int waitItemIndex;
+
+					waitItemIndex = (int)( ( (int) result ) - WAIT_OBJECT_0 );
+					dlog( kDebugLevelChatty, DEBUG_NAME "waitable event on %d\n", waitItemIndex );
+					check( ( waitItemIndex >= 0 ) && ( waitItemIndex < waitListCount ) );
+
+					if ( ( waitItemIndex >= 0 ) && ( waitItemIndex < waitListCount ) )
+					{
+						HANDLE	signaledEvent;
+						int		n = 0;
+						
+						signaledEvent = waitList[ waitItemIndex ];
+
+						// If gCurrentSource is not NULL, then this routine has been called
+						// re-entrantly which should never happen.
+
+						check( !gCurrentSource );
+
+						for ( gCurrentSource = gEventSourceList; gCurrentSource; )
+						{
+							EventSource * current = gCurrentSource;
+
+							if ( gCurrentSource->event == signaledEvent )
+							{
+								gCurrentSource->handler( &gMDNSRecord, gCurrentSource->event, gCurrentSource->context );
+								++n;
+								break;
+							}
+
+							// If the current node was removed as a result of calling
+							// the handler, then gCurrentSource was already incremented to
+							// the next node.  If it wasn't removed, then increment it
+							// ourselves
+
+							if ( gCurrentSource == current )
+							{
+								gCurrentSource = gCurrentSource->next;
+							}
+						}
+
+						gCurrentSource = NULL;
+
+						check( n > 0 );
+					}
+					else
+					{
+						dlog( kDebugLevelWarning, DEBUG_NAME "%s: unexpected wait result (result=0x%08X)\n", __ROUTINE__, result );
+					}
+				}
+			}
+			else
+			{
+				Sleep( 3 * 1000 );
+				
+				err = SetupInterfaceList( &gMDNSRecord );
+				check( !err );
+
+				err = uDNS_SetupDNSConfig( &gMDNSRecord );
+				check( !err );
+				
+				break;
+			}
+		}
+
+		if ( waitList )
+		{
+			free( waitList );
+			waitList = NULL;
+			waitListCount = 0;
+		}
+	}
+
+exit:
+
+	return( 0 );
+}
+
+//===========================================================================================================================
+//	ServiceSpecificStop
+//===========================================================================================================================
+
+static OSStatus	ServiceSpecificStop( void )
+{
+	OSStatus	err;
+	BOOL	 	ok;
+
+	ok = SetEvent(gStopEvent);
+	err = translate_errno( ok, (OSStatus) GetLastError(), kUnknownErr );
+	require_noerr( err, exit );
+exit:
+	return( err );
+}
+
+//===========================================================================================================================
+//	ServiceSpecificFinalize
+//===========================================================================================================================
+
+static void	ServiceSpecificFinalize( int argc, LPTSTR argv[] )
+{
+	DEBUG_UNUSED( argc );
+	DEBUG_UNUSED( argv );
+	
+	//
+	// clean up any open sessions
+	//
+	while ( gEventSourceList )
+	{
+		UnregisterWaitableEvent( &gMDNSRecord, gEventSourceList->event );
+	}
+
+	//
+	// clean up the notifications
+	//
+	TearDownNotifications();
+
+	//
+	// clean up loaded library
+	//
+
+	if( gIPHelperLibraryInstance )
+	{
+		gGetIpInterfaceEntryFunctionPtr = NULL;
+		
+		FreeLibrary( gIPHelperLibraryInstance );
+		gIPHelperLibraryInstance = NULL;
+	}
+}
+
+
+//===========================================================================================================================
+//	SetupNotifications
+//===========================================================================================================================
+
+mDNSlocal mStatus	SetupNotifications()
+{
+	mStatus				err;
+	SocketRef			sock;
+	unsigned long		param;
+	int					inBuffer;
+	int					outBuffer;
+	DWORD				outSize;
+	
+	gStopEvent	=	CreateEvent(NULL, FALSE, FALSE, NULL);
+	err = translate_errno( gStopEvent, errno_compat(), kNoResourcesErr );
+	require_noerr( err, exit );
+
+	// Register to listen for address list changes.
+	
+	gInterfaceListChangedEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
+	err = translate_errno( gInterfaceListChangedEvent, (mStatus) GetLastError(), kUnknownErr );
+	require_noerr( err, exit );
+
+	sock = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
+	err = translate_errno( IsValidSocket( sock ), errno_compat(), kUnknownErr );
+	require_noerr( err, exit );
+	gInterfaceListChangedSocket = sock;
+	
+	// Make the socket non-blocking so the WSAIoctl returns immediately with WSAEWOULDBLOCK. It will set the event 
+	// when a change to the interface list is detected.
+	
+	param = 1;
+	err = ioctlsocket( sock, FIONBIO, &param );
+	err = translate_errno( err == 0, errno_compat(), kUnknownErr );
+	require_noerr( err, exit );
+	
+	inBuffer	= 0;
+	outBuffer	= 0;
+	err = WSAIoctl( sock, SIO_ADDRESS_LIST_CHANGE, &inBuffer, 0, &outBuffer, 0, &outSize, NULL, NULL );
+	if( err < 0 )
+	{
+		check( errno_compat() == WSAEWOULDBLOCK );
+	}
+	
+	err = WSAEventSelect( sock, gInterfaceListChangedEvent, FD_ADDRESS_LIST_CHANGE );
+	err = translate_errno( err == 0, errno_compat(), kUnknownErr );
+	require_noerr( err, exit );
+
+	gDescChangedEvent = CreateEvent(NULL, TRUE, FALSE, NULL);
+	err = translate_errno( gDescChangedEvent, (mStatus) GetLastError(), kUnknownErr );
+	require_noerr( err, exit );
+
+	err = RegOpenKeyEx( HKEY_LOCAL_MACHINE, TEXT("SYSTEM\\CurrentControlSet\\Services\\lanmanserver\\parameters"), 0, KEY_READ, &gDescKey);
+	check_translated_errno( err == 0, errno_compat(), kNameErr );
+
+	if ( gDescKey != NULL )
+	{
+		err = RegNotifyChangeKeyValue( gDescKey, TRUE, REG_NOTIFY_CHANGE_LAST_SET, gDescChangedEvent, TRUE);
+		require_noerr( err, exit );
+	}
+
+	// This will catch all changes to tcp/ip networking, including changes to the domain search list
+
+	gTcpipChangedEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+	err = translate_errno( gTcpipChangedEvent, (mStatus) GetLastError(), kUnknownErr );
+	require_noerr( err, exit );
+
+	err = RegCreateKey( HKEY_LOCAL_MACHINE, TEXT("SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters"), &gTcpipKey );
+	require_noerr( err, exit );
+
+	err = RegNotifyChangeKeyValue( gTcpipKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, gTcpipChangedEvent, TRUE);
+	require_noerr( err, exit );
+
+	// This will catch all changes to ddns configuration
+
+	gDdnsChangedEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+	err = translate_errno( gDdnsChangedEvent, (mStatus) GetLastError(), kUnknownErr );
+	require_noerr( err, exit );
+
+	err = RegCreateKey( HKEY_LOCAL_MACHINE, kServiceParametersNode TEXT("\\DynDNS\\Setup"), &gDdnsKey );
+	require_noerr( err, exit );
+
+	err = RegNotifyChangeKeyValue( gDdnsKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, gDdnsChangedEvent, TRUE);
+	require_noerr( err, exit );
+
+	// This will catch all changes to file sharing
+
+	gFileSharingChangedEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
+	err = translate_errno( gFileSharingChangedEvent, (mStatus) GetLastError(), kUnknownErr );
+	require_noerr( err, exit );
+
+	err = RegCreateKey( HKEY_LOCAL_MACHINE, TEXT("SYSTEM\\CurrentControlSet\\Services\\lanmanserver\\Shares"), &gFileSharingKey );
+	
+	// Just to make sure that initialization doesn't fail on some old OS
+	// that doesn't have this key, we'll only add the notification if
+	// the key exists.
+
+	if ( !err )
+	{
+		err = RegNotifyChangeKeyValue( gFileSharingKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, gFileSharingChangedEvent, TRUE);
+		require_noerr( err, exit );
+	}
+	else
+	{
+		err = mStatus_NoError;
+	}
+
+	// This will catch changes to the Windows firewall
+
+	gFirewallChangedEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
+	err = translate_errno( gFirewallChangedEvent, (mStatus) GetLastError(), kUnknownErr );
+	require_noerr( err, exit );
+
+	// Just to make sure that initialization doesn't fail on some old OS
+	// that doesn't have this key, we'll only add the notification if
+	// the key exists.
+
+	err = RegCreateKey( HKEY_LOCAL_MACHINE, TEXT("SYSTEM\\CurrentControlSet\\Services\\SharedAccess\\Parameters\\FirewallPolicy\\FirewallRules"), &gFirewallKey );
+	
+	if ( !err )
+	{
+		err = RegNotifyChangeKeyValue( gFirewallKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, gFirewallChangedEvent, TRUE);
+		require_noerr( err, exit );
+	}
+	else
+	{
+		err = mStatus_NoError;
+	}
+
+	// This will catch all changes to advertised services configuration
+
+	gAdvertisedServicesChangedEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
+	err = translate_errno( gAdvertisedServicesChangedEvent, (mStatus) GetLastError(), kUnknownErr );
+	require_noerr( err, exit );
+
+	err = RegCreateKey( HKEY_LOCAL_MACHINE, kServiceParametersNode TEXT("\\Services"), &gAdvertisedServicesKey );
+	require_noerr( err, exit );
+
+	err = RegNotifyChangeKeyValue( gAdvertisedServicesKey, TRUE, REG_NOTIFY_CHANGE_NAME|REG_NOTIFY_CHANGE_LAST_SET, gAdvertisedServicesChangedEvent, TRUE);
+	require_noerr( err, exit );
+
+	gSPSWakeupEvent = CreateWaitableTimer( NULL, FALSE, NULL );
+	err = translate_errno( gSPSWakeupEvent, (mStatus) GetLastError(), kUnknownErr );
+	require_noerr( err, exit );
+
+	gSPSSleepEvent = CreateWaitableTimer( NULL, FALSE, NULL );
+	err = translate_errno( gSPSSleepEvent, (mStatus) GetLastError(), kUnknownErr );
+	require_noerr( err, exit );
+
+	gUDSEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
+	err = translate_errno( gUDSEvent, ( mStatus ) GetLastError(), kUnknownErr );
+	require_noerr( err, exit );
+
+exit:
+	if( err )
+	{
+		TearDownNotifications();
+	}
+	return( err );
+}
+
+//===========================================================================================================================
+//	TearDownNotifications
+//===========================================================================================================================
+
+mDNSlocal mStatus	TearDownNotifications()
+{
+	if ( gStopEvent )
+	{
+		CloseHandle( gStopEvent );
+		gStopEvent = NULL;
+	}
+
+	if( IsValidSocket( gInterfaceListChangedSocket ) )
+	{
+		close_compat( gInterfaceListChangedSocket );
+		gInterfaceListChangedSocket = kInvalidSocketRef;
+	}
+
+	if( gInterfaceListChangedEvent )
+	{
+		CloseHandle( gInterfaceListChangedEvent );
+		gInterfaceListChangedEvent = 0;
+	}
+
+	if ( gDescChangedEvent != NULL )
+	{
+		CloseHandle( gDescChangedEvent );
+		gDescChangedEvent = NULL;
+	}
+
+	if ( gDescKey != NULL )
+	{
+		RegCloseKey( gDescKey );
+		gDescKey = NULL;
+	}
+
+	if ( gTcpipChangedEvent != NULL )
+	{
+		CloseHandle( gTcpipChangedEvent );
+		gTcpipChangedEvent = NULL;
+	}
+
+	if ( gDdnsChangedEvent != NULL )
+	{
+		CloseHandle( gDdnsChangedEvent );
+		gDdnsChangedEvent = NULL;
+	}
+
+	if ( gDdnsKey != NULL )
+	{
+		RegCloseKey( gDdnsKey );
+		gDdnsKey = NULL;
+	}
+
+	if ( gFileSharingChangedEvent != NULL )
+	{
+		CloseHandle( gFileSharingChangedEvent );
+		gFileSharingChangedEvent = NULL;
+	}
+
+	if ( gFileSharingKey != NULL )
+	{
+		RegCloseKey( gFileSharingKey );
+		gFileSharingKey = NULL;
+	}
+
+	if ( gFirewallChangedEvent != NULL )
+	{
+		CloseHandle( gFirewallChangedEvent );
+		gFirewallChangedEvent = NULL;
+	}
+
+	if ( gFirewallKey != NULL )
+	{
+		RegCloseKey( gFirewallKey );
+		gFirewallKey = NULL;
+	}
+
+	if ( gAdvertisedServicesChangedEvent != NULL )
+	{
+		CloseHandle( gAdvertisedServicesChangedEvent );
+		gAdvertisedServicesChangedEvent = NULL;
+	}
+
+	if ( gAdvertisedServicesKey != NULL )
+	{
+		RegCloseKey( gAdvertisedServicesKey );
+		gAdvertisedServicesKey = NULL;
+	}
+
+	if ( gSPSWakeupEvent )
+	{
+		CloseHandle( gSPSWakeupEvent );
+		gSPSWakeupEvent = NULL;
+	}
+
+	if ( gSPSSleepEvent )
+	{
+		CloseHandle( gSPSSleepEvent );
+		gSPSSleepEvent = NULL;
+	}
+
+	return( mStatus_NoError );
+}
+
+
+//===========================================================================================================================
+//	RegisterWaitableEvent
+//===========================================================================================================================
+
+static mStatus RegisterWaitableEvent( mDNS * const inMDNS, HANDLE event, void * context, RegisterWaitableEventHandler handler )
+{
+	EventSource * source;
+	mStatus err = mStatus_NoError;
+
+	( void ) inMDNS;
+	check( event );
+	check( handler );
+
+	source = ( EventSource* ) malloc( sizeof( EventSource ) );
+	require_action( source, exit, err = mStatus_NoMemoryErr );
+	mDNSPlatformMemZero( source, sizeof( EventSource ) );
+	source->event = event;
+	source->context = context;
+	source->handler = handler;
+
+	source->next			= gEventSourceList;
+	gEventSourceList		= source;
+	gEventSourceListChanged	= TRUE;
+	gEventSources++;
+
+exit:
+
+	return err;
+}
+
+
+//===========================================================================================================================
+//	UnregisterWaitableEvent
+//===========================================================================================================================
+
+static void UnregisterWaitableEvent( mDNS * const inMDNS, HANDLE event )
+{
+	EventSource	*	current	= gEventSourceList;
+	EventSource	*	last	= NULL;
+
+	( void ) inMDNS;
+	check( event );
+
+	while ( current )
+	{
+		if ( current->event == event )
+		{
+			if ( last == NULL )
+			{
+				gEventSourceList = current->next;
+			}
+			else
+			{
+				last->next = current->next;
+			}
+
+			gEventSourceListChanged = TRUE;
+
+			// Protect against removing the node that we happen
+			// to be looking at as we iterate through the event
+			// source list in ServiceSpecificRun()
+
+			if ( current == gCurrentSource )
+			{
+				gCurrentSource = current->next;
+			}
+
+			gEventSources--;
+			free( current );
+
+			break;
+		}
+
+		last	= current;
+		current	= current->next;
+	}
+}
+
+
+//===========================================================================================================================
+//	SetupWaitList
+//===========================================================================================================================
+
+mDNSlocal mStatus SetupWaitList( mDNS * const inMDNS, HANDLE **outWaitList, int *outWaitListCount )
+{
+	int				waitListCount;
+	HANDLE		*	waitList;
+	HANDLE		*	waitItemPtr;
+	EventSource	*	source;
+	mStatus			err;
+	
+	dlog( kDebugLevelTrace, DEBUG_NAME "setting up wait list\n" );
+	
+	( void ) inMDNS;
+	check( inMDNS->p );
+	check( outWaitList );
+	check( outWaitListCount );
+	
+	// Allocate an array to hold all the objects to wait on.
+	
+	waitListCount = kWaitListFixedItemCount + gEventSources;
+	waitList = ( HANDLE* ) malloc( waitListCount * sizeof( *waitList ) );
+	require_action( waitList, exit, err = mStatus_NoMemoryErr );
+	waitItemPtr = waitList;
+	
+	// Add the fixed wait items to the beginning of the list.
+	
+	*waitItemPtr++	=	gStopEvent;
+	*waitItemPtr++	=	gInterfaceListChangedEvent;
+	*waitItemPtr++	=	gDescChangedEvent;
+	*waitItemPtr++	=	gTcpipChangedEvent;
+	*waitItemPtr++	=	gDdnsChangedEvent;
+	*waitItemPtr++	=	gFileSharingChangedEvent;
+	*waitItemPtr++	=	gFirewallChangedEvent;
+	*waitItemPtr++	=	gAdvertisedServicesChangedEvent;
+	*waitItemPtr++	=	gSPSWakeupEvent;
+	*waitItemPtr++	=	gSPSSleepEvent;
+
+	for ( source = gEventSourceList; source; source = source->next )
+	{
+		*waitItemPtr++ = source->event;
+	}
+
+	check( ( int )( waitItemPtr - waitList ) == waitListCount );
+	
+	*outWaitList 		= waitList;
+	*outWaitListCount	= waitListCount;
+	waitList			= NULL;
+	err					= mStatus_NoError;
+	
+exit:
+
+	if( waitList )
+	{
+		free( waitList );
+	}
+
+	dlog( kDebugLevelTrace, DEBUG_NAME "setting up wait list done (err=%d %m)\n", err, err );
+	return( err );
+}
+
+
+//===========================================================================================================================
+//	CoreCallback
+//===========================================================================================================================
+
+static void
+CoreCallback(mDNS * const inMDNS, mStatus status)
+{
+	if (status == mStatus_ConfigChanged)
+	{
+		SetLLRoute( inMDNS );
+	}
+}
+
+
+//===========================================================================================================================
+//	UDSCanAccept
+//===========================================================================================================================
+
+mDNSlocal void UDSCanAccept( mDNS * const inMDNS, HANDLE event, void * context )
+{
+	( void ) inMDNS;
+	( void ) event;
+	
+	if ( gUDSCallback )
+	{
+		gUDSCallback( ( int ) gUDSSocket, 0, context );
+	}
+}
+
+
+//===========================================================================================================================
+//	UDSCanRead
+//===========================================================================================================================
+
+mDNSlocal void UDSCanRead( TCPSocket * sock )
+{
+	udsEventCallback callback = ( udsEventCallback ) sock->userCallback;
+
+	if ( callback )
+	{
+		callback( (int) sock->fd, 0, sock->userContext );
+	}
+}
+
+
+//===========================================================================================================================
+//	udsSupportAddFDToEventLoop
+//===========================================================================================================================
+
+
+mStatus
+udsSupportAddFDToEventLoop( SocketRef fd, udsEventCallback callback, void *context, void **platform_data)
+{
+	mStatus err = mStatus_NoError;
+
+	// We are using some knowledge of what is being passed to us here.  If the fd is a listen socket,
+	// then the "callback" parameter is NULL.  If it is an actual read/write socket, then the "callback"
+	// parameter is not null. This is important because we use waitable events for the listen socket
+	// and alertable I/O for the read/write sockets.
+
+	if ( context )
+	{
+		TCPSocket * sock;
+
+		sock = malloc( sizeof( TCPSocket ) );
+		require_action( sock, exit, err = mStatus_NoMemoryErr );
+		mDNSPlatformMemZero( sock, sizeof( TCPSocket ) );
+
+		sock->fd				= (SOCKET) fd;
+		sock->readEventHandler	= UDSCanRead;
+		sock->userCallback		= callback;
+		sock->userContext		= context;
+		sock->m					= &gMDNSRecord;
+
+		err = TCPAddSocket( sock->m, sock );
+		require_noerr( err, exit );
+
+		*platform_data = sock;
+	}
+	else
+	{
+		gUDSSocket		= fd;
+		gUDSCallback	= callback;
+		gUDSEvent		= CreateEvent( NULL, FALSE, FALSE, NULL );
+		err = translate_errno( gUDSEvent, (mStatus) GetLastError(), kUnknownErr );
+		require_noerr( err, exit ); 
+		err = WSAEventSelect( fd, gUDSEvent, FD_ACCEPT | FD_CLOSE );
+		err = translate_errno( err == 0, WSAGetLastError(), kNoResourcesErr );
+		require_noerr( err, exit );
+		err = RegisterWaitableEvent( &gMDNSRecord, gUDSEvent, context, UDSCanAccept );
+		require_noerr( err, exit );
+	}
+
+exit:
+
+	return err;
+}
+
+
+int
+udsSupportReadFD( SocketRef fd, char *buf, int len, int flags, void *platform_data )
+{
+	TCPSocket	*	sock;
+	mDNSBool		closed;
+	int				ret;
+
+	( void ) flags;
+
+	sock = ( TCPSocket* ) platform_data;
+	require_action( sock, exit, ret = -1 );
+	require_action( sock->fd == fd, exit, ret = -1 );
+
+	ret = mDNSPlatformReadTCP( sock, buf, len, &closed );
+
+	if ( closed )
+	{
+		ret = 0;
+	}
+
+exit:
+
+	return ret;
+}
+
+
+mStatus
+udsSupportRemoveFDFromEventLoop( SocketRef fd, void *platform_data)		// Note: This also CLOSES the socket
+{
+	mStatus err = kNoErr;
+
+	if ( platform_data != NULL )
+	{
+		TCPSocket * sock;
+
+		dlog( kDebugLevelInfo, DEBUG_NAME "session closed\n" );
+		sock = ( TCPSocket* ) platform_data;
+		check( sock->fd == fd );
+		mDNSPlatformTCPCloseConnection( sock );
+	}
+	else if ( gUDSEvent != NULL )
+	{
+		UnregisterWaitableEvent( &gMDNSRecord, gUDSEvent );
+		WSAEventSelect( fd, gUDSEvent, 0 );
+		CloseHandle( gUDSEvent );
+		gUDSEvent = NULL;
+	}
+
+	return err;
+}
+
+
+mDNSexport void RecordUpdatedNiceLabel(mDNS *const m, mDNSs32 delay)
+	{
+	(void)m;
+	(void)delay;
+	// No-op, for now
+	}
+
+
+//===========================================================================================================================
+//	SystemWakeForNetworkAccess
+//===========================================================================================================================
+
+mDNSu8
+SystemWakeForNetworkAccess( LARGE_INTEGER * timeout )
+{
+	HKEY					key = NULL;
+	DWORD					dwSize;
+	DWORD					enabled;
+	mDNSu8					ok;
+	SYSTEM_POWER_STATUS		powerStatus;
+	time_t					startTime;
+	time_t					nextWakeupTime;
+	int						delta;
+	DWORD					err;
+
+	dlog( kDebugLevelInfo, DEBUG_NAME "SystemWakeForNetworkAccess\n" );
+
+	// Make sure we have a timer
+
+	require_action( gSPSWakeupEvent != NULL, exit, ok = FALSE );
+	require_action( gSPSSleepEvent != NULL, exit, ok = FALSE );
+
+	// Make sure the user enabled bonjour sleep proxy client 
+	
+	err = RegCreateKey( HKEY_LOCAL_MACHINE, kServiceParametersNode L"\\Power Management", &key );
+	require_action( !err, exit, ok = FALSE );
+	dwSize = sizeof( DWORD );
+	err = RegQueryValueEx( key, L"Enabled", NULL, NULL, (LPBYTE) &enabled, &dwSize );
+	require_action( !err, exit, ok = FALSE );
+	require_action( enabled, exit, ok = FALSE );
+	
+	// Make sure machine is on AC power
+	
+	ok = ( mDNSu8 ) GetSystemPowerStatus( &powerStatus );
+	require_action( ok, exit, ok = FALSE );
+	require_action( powerStatus.ACLineStatus == AC_LINE_ONLINE, exit, ok = FALSE );
+
+	// Now make sure we have a network interface that does wake-on-lan
+
+	ok = ( mDNSu8 ) IsWOMPEnabled( &gMDNSRecord );
+	require_action( ok, exit, ok = FALSE );
+
+	// Now make sure we have advertised services. Doesn't make sense to
+	// enable sleep proxy if we have no multicast services that could
+	// potentially wake us up.
+
+	ok = ( mDNSu8 ) mDNSCoreHaveAdvertisedMulticastServices( &gMDNSRecord );
+	require_action( ok, exit, ok = FALSE );
+
+	// Calculate next wake up time
+
+	startTime		= time( NULL );					// Seconds since midnight January 1, 1970
+	nextWakeupTime	= startTime + ( 120 * 60 );		// 2 hours later
+	
+	if ( gMDNSRecord.p->nextDHCPLeaseExpires < nextWakeupTime )
+	{
+		nextWakeupTime = gMDNSRecord.p->nextDHCPLeaseExpires;
+	}
+
+	// Finally calculate the next relative wakeup time
+
+	delta = ( int )( ( ( double )( nextWakeupTime - startTime ) ) * 0.9 );
+	ReportStatus( EVENTLOG_INFORMATION_TYPE, "enabling sleep proxy client with next maintenance wake in %d seconds", delta );
+
+	// Convert seconds to 100 nanosecond units expected by SetWaitableTimer
+
+	timeout->QuadPart  = -delta;
+	timeout->QuadPart *= kSecondsTo100NSUnits;
+
+	ok = TRUE;
+
+exit:
+
+	if ( key )
+	{
+		RegCloseKey( key );
+	}
+
+	return ok;
+}
+
+
+//===========================================================================================================================
+//	HaveRoute
+//===========================================================================================================================
+
+static bool
+HaveRoute( PMIB_IPFORWARDROW rowExtant, unsigned long addr, unsigned long metric )
+{
+	PMIB_IPFORWARDTABLE	pIpForwardTable	= NULL;
+	DWORD				dwSize			= 0;
+	BOOL				bOrder			= FALSE;
+	OSStatus			err;
+	bool				found			= false;
+	unsigned long int	i;
+
+	//
+	// Find out how big our buffer needs to be.
+	//
+	err = GetIpForwardTable(NULL, &dwSize, bOrder);
+	require_action( err == ERROR_INSUFFICIENT_BUFFER, exit, err = kUnknownErr );
+
+	//
+	// Allocate the memory for the table
+	//
+	pIpForwardTable = (PMIB_IPFORWARDTABLE) malloc( dwSize );
+	require_action( pIpForwardTable, exit, err = kNoMemoryErr );
+  
+	//
+	// Now get the table.
+	//
+	err = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder);
+	require_noerr( err, exit );
+
+	//
+	// Search for the row in the table we want.
+	//
+	for ( i = 0; i < pIpForwardTable->dwNumEntries; i++)
+	{
+		if ( ( pIpForwardTable->table[i].dwForwardDest == addr ) && ( !metric || ( pIpForwardTable->table[i].dwForwardMetric1 == metric ) ) )
+		{
+			memcpy( rowExtant, &(pIpForwardTable->table[i]), sizeof(*rowExtant) );
+			found = true;
+			break;
+		}
+	}
+
+exit:
+
+	if ( pIpForwardTable != NULL ) 
+	{
+		free(pIpForwardTable);
+	}
+    
+	return found;
+}
+
+
+//===========================================================================================================================
+//	IsValidAddress
+//===========================================================================================================================
+
+static bool
+IsValidAddress( const char * addr )
+{
+	return ( addr && ( strcmp( addr, "0.0.0.0" ) != 0 ) ) ? true : false;
+}	
+
+
+//===========================================================================================================================
+//	GetAdditionalMetric
+//===========================================================================================================================
+
+static ULONG
+GetAdditionalMetric( DWORD ifIndex )
+{
+	ULONG metric = 0;
+
+	if( !gIPHelperLibraryInstance )
+	{
+		gIPHelperLibraryInstance = LoadLibrary( TEXT( "Iphlpapi" ) );
+
+		gGetIpInterfaceEntryFunctionPtr = 
+				(GetIpInterfaceEntryFunctionPtr) GetProcAddress( gIPHelperLibraryInstance, "GetIpInterfaceEntry" );
+
+		if( !gGetIpInterfaceEntryFunctionPtr )
+		{		
+			BOOL ok;
+				
+			ok = FreeLibrary( gIPHelperLibraryInstance );
+			check_translated_errno( ok, GetLastError(), kUnknownErr );
+			gIPHelperLibraryInstance = NULL;
+		}
+	}
+
+	if ( gGetIpInterfaceEntryFunctionPtr )
+	{
+		MIB_IPINTERFACE_ROW row;
+		DWORD err;
+
+		ZeroMemory( &row, sizeof( MIB_IPINTERFACE_ROW ) );
+		row.Family = AF_INET;
+		row.InterfaceIndex = ifIndex;
+		err = gGetIpInterfaceEntryFunctionPtr( &row );
+		require_noerr( err, exit );
+		metric = row.Metric + 256;
+	}
+
+exit:
+
+	return metric;
+}
+
+
+//===========================================================================================================================
+//	SetLLRoute
+//===========================================================================================================================
+
+static OSStatus
+SetLLRoute( mDNS * const inMDNS )
+{
+	OSStatus err = kNoErr;
+
+	DEBUG_UNUSED( inMDNS );
+
+	//
+	// <rdar://problem/4096464> Don't call SetLLRoute on loopback
+	// <rdar://problem/6885843> Default route on Windows 7 breaks network connectivity
+	// 
+	// Don't mess w/ the routing table on Vista and later OSes, as 
+	// they have a permanent route to link-local addresses. Otherwise,
+	// set a route to link local addresses (169.254.0.0)
+	//
+	if ( ( gOSMajorVersion < 6 ) && gServiceManageLLRouting && !gPlatformStorage.registeredLoopback4 )
+	{
+		DWORD				ifIndex;
+		MIB_IPFORWARDROW	rowExtant;
+		bool				addRoute;
+		MIB_IPFORWARDROW	row;
+
+		ZeroMemory(&row, sizeof(row));
+
+		err = GetRouteDestination(&ifIndex, &row.dwForwardNextHop);
+		require_noerr( err, exit );
+		row.dwForwardDest		= inet_addr(kLLNetworkAddr);
+		row.dwForwardIfIndex	= ifIndex;
+		row.dwForwardMask		= inet_addr(kLLNetworkAddrMask);
+		row.dwForwardType		= 3;
+		row.dwForwardProto		= MIB_IPPROTO_NETMGMT;
+		row.dwForwardAge		= 0;
+		row.dwForwardPolicy		= 0;
+		row.dwForwardMetric1	= 20 + GetAdditionalMetric( ifIndex );
+		row.dwForwardMetric2	= (DWORD) - 1;
+		row.dwForwardMetric3	= (DWORD) - 1;
+		row.dwForwardMetric4	= (DWORD) - 1;
+		row.dwForwardMetric5	= (DWORD) - 1;
+
+		addRoute = true;
+
+		//
+		// check to make sure we don't already have a route
+		//
+		if ( HaveRoute( &rowExtant, inet_addr( kLLNetworkAddr ), 0 ) )
+		{
+			//
+			// set the age to 0 so that we can do a memcmp.
+			//
+			rowExtant.dwForwardAge = 0;
+
+			//
+			// check to see if this route is the same as our route
+			//
+			if (memcmp(&row, &rowExtant, sizeof(row)) != 0)
+			{
+				//
+				// if it isn't then delete this entry
+				//
+				DeleteIpForwardEntry(&rowExtant);
+			}
+			else
+			{
+				//
+				// else it is, so we don't want to create another route
+				//
+				addRoute = false;
+			}
+		}
+
+		if (addRoute && row.dwForwardNextHop)
+		{
+			err = CreateIpForwardEntry(&row);
+			check_noerr( err );
+		}
+	}
+
+exit:
+
+	return ( err );
+}
+
+
+//===========================================================================================================================
+//	GetRouteDestination
+//===========================================================================================================================
+
+static OSStatus
+GetRouteDestination(DWORD * ifIndex, DWORD * address)
+{
+	struct in_addr		ia;
+	IP_ADAPTER_INFO	*	pAdapterInfo	=	NULL;
+	IP_ADAPTER_INFO	*	pAdapter		=	NULL;
+	ULONG				bufLen;
+	mDNSBool			done			=	mDNSfalse;
+	OSStatus			err;
+
+	//
+	// GetBestInterface will fail if there is no default gateway
+	// configured.  If that happens, we will just take the first
+	// interface in the list. MSDN support says there is no surefire
+	// way to manually determine what the best interface might
+	// be for a particular network address.
+	//
+	ia.s_addr	=	inet_addr(kLLNetworkAddr);
+	err			=	GetBestInterface(*(IPAddr*) &ia, ifIndex);
+
+	if (err)
+	{
+		*ifIndex = 0;
+	}
+
+	//
+	// Make an initial call to GetAdaptersInfo to get
+	// the necessary size into the bufLen variable
+	//
+	err = GetAdaptersInfo( NULL, &bufLen);
+	require_action( err == ERROR_BUFFER_OVERFLOW, exit, err = kUnknownErr );
+
+	pAdapterInfo = (IP_ADAPTER_INFO*) malloc( bufLen );
+	require_action( pAdapterInfo, exit, err = kNoMemoryErr );
+	
+	err = GetAdaptersInfo( pAdapterInfo, &bufLen);
+	require_noerr( err, exit );
+	
+	pAdapter	=	pAdapterInfo;
+	err			=	kUnknownErr;
+			
+	// <rdar://problem/3718122>
+	// <rdar://problem/5652098>
+	//
+	// Look for the Nortel VPN virtual interface, along with Juniper virtual interface.
+	//
+	// If these interfaces are active (i.e., has a non-zero IP Address),
+	// then we want to disable routing table modifications.
+
+	while (pAdapter)
+	{
+		if ( ( IsNortelVPN( pAdapter ) || IsJuniperVPN( pAdapter ) || IsCiscoVPN( pAdapter ) ) &&
+			 ( inet_addr( pAdapter->IpAddressList.IpAddress.String ) != 0 ) )
+		{
+			dlog( kDebugLevelTrace, DEBUG_NAME "disabling routing table management due to VPN incompatibility" );
+			goto exit;
+		}
+
+		pAdapter = pAdapter->Next;
+	}
+
+	while ( !done )
+	{
+		pAdapter	=	pAdapterInfo;
+		err			=	kUnknownErr;
+
+		while (pAdapter)
+		{
+			// If we don't have an interface selected, choose the first one that is of type ethernet and
+			// has a valid IP Address
+
+			if ((pAdapter->Type == MIB_IF_TYPE_ETHERNET) && ( IsValidAddress( pAdapter->IpAddressList.IpAddress.String ) ) && (!(*ifIndex) || (pAdapter->Index == (*ifIndex))))
+			{
+				*address =	inet_addr( pAdapter->IpAddressList.IpAddress.String );
+				*ifIndex =  pAdapter->Index;
+				err		 =	kNoErr;
+				break;
+			}
+		
+			pAdapter = pAdapter->Next;
+		}
+
+		// If we found the right interface, or we weren't trying to find a specific interface then we're done
+
+		if ( !err || !( *ifIndex) )
+		{
+			done = mDNStrue;
+		}
+
+		// Otherwise, try again by wildcarding the interface
+
+		else
+		{
+			*ifIndex = 0;
+		}
+	} 
+
+exit:
+
+	if ( pAdapterInfo != NULL )
+	{
+		free( pAdapterInfo );
+	}
+
+	return( err );
+}
+
+
+static bool
+IsNortelVPN( IP_ADAPTER_INFO * pAdapter )
+{
+	return ((pAdapter->Type == MIB_IF_TYPE_ETHERNET) &&
+		    (pAdapter->AddressLength == 6) &&
+		    (pAdapter->Address[0] == 0x44) &&
+		    (pAdapter->Address[1] == 0x45) &&
+		    (pAdapter->Address[2] == 0x53) &&
+		    (pAdapter->Address[3] == 0x54) &&
+		    (pAdapter->Address[4] == 0x42) &&
+			(pAdapter->Address[5] == 0x00)) ? true : false;
+}
+
+
+static bool
+IsJuniperVPN( IP_ADAPTER_INFO * pAdapter )
+{	
+	return ( strnistr( pAdapter->Description, "Juniper", sizeof( pAdapter->Description  ) ) != NULL ) ? true : false;
+}
+
+
+static bool
+IsCiscoVPN( IP_ADAPTER_INFO * pAdapter )
+{
+	return ((pAdapter->Type == MIB_IF_TYPE_ETHERNET) &&
+		    (pAdapter->AddressLength == 6) &&
+		    (pAdapter->Address[0] == 0x00) &&
+		    (pAdapter->Address[1] == 0x05) &&
+		    (pAdapter->Address[2] == 0x9a) &&
+		    (pAdapter->Address[3] == 0x3c) &&
+		    (pAdapter->Address[4] == 0x7a) &&
+			(pAdapter->Address[5] == 0x00)) ? true : false;
+}
+
+
+static const char *
+strnistr( const char * string, const char * subString, size_t max )
+{
+	size_t       subStringLen;
+	size_t       offset;
+	size_t       maxOffset;
+	size_t       stringLen;
+	const char * pPos;
+
+	if ( ( string == NULL ) || ( subString == NULL ) )
+	{
+		return string;
+	}
+
+	stringLen = ( max > strlen( string ) ) ? strlen( string ) : max;
+
+	if ( stringLen == 0 )
+	{
+		return NULL;
+	}
+	
+	subStringLen = strlen( subString );
+
+	if ( subStringLen == 0 )
+	{
+		return string;
+	}
+
+	if ( subStringLen > stringLen )
+	{
+		return NULL;
+	}
+
+	maxOffset = stringLen - subStringLen;
+	pPos      = string;
+
+	for ( offset = 0; offset <= maxOffset; offset++ )
+	{
+		if ( _strnicmp( pPos, subString, subStringLen ) == 0 )
+		{
+			return pPos;
+		}
+
+		pPos++;
+	}
+
+	return NULL;
+}
+
diff --git a/mdnsresponder/mDNSWindows/SystemService/Service.h b/mdnsresponder/mDNSWindows/SystemService/Service.h
new file mode 100755
index 0000000..0b806f0
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/SystemService/Service.h
@@ -0,0 +1,30 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+#ifndef	__MDNS_SERVICE_H__
+#define	__MDNS_SERVICE_H__
+
+
+#include <windows.h>
+
+
+extern int	RunDirect( int argc, LPTSTR argv[] );
+extern int	Main( int argc, LPTSTR argv[] );
+
+
+#endif
+
diff --git a/mdnsresponder/mDNSWindows/SystemService/Service.mcp b/mdnsresponder/mDNSWindows/SystemService/Service.mcp
new file mode 100644
index 0000000..ca15566
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/SystemService/Service.mcp
Binary files differ
diff --git a/mdnsresponder/mDNSWindows/SystemService/Service.rc b/mdnsresponder/mDNSWindows/SystemService/Service.rc
new file mode 100644
index 0000000..60ad484
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/SystemService/Service.rc
@@ -0,0 +1,114 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "winres.h"
+#include "WinVersRes.h"
+#include "EventLog.rc"
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION MASTER_PROD_VERS
+ PRODUCTVERSION MASTER_PROD_VERS
+ FILEFLAGSMASK 0x3fL
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x1L
+ FILESUBTYPE 0x0L
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "040904b0"
+        BEGIN
+            VALUE "CompanyName", MASTER_COMPANY_NAME
+            VALUE "FileDescription", "Bonjour Service"
+            VALUE "FileVersion", MASTER_PROD_VERS_STR
+            VALUE "InternalName", "mDNSResponder.exe"
+            VALUE "LegalCopyright", MASTER_LEGAL_COPYRIGHT
+            VALUE "OriginalFilename", "mDNSResponder.exe"
+            VALUE "ProductName", MASTER_PROD_NAME
+            VALUE "ProductVersion", MASTER_PROD_VERS_STR
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x409, 1200
+    END
+END
+
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE 
+BEGIN
+    "resource.h\0"
+END
+
+2 TEXTINCLUDE 
+BEGIN
+    "#include ""winres.h""\r\n"
+    "#include ""WinVersRes.h""\0"
+END
+
+3 TEXTINCLUDE 
+BEGIN
+    "\r\n"
+    "\0"
+END
+
+#endif    // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// String Table
+//
+
+STRINGTABLE 
+BEGIN
+    IDS_SERVICE_DESCRIPTION "Enables hardware devices and software services to automatically configure themselves on the network and advertise their presence, so that users can discover and use those services without any unnecessary manual setup or administration."
+END
+
+#endif    // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif    // not APSTUDIO_INVOKED
+
diff --git a/mdnsresponder/mDNSWindows/SystemService/Service.vcproj b/mdnsresponder/mDNSWindows/SystemService/Service.vcproj
new file mode 100644
index 0000000..720f00a
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/SystemService/Service.vcproj
@@ -0,0 +1,562 @@
+<?xml version="1.0" encoding="Windows-1252"?>

+<VisualStudioProject

+	ProjectType="Visual C++"

+	Version="8.00"

+	Name="mDNSResponder"

+	ProjectGUID="{C1D98254-BA27-4427-A3BE-A68CA2CC5F69}"

+	RootNamespace="mDNSResponder"

+	Keyword="Win32Proj"

+	>

+	<Platforms>

+		<Platform

+			Name="Win32"

+		/>

+		<Platform

+			Name="x64"

+		/>

+	</Platforms>

+	<ToolFiles>

+	</ToolFiles>

+	<Configurations>

+		<Configuration

+			Name="Debug|Win32"

+			OutputDirectory="$(PlatformName)\$(ConfigurationName)"

+			IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"

+			ConfigurationType="1"

+			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"

+			CharacterSet="2"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				Optimization="0"

+				AdditionalIncludeDirectories=".;../;../../mDNSCore;../../mDNSShared;&quot;$(VCInstallDir)include&quot;;&quot;$(VCInstallDir)atlmfc\include&quot;;&quot;C:/Program Files/Microsoft SDKs/Windows/v6.1/Include&quot;"

+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;_WIN32_WINNT=0x0501;DEBUG=1;MDNS_DEBUGMSGS=0;TARGET_OS_WIN32;WIN32_LEAN_AND_MEAN;USE_TCP_LOOPBACK;PLATFORM_NO_STRSEP;PLATFORM_NO_EPIPE;PLATFORM_NO_RLIMIT;PID_FILE=&quot;&quot;&quot;&quot;;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1;_LEGACY_NAT_TRAVERSAL_;_USE_32BIT_TIME_T"

+				StringPooling="true"

+				MinimalRebuild="true"

+				ExceptionHandling="0"

+				BasicRuntimeChecks="3"

+				SmallerTypeCheck="true"

+				RuntimeLibrary="1"

+				BufferSecurityCheck="true"

+				UsePrecompiledHeader="0"

+				AssemblerListingLocation="$(IntDir)\"

+				WarningLevel="4"

+				Detect64BitPortabilityProblems="true"

+				DebugInformationFormat="3"

+				CallingConvention="2"

+				DisableSpecificWarnings="4127;4201"

+				ShowIncludes="false"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+				AdditionalIncludeDirectories="../"

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				AdditionalOptions="/NXCOMPAT /DYNAMICBASE /SAFESEH"

+				AdditionalDependencies="ws2_32.lib iphlpapi.lib crypt32.lib netapi32.lib powrprof.lib"

+				OutputFile="$(OutDir)/mDNSResponder.exe"

+				LinkIncremental="2"

+				IgnoreAllDefaultLibraries="false"

+				GenerateDebugInformation="true"

+				ProgramDatabaseFile="$(OutDir)/mDNSResponder.pdb"

+				SubSystem="1"

+				TargetMachine="1"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+				AdditionalManifestFiles="res\mDNSResponder.manifest"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+			/>

+		</Configuration>

+		<Configuration

+			Name="Debug|x64"

+			OutputDirectory="$(PlatformName)\$(ConfigurationName)"

+			IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"

+			ConfigurationType="1"

+			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"

+			CharacterSet="2"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+				TargetEnvironment="3"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				Optimization="0"

+				AdditionalIncludeDirectories=".;../;../../mDNSCore;../../mDNSShared;&quot;$(VCInstallDir)include&quot;;&quot;$(VCInstallDir)atlmfc\include&quot;;&quot;C:/Program Files/Microsoft SDKs/Windows/v6.1/Include&quot;"

+				PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;_WIN32_WINNT=0x0501;DEBUG=1;MDNS_DEBUGMSGS=0;TARGET_OS_WIN32;WIN32_LEAN_AND_MEAN;USE_TCP_LOOPBACK;PLATFORM_NO_STRSEP;PLATFORM_NO_EPIPE;PLATFORM_NO_RLIMIT;PID_FILE=&quot;&quot;&quot;&quot;;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1;_LEGACY_NAT_TRAVERSAL_"

+				StringPooling="true"

+				MinimalRebuild="true"

+				ExceptionHandling="0"

+				BasicRuntimeChecks="3"

+				SmallerTypeCheck="true"

+				RuntimeLibrary="1"

+				BufferSecurityCheck="true"

+				UsePrecompiledHeader="0"

+				AssemblerListingLocation="$(IntDir)\"

+				WarningLevel="4"

+				Detect64BitPortabilityProblems="true"

+				DebugInformationFormat="3"

+				CallingConvention="2"

+				DisableSpecificWarnings="4127;4201"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+				AdditionalIncludeDirectories="../"

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				AdditionalOptions="/NXCOMPAT /DYNAMICBASE"

+				AdditionalDependencies="ws2_32.lib iphlpapi.lib crypt32.lib netapi32.lib powrprof.lib"

+				OutputFile="$(OutDir)/mDNSResponder.exe"

+				LinkIncremental="2"

+				IgnoreAllDefaultLibraries="false"

+				GenerateDebugInformation="true"

+				ProgramDatabaseFile="$(OutDir)/mDNSResponder.pdb"

+				SubSystem="1"

+				TargetMachine="17"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+				AdditionalManifestFiles="res\mDNSResponder64.manifest"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+			/>

+		</Configuration>

+		<Configuration

+			Name="Release|Win32"

+			OutputDirectory="$(PlatformName)\$(ConfigurationName)"

+			IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"

+			ConfigurationType="1"

+			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"

+			CharacterSet="2"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				AdditionalIncludeDirectories=".;../;../../mDNSCore;../../mDNSShared;&quot;$(VCInstallDir)include&quot;;&quot;$(VCInstallDir)atlmfc\include&quot;;&quot;C:/Program Files/Microsoft SDKs/Windows/v6.1/Include&quot;"

+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;_WIN32_WINNT=0x0501;TARGET_OS_WIN32;WIN32_LEAN_AND_MEAN;USE_TCP_LOOPBACK;PLATFORM_NO_STRSEP;PLATFORM_NO_EPIPE;PLATFORM_NO_RLIMIT;PID_FILE=&quot;&quot;&quot;&quot;;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1;_LEGACY_NAT_TRAVERSAL_;_USE_32BIT_TIME_T"

+				RuntimeLibrary="0"

+				UsePrecompiledHeader="0"

+				AssemblerListingLocation="$(IntDir)\"

+				WarningLevel="4"

+				Detect64BitPortabilityProblems="true"

+				DebugInformationFormat="3"

+				CallingConvention="2"

+				DisableSpecificWarnings="4127;4201"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+				AdditionalIncludeDirectories="../"

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				AdditionalOptions="/NXCOMPAT /DYNAMICBASE /SAFESEH"

+				AdditionalDependencies="ws2_32.lib iphlpapi.lib crypt32.lib netapi32.lib powrprof.lib"

+				OutputFile="$(OutDir)/mDNSResponder.exe"

+				LinkIncremental="1"

+				GenerateDebugInformation="true"

+				ProgramDatabaseFile="$(IntDir)/$(ProjectName).pdb"

+				SubSystem="1"

+				OptimizeReferences="2"

+				EnableCOMDATFolding="2"

+				TargetMachine="1"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+				AdditionalManifestFiles="res\mDNSResponder.manifest"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+				CommandLine="if not &quot;%RC_XBS%&quot; == &quot;YES&quot; goto END&#x0D;&#x0A;if not exist &quot;$(DSTROOT)\Program Files\Bonjour\$(PlatformName)&quot;   mkdir &quot;$(DSTROOT)\Program Files\Bonjour\$(PlatformName)&quot;&#x0D;&#x0A;if not exist &quot;$(DSTROOT)\AppleInternal&quot;                                                   mkdir &quot;$(DSTROOT)\AppleInternal&quot;&#x0D;&#x0A;if not exist &quot;$(DSTROOT)\AppleInternal\bin&quot;                                            mkdir &quot;$(DSTROOT)\AppleInternal\bin&quot;&#x0D;&#x0A;xcopy /I/Y &quot;$(TargetPath)&quot;                                                                           &quot;$(DSTROOT)\Program Files\Bonjour\$(PlatformName)&quot;&#x0D;&#x0A;xcopy /I/Y &quot;$(TargetDir)$(TargetName).pdb&quot;                                          &quot;$(DSTROOT)\AppleInternal\bin&quot;&#x0D;&#x0A;:END&#x0D;&#x0A;"

+			/>

+		</Configuration>

+		<Configuration

+			Name="Release|x64"

+			OutputDirectory="$(PlatformName)\$(ConfigurationName)"

+			IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"

+			ConfigurationType="1"

+			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"

+			CharacterSet="2"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+				TargetEnvironment="3"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				AdditionalIncludeDirectories=".;../;../../mDNSCore;../../mDNSShared;&quot;$(VCInstallDir)include&quot;;&quot;$(VCInstallDir)atlmfc\include&quot;;&quot;C:/Program Files/Microsoft SDKs/Windows/v6.1/Include&quot;"

+				PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;_WIN32_WINNT=0x0501;TARGET_OS_WIN32;WIN32_LEAN_AND_MEAN;USE_TCP_LOOPBACK;PLATFORM_NO_STRSEP;PLATFORM_NO_EPIPE;PLATFORM_NO_RLIMIT;PID_FILE=&quot;&quot;&quot;&quot;;UNICODE;_UNICODE;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1;_LEGACY_NAT_TRAVERSAL_"

+				RuntimeLibrary="0"

+				UsePrecompiledHeader="0"

+				AssemblerListingLocation="$(IntDir)\"

+				WarningLevel="4"

+				Detect64BitPortabilityProblems="true"

+				DebugInformationFormat="3"

+				DisableSpecificWarnings="4127;4201"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+				AdditionalIncludeDirectories="../"

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				AdditionalOptions="/NXCOMPAT /DYNAMICBASE"

+				AdditionalDependencies="ws2_32.lib iphlpapi.lib netapi32.lib powrprof.lib"

+				OutputFile="$(OutDir)/mDNSResponder.exe"

+				LinkIncremental="1"

+				GenerateDebugInformation="true"

+				ProgramDatabaseFile="$(IntDir)/$(ProjectName).pdb"

+				SubSystem="1"

+				OptimizeReferences="2"

+				EnableCOMDATFolding="2"

+				TargetMachine="17"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+				AdditionalManifestFiles="res\mDNSResponder64.manifest"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+				CommandLine="if not &quot;%RC_XBS%&quot; == &quot;YES&quot; goto END&#x0D;&#x0A;if not exist &quot;$(DSTROOT)\Program Files\Bonjour\$(PlatformName)&quot;   mkdir &quot;$(DSTROOT)\Program Files\Bonjour\$(PlatformName)&quot;&#x0D;&#x0A;if not exist &quot;$(DSTROOT)\AppleInternal&quot;                                                   mkdir &quot;$(DSTROOT)\AppleInternal&quot;&#x0D;&#x0A;if not exist &quot;$(DSTROOT)\AppleInternal\bin&quot;                                            mkdir &quot;$(DSTROOT)\AppleInternal\bin&quot;&#x0D;&#x0A;xcopy /I/Y &quot;$(TargetPath)&quot;                                                                           &quot;$(DSTROOT)\Program Files\Bonjour\$(PlatformName)&quot;&#x0D;&#x0A;xcopy /I/Y &quot;$(TargetDir)$(TargetName).pdb&quot;                                          &quot;$(DSTROOT)\AppleInternal\bin&quot;&#x0D;&#x0A;:END&#x0D;&#x0A;"

+			/>

+		</Configuration>

+	</Configurations>

+	<References>

+	</References>

+	<Files>

+		<Filter

+			Name="Source Files"

+			Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"

+			UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"

+			>

+			<File

+				RelativePath="..\..\mDNSShared\DebugServices.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\mDNSCore\DNSCommon.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\mDNSCore\DNSDigest.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\mDNSShared\dnssd_ipc.c"

+				>

+			</File>

+			<File

+				RelativePath=".\EventLog.mc"

+				>

+				<FileConfiguration

+					Name="Debug|Win32"

+					>

+					<Tool

+						Name="VCCustomBuildTool"

+						Description="Compiling Message Resource"

+						CommandLine="mc.exe EventLog.mc"

+						Outputs="EventLog.rc EventLog.h"

+					/>

+				</FileConfiguration>

+				<FileConfiguration

+					Name="Debug|x64"

+					>

+					<Tool

+						Name="VCCustomBuildTool"

+						Description="Compiling Message Resource"

+						CommandLine="mc.exe EventLog.mc"

+						Outputs="EventLog.rc EventLog.h"

+					/>

+				</FileConfiguration>

+				<FileConfiguration

+					Name="Release|Win32"

+					>

+					<Tool

+						Name="VCCustomBuildTool"

+						Description="Compiling Message Resource"

+						CommandLine="mc.exe EventLog.mc"

+						Outputs="EventLog.rc EventLog.h"

+					/>

+				</FileConfiguration>

+				<FileConfiguration

+					Name="Release|x64"

+					>

+					<Tool

+						Name="VCCustomBuildTool"

+						Description="Compiling Message Resource"

+						CommandLine="mc.exe EventLog.mc"

+						Outputs="EventLog.rc EventLog.h"

+					/>

+				</FileConfiguration>

+			</File>

+			<File

+				RelativePath="Firewall.cpp"

+				>

+			</File>

+			<File

+				RelativePath="..\..\mDNSShared\GenLinkedList.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\mDNSMacOSX\LegacyNATTraversal.c"

+				>

+			</File>

+			<File

+				RelativePath=".\main.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\mDNSCore\mDNS.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\mDNSShared\mDNSDebug.c"

+				>

+			</File>

+			<File

+				RelativePath="..\mDNSWin32.c"

+				>

+			</File>

+			<File

+				RelativePath="..\Secret.c"

+				>

+			</File>

+			<File

+				RelativePath=".\Service.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\mDNSCore\uDNS.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\mDNSShared\uds_daemon.c"

+				>

+			</File>

+		</Filter>

+		<Filter

+			Name="Header Files"

+			Filter="h;hpp;hxx;hm;inl;inc;xsd"

+			UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"

+			>

+			<File

+				RelativePath="..\..\mDNSShared\CommonServices.h"

+				>

+			</File>

+			<File

+				RelativePath="..\..\mDNSShared\DebugServices.h"

+				>

+			</File>

+			<File

+				RelativePath="..\..\mDNSCore\DNSCommon.h"

+				>

+			</File>

+			<File

+				RelativePath="..\..\mDNSShared\dnssd_ipc.h"

+				>

+			</File>

+			<File

+				RelativePath="..\..\mDNSShared\GenLinkedList.h"

+				>

+			</File>

+			<File

+				RelativePath="..\..\mDNSCore\mDNSDebug.h"

+				>

+			</File>

+			<File

+				RelativePath="..\..\mDNSCore\mDNSEmbeddedAPI.h"

+				>

+			</File>

+			<File

+				RelativePath="..\mDNSWin32.h"

+				>

+			</File>

+			<File

+				RelativePath=".\Resource.h"

+				>

+			</File>

+			<File

+				RelativePath="..\Secret.h"

+				>

+			</File>

+			<File

+				RelativePath=".\Service.h"

+				>

+			</File>

+			<File

+				RelativePath="..\..\mDNSCore\uDNS.h"

+				>

+			</File>

+			<File

+				RelativePath="..\..\mDNSShared\uds_daemon.h"

+				>

+			</File>

+		</Filter>

+		<Filter

+			Name="Resource Files"

+			Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"

+			UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"

+			>

+			<File

+				RelativePath=".\Service.rc"

+				>

+			</File>

+		</Filter>

+	</Files>

+	<Globals>

+	</Globals>

+</VisualStudioProject>

diff --git a/mdnsresponder/mDNSWindows/SystemService/main.c b/mdnsresponder/mDNSWindows/SystemService/main.c
new file mode 100755
index 0000000..33cce9e
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/SystemService/main.c
@@ -0,0 +1,32 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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 "Service.h"
+
+
+//===========================================================================================================================
+//	main
+//===========================================================================================================================
+#if defined(UNICODE)
+int __cdecl wmain( int argc, wchar_t * argv[] )
+#else
+int	__cdecl main( int argc, char *argv[] )
+#endif
+{
+	return Main( argc, argv );
+}
+
diff --git a/mdnsresponder/mDNSWindows/SystemService/res/mDNSResponder.manifest b/mdnsresponder/mDNSWindows/SystemService/res/mDNSResponder.manifest
new file mode 100644
index 0000000..7275854
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/SystemService/res/mDNSResponder.manifest
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+	<assemblyIdentity version="1.0.0.0" processorArchitecture="X86" name="Apple.Bonjour.mDNSResponder" type="win32"/>
+	<description>Enables hardware devices and software services to automatically configure themselves and advertise their presence on the network.</description>
+	<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
+		<security>
+			<requestedPrivileges>
+				<requestedExecutionLevel level="requireAdministrator" uiAccess="false"/>
+			</requestedPrivileges>
+		</security>
+	</trustInfo>
+</assembly>
diff --git a/mdnsresponder/mDNSWindows/SystemService/res/mDNSResponder64.manifest b/mdnsresponder/mDNSWindows/SystemService/res/mDNSResponder64.manifest
new file mode 100644
index 0000000..13b3998
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/SystemService/res/mDNSResponder64.manifest
@@ -0,0 +1,12 @@
+<?xml version="1.0" encoding="UTF-8" standalone="yes"?>
+<assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0">
+	<assemblyIdentity version="1.0.0.0" processorArchitecture="X86" name="Apple.Bonjour.ControlPanel" type="win32"/>
+	<description>Enables hardware devices and software services to automatically configure themselves and advertise their presence on the network.</description>
+	<trustInfo xmlns="urn:schemas-microsoft-com:asm.v3">
+		<security>
+			<requestedPrivileges>
+				<requestedExecutionLevel level="requireAdministrator" uiAccess="false"/>
+			</requestedPrivileges>
+		</security>
+	</trustInfo>
+</assembly>
diff --git a/mdnsresponder/mDNSWindows/SystemService/resource.h b/mdnsresponder/mDNSWindows/SystemService/resource.h
new file mode 100644
index 0000000..d968af9
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/SystemService/resource.h
@@ -0,0 +1,17 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by Service.rc
+//
+
+#define IDS_SERVICE_DESCRIPTION        100
+
+// Next default values for new objects
+// 
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE        101
+#define _APS_NEXT_COMMAND_VALUE         40001
+#define _APS_NEXT_CONTROL_VALUE         1000
+#define _APS_NEXT_SYMED_VALUE           101
+#endif
+#endif
diff --git a/mdnsresponder/mDNSWindows/SystemService/resrc1.h b/mdnsresponder/mDNSWindows/SystemService/resrc1.h
new file mode 100644
index 0000000..54a6524
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/SystemService/resrc1.h
@@ -0,0 +1,15 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by Service.rc
+//
+
+// Next default values for new objects
+// 
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE        101
+#define _APS_NEXT_COMMAND_VALUE         40001
+#define _APS_NEXT_CONTROL_VALUE         1000
+#define _APS_NEXT_SYMED_VALUE           101
+#endif
+#endif
diff --git a/mdnsresponder/mDNSWindows/VPCDetect.cpp b/mdnsresponder/mDNSWindows/VPCDetect.cpp
new file mode 100755
index 0000000..3df7c14
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/VPCDetect.cpp
@@ -0,0 +1,165 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+#define _WIN32_DCOM
+#include "VPCDetect.h"
+#include "DebugServices.h"
+#include <comdef.h>
+#include <Wbemidl.h>
+
+# pragma comment(lib, "wbemuuid.lib")
+
+static BOOL g_doneCheck = FALSE;
+static BOOL g_isVPC		= FALSE;
+
+
+mStatus
+IsVPCRunning( BOOL * inVirtualPC )
+{
+	IWbemLocator			*	pLoc 		= 0;
+	IWbemServices			*	pSvc 		= 0;
+    IEnumWbemClassObject	*	pEnumerator = NULL;
+	bool						coInit 		= false;
+	HRESULT						hres;
+	SC_HANDLE					scm			= NULL;
+	SC_HANDLE					service		= NULL;
+	SERVICE_STATUS				status;
+	mStatus						err;
+	BOOL						ok          = TRUE;
+
+	// Initialize flag
+
+	*inVirtualPC = FALSE;
+
+	// Find out if WMI is running
+
+	scm = OpenSCManager( 0, 0, SC_MANAGER_CONNECT );
+	err = translate_errno( scm, (OSStatus) GetLastError(), kOpenErr );
+	require_noerr( err, exit );
+
+	service = OpenService( scm, TEXT( "winmgmt" ), SERVICE_QUERY_STATUS );
+	err = translate_errno( service, (OSStatus) GetLastError(), kNotFoundErr );
+	require_noerr( err, exit );
+	
+	ok = QueryServiceStatus( service, &status );
+	err = translate_errno( ok, (OSStatus) GetLastError(), kAuthenticationErr );
+	require_noerr( err, exit );
+	require_action( status.dwCurrentState == SERVICE_RUNNING, exit, err = kUnknownErr );
+	
+    // Initialize COM.
+
+	hres = CoInitializeEx(0, COINIT_MULTITHREADED);
+	require_action( SUCCEEDED( hres ), exit, err = kUnknownErr );
+	coInit = true;
+
+	// Initialize Security
+
+	hres =  CoInitializeSecurity(NULL, -1, NULL, NULL, RPC_C_AUTHN_LEVEL_DEFAULT, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE, NULL );
+	require_action( SUCCEEDED( hres ), exit, err = kUnknownErr );
+
+                      
+    // Obtain the initial locator to Windows Management on a particular host computer.
+
+    hres = CoCreateInstance( CLSID_WbemLocator, 0, CLSCTX_INPROC_SERVER, IID_IWbemLocator, (LPVOID *) &pLoc );
+	require_action( SUCCEEDED( hres ), exit, err = kUnknownErr );
+ 
+    // Connect to the root\cimv2 namespace with the
+    // current user and obtain pointer pSvc
+    // to make IWbemServices calls.
+
+	hres = pLoc->ConnectServer( _bstr_t(L"ROOT\\CIMV2"), NULL, NULL, 0, WBEM_FLAG_CONNECT_USE_MAX_WAIT, 0, 0, &pSvc );
+	require_action( SUCCEEDED( hres ), exit, err = kUnknownErr );
+    
+    // Set the IWbemServices proxy so that impersonation
+    // of the user (client) occurs.
+
+	hres = CoSetProxyBlanket( pSvc, RPC_C_AUTHN_WINNT, RPC_C_AUTHZ_NONE, NULL, RPC_C_AUTHN_LEVEL_CALL, RPC_C_IMP_LEVEL_IMPERSONATE, NULL, EOAC_NONE );
+	require_action( SUCCEEDED( hres ), exit, err = kUnknownErr );
+
+    // Use the IWbemServices pointer to make requests of WMI. 
+    // Make requests here:
+
+	hres = pSvc->ExecQuery( bstr_t("WQL"), bstr_t("SELECT * from Win32_BaseBoard"), WBEM_FLAG_FORWARD_ONLY | WBEM_FLAG_RETURN_IMMEDIATELY, NULL, &pEnumerator);
+    
+	require_action( SUCCEEDED( hres ), exit, err = kUnknownErr );
+
+	do
+	{
+		IWbemClassObject* pInstance = NULL;
+		ULONG dwCount = NULL;
+
+		hres = pEnumerator->Next( WBEM_INFINITE, 1, &pInstance, &dwCount);
+
+		if ( pInstance )
+		{
+			VARIANT v;
+			BSTR strClassProp = SysAllocString(L"Manufacturer");
+			HRESULT hr;
+
+			hr = pInstance->Get(strClassProp, 0, &v, 0, 0);
+			SysFreeString(strClassProp);
+
+			// check the HRESULT to see if the action succeeded.
+
+			if (SUCCEEDED(hr) && (V_VT(&v) == VT_BSTR))
+			{
+				wchar_t * wstring = wcslwr( V_BSTR( &v ) );
+
+				if (wcscmp( wstring, L"microsoft corporation" ) == 0 )
+				{
+					*inVirtualPC = TRUE;
+				}
+			}
+		
+			VariantClear(&v);
+		}
+	} while (hres == WBEM_S_NO_ERROR);
+         
+exit:
+ 
+	if ( pSvc != NULL )
+	{
+    	pSvc->Release();
+	}
+
+	if ( pLoc != NULL )
+	{
+    	pLoc->Release();     
+	}
+
+	if ( coInit )
+	{
+    	CoUninitialize();
+	}
+
+	if ( service )
+	{
+		CloseServiceHandle( service );
+	}
+
+	if ( scm )
+	{
+		CloseServiceHandle( scm );
+	}
+
+	if ( *inVirtualPC )
+	{
+		dlog( kDebugLevelTrace, "Virtual PC detected" );
+	}
+
+	return err;
+}
diff --git a/mdnsresponder/mDNSWindows/VPCDetect.h b/mdnsresponder/mDNSWindows/VPCDetect.h
new file mode 100644
index 0000000..9f34bee
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/VPCDetect.h
@@ -0,0 +1,36 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+#pragma once
+
+#include <windows.h>
+#include <mDNSEmbeddedAPI.h>
+
+
+#if defined(__cplusplus)
+extern "C"
+{
+#endif
+
+
+extern mStatus
+IsVPCRunning( BOOL * inVirtualPC );
+
+
+#if defined(__cplusplus)
+}
+#endif
diff --git a/mdnsresponder/mDNSWindows/WinServices.cpp b/mdnsresponder/mDNSWindows/WinServices.cpp
new file mode 100644
index 0000000..e47c468
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/WinServices.cpp
@@ -0,0 +1,93 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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 "WinServices.h"
+#include <DebugServices.h>
+
+
+//===========================================================================================================================
+//	UTF8StringToStringObject
+//===========================================================================================================================
+
+OSStatus	UTF8StringToStringObject( const char *inUTF8, CString &inObject )
+{
+	OSStatus		err;
+	int				n;
+	BSTR			unicode;
+	
+	unicode = NULL;
+	
+	n = MultiByteToWideChar( CP_UTF8, 0, inUTF8, -1, NULL, 0 );
+	if( n > 0 )
+	{
+		unicode = (BSTR) malloc( (size_t)( n * sizeof( wchar_t ) ) );
+		if( !unicode )
+		{
+			err = ERROR_INSUFFICIENT_BUFFER;
+			goto exit;
+		}
+
+		n = MultiByteToWideChar( CP_UTF8, 0, inUTF8, -1, unicode, n );
+		try
+		{
+			inObject = unicode;
+		}
+		catch( ... )
+		{
+			err = ERROR_NO_UNICODE_TRANSLATION;
+			goto exit;
+		}
+	}
+	else
+	{
+		inObject = "";
+	}
+	err = ERROR_SUCCESS;
+	
+exit:
+	if( unicode )
+	{
+		free( unicode );
+	}
+	return( err );
+}
+
+
+//===========================================================================================================================
+//	UTF8StringToStringObject
+//===========================================================================================================================
+
+OSStatus
+StringObjectToUTF8String( CString &inObject, char* outUTF8, size_t outUTF8Len )
+{
+    OSStatus err = kNoErr;
+
+	memset( outUTF8, 0, outUTF8Len );
+
+	if ( inObject.GetLength() > 0 )
+    {
+		size_t size;
+
+		size = (size_t) WideCharToMultiByte( CP_UTF8, 0, inObject.GetBuffer(), inObject.GetLength(), outUTF8, (int) outUTF8Len, NULL, NULL);
+        err = translate_errno( size != 0, GetLastError(), kUnknownErr );
+        require_noerr( err, exit );
+    }
+
+exit:
+
+	return err;
+}
diff --git a/mdnsresponder/mDNSWindows/WinServices.h b/mdnsresponder/mDNSWindows/WinServices.h
new file mode 100644
index 0000000..f650d1d
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/WinServices.h
@@ -0,0 +1,34 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 1997-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+
+#pragma once
+
+#include <afxwin.h>         // MFC core and standard components
+#include <afxext.h>         // MFC extensions
+#include <afxdtctl.h>		// MFC support for Internet Explorer 4 Common Controls
+#ifndef _AFX_NO_AFXCMN_SUPPORT
+#	include <afxcmn.h>		// MFC support for Windows Common Controls
+#endif
+
+#include <winsock2.h>
+#include <afxsock.h>		// MFC socket extensions
+#include "CommonServices.h"
+
+
+OSStatus	UTF8StringToStringObject( const char *inUTF8, CString &outObject );
+OSStatus	StringObjectToUTF8String( CString &inObject, char* outUTF8, size_t outUTF8Len );
diff --git a/mdnsresponder/mDNSWindows/WinVersRes.h b/mdnsresponder/mDNSWindows/WinVersRes.h
new file mode 100644
index 0000000..c395aa4
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/WinVersRes.h
@@ -0,0 +1,35 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+#ifndef WINRESVERS_H
+#define WINRESVERS_H
+
+#define MASTER_PROD_NAME	"Bonjour"
+
+// Define the company name for mDNSResponder on Windows
+#define MASTER_COMPANY_NAME   "Apple Inc."
+
+// Define the product version for mDNSResponder on Windows
+#define MASTER_PROD_VERS		2,0,0,19
+#define MASTER_PROD_VERS_STR	"2,0,0,19"
+#define MASTER_PROD_VERS_STR2	"2.0.0.19"
+#define MASTER_PROD_VERS_STR3 "Explorer Plugin 2.0.0.19"
+
+// Define the legal copyright
+#define MASTER_LEGAL_COPYRIGHT "Copyright (C) 2003-2010 Apple Inc."
+
+#endif // WINRESVERS_H
diff --git a/mdnsresponder/mDNSWindows/isocode.h b/mdnsresponder/mDNSWindows/isocode.h
new file mode 100755
index 0000000..fe04f31
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/isocode.h
@@ -0,0 +1,135 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+/* isocode.h                                                             */
+/* ----------------------------------------------------------------------*/
+/* THIS FILE HAS BEEN AUTO-GENERATED.  DO NOT EDIT DIRECTLY              */
+/* If a language needs to be added, edit isocode.txt, and run isocode.pl */
+/* to generate a new version of this file                                */
+/* ----------------------------------------------------------------------*/
+/* ----------------------------------------------------------------------*/
+
+
+unsigned char ISOCODES[] = {
+12, 9, 'e','n', 0 , 0 , 0 , 0 ,
+40, 9, 'e','n', 0 , 0 , 0 , 0 ,
+16, 9, 'e','n', 0 , 0 , 0 , 0 ,
+36, 9, 'e','n', 0 , 0 , 0 , 0 ,
+24, 9, 'e','n', 0 , 0 , 0 , 0 ,
+32, 9, 'e','n', 0 , 0 , 0 , 0 ,
+20, 9, 'e','n', 0 , 0 , 0 , 0 ,
+52, 9, 'e','n', 0 , 0 , 0 , 0 ,
+28, 9, 'e','n', 0 , 0 , 0 , 0 ,
+44, 9, 'e','n', 0 , 0 , 0 , 0 ,
+8, 9, 'e','n', 0 , 0 , 0 , 0 ,
+4, 9, 'e','n', 0 , 0 , 0 , 0 ,
+48, 9, 'e','n', 0 , 0 , 0 , 0 ,
+8, 12, 'f','r', 0 , 0 , 0 , 0 ,
+44, 12, 'f','r', 0 , 0 , 0 , 0 ,
+12, 12, 'f','r', 0 , 0 , 0 , 0 ,
+36, 12, 'f','r', 0 , 0 , 0 , 0 ,
+48, 12, 'f','r', 0 , 0 , 0 , 0 ,
+4, 12, 'f','r', 0 , 0 , 0 , 0 ,
+20, 12, 'f','r', 0 , 0 , 0 , 0 ,
+52, 12, 'f','r', 0 , 0 , 0 , 0 ,
+24, 12, 'f','r', 0 , 0 , 0 , 0 ,
+40, 12, 'f','r', 0 , 0 , 0 , 0 ,
+16, 12, 'f','r', 0 , 0 , 0 , 0 ,
+28, 12, 'f','r', 0 , 0 , 0 , 0 ,
+4, 98, 'f','r', 0 , 0 , 0 , 0 ,
+12, 7, 'd','e', 0 , 0 , 0 , 0 ,
+4, 7, 'd','e', 0 , 0 , 0 , 0 ,
+20, 7, 'd','e', 0 , 0 , 0 , 0 ,
+16, 7, 'd','e', 0 , 0 , 0 , 0 ,
+8, 7, 'd','e', 0 , 0 , 0 , 0 ,
+4, 17, 'j','a', 0 , 0 , 0 , 0 ,
+8, 19, 'n','l', 0 , 0 , 0 , 0 ,
+4, 19, 'n','l', 0 , 0 , 0 , 0 ,
+4, 16, 'i','t', 0 , 0 , 0 , 0 ,
+8, 16, 'i','t', 0 , 0 , 0 , 0 ,
+44, 10, 'e','s', 0 , 0 , 0 , 0 ,
+64, 10, 'e','s', 0 , 0 , 0 , 0 ,
+52, 10, 'e','s', 0 , 0 , 0 , 0 ,
+36, 10, 'e','s', 0 , 0 , 0 , 0 ,
+20, 10, 'e','s', 0 , 0 , 0 , 0 ,
+28, 10, 'e','s', 0 , 0 , 0 , 0 ,
+48, 10, 'e','s', 0 , 0 , 0 , 0 ,
+68, 10, 'e','s', 0 , 0 , 0 , 0 ,
+16, 10, 'e','s', 0 , 0 , 0 , 0 ,
+72, 10, 'e','s', 0 , 0 , 0 , 0 ,
+12, 10, 'e','s', 0 , 0 , 0 , 0 ,
+8, 10, 'e','s', 0 , 0 , 0 , 0 ,
+76, 10, 'e','s', 0 , 0 , 0 , 0 ,
+24, 10, 'e','s', 0 , 0 , 0 , 0 ,
+60, 10, 'e','s', 0 , 0 , 0 , 0 ,
+40, 10, 'e','s', 0 , 0 , 0 , 0 ,
+80, 10, 'e','s', 0 , 0 , 0 , 0 ,
+4, 10, 'e','s', 0 , 0 , 0 , 0 ,
+56, 10, 'e','s', 0 , 0 , 0 , 0 ,
+32, 10, 'e','s', 0 , 0 , 0 , 0 ,
+8, 4, 'z','h','_','C','N', 0 ,
+16, 4, 'z','h','_','C','N', 0 ,
+12, 4, 'z','h','_','T','W', 0 ,
+20, 4, 'z','h','_','T','W', 0 ,
+4, 4, 'z','h','_','T','W', 0 ,
+4, 6, 'd','a', 0 , 0 , 0 , 0 ,
+4, 11, 'f','i', 0 , 0 , 0 , 0 ,
+4, 18, 'k','o', 0 , 0 , 0 , 0 ,
+4, 20, 'n','b', 0 , 0 , 0 , 0 ,
+8, 20, 'n','b', 0 , 0 , 0 , 0 ,
+4, 22, 'p','t', 0 , 0 , 0 , 0 ,
+4, 29, 's','v', 0 , 0 , 0 , 0 ,
+8, 29, 's','v', 0 , 0 , 0 , 0 ,
+20, 1, 'a','r', 0 , 0 , 0 , 0 ,
+60, 1, 'a','r', 0 , 0 , 0 , 0 ,
+12, 1, 'a','r', 0 , 0 , 0 , 0 ,
+8, 1, 'a','r', 0 , 0 , 0 , 0 ,
+44, 1, 'a','r', 0 , 0 , 0 , 0 ,
+52, 1, 'a','r', 0 , 0 , 0 , 0 ,
+48, 1, 'a','r', 0 , 0 , 0 , 0 ,
+16, 1, 'a','r', 0 , 0 , 0 , 0 ,
+24, 1, 'a','r', 0 , 0 , 0 , 0 ,
+32, 1, 'a','r', 0 , 0 , 0 , 0 ,
+64, 1, 'a','r', 0 , 0 , 0 , 0 ,
+4, 1, 'a','r', 0 , 0 , 0 , 0 ,
+40, 1, 'a','r', 0 , 0 , 0 , 0 ,
+28, 1, 'a','r', 0 , 0 , 0 , 0 ,
+56, 1, 'a','r', 0 , 0 , 0 , 0 ,
+36, 1, 'a','r', 0 , 0 , 0 , 0 ,
+4, 2, 'b','g', 0 , 0 , 0 , 0 ,
+4, 26, 'h','r', 0 , 0 , 0 , 0 ,
+4, 5, 'c','s', 0 , 0 , 0 , 0 ,
+4, 8, 'e','l', 0 , 0 , 0 , 0 ,
+4, 13, 'i','w', 0 , 0 , 0 , 0 ,
+4, 14, 'h','u', 0 , 0 , 0 , 0 ,
+4, 15, 'i','s', 0 , 0 , 0 , 0 ,
+4, 21, 'p','l', 0 , 0 , 0 , 0 ,
+8, 22, 'p','t','_','P','T', 0 ,
+4, 24, 'r','o', 0 , 0 , 0 , 0 ,
+8, 24, 'r','o', 0 , 0 , 0 , 0 ,
+4, 25, 'r','u', 0 , 0 , 0 , 0 ,
+8, 25, 'r','u', 0 , 0 , 0 , 0 ,
+4, 30, 't','h', 0 , 0 , 0 , 0 ,
+4, 31, 't','r', 0 , 0 , 0 , 0 ,
+4, 34, 'u','k', 0 , 0 , 0 , 0 ,
+};
+
+#define NUM_ISOCODES      101
+#define LANG_CODE_LEN     5
+#define MODULO_ISOCODES   8
+
+
diff --git a/mdnsresponder/mDNSWindows/loclibrary.c b/mdnsresponder/mDNSWindows/loclibrary.c
new file mode 100755
index 0000000..9744120
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/loclibrary.c
@@ -0,0 +1,268 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+    
+/* loclibrary.c                                                          
+ * ----------------------------------------------------------------------
+ * Source for localization library                                       
+ * Originally created by jsantamaria: 3 may 2004                         
+ * ----------------------------------------------------------------------
+ */
+ 
+#include "DebugServices.h"
+#include <windows.h>
+#include <stdio.h>
+#include "isocode.h"
+#include "loclibrary.h"
+#include "Shlwapi.h"
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <wchar.h>
+
+
+#ifdef __cplusplus
+extern "c" {
+#endif
+
+#ifdef _MSC_VER
+#define swprintf _snwprintf
+#define snprintf _snprintf
+#endif
+
+
+
+#define DEFAULT_LANG_CODE "en"
+
+// gets the user language
+static LANGID _getUserLanguage( void ) {
+	
+	return GetUserDefaultUILanguage();
+
+}
+
+
+// gets the ISO mapping
+static int _getISOCode(LANGID wLangID, char *isoLangCode, int codeLen) {
+	int i;
+	unsigned short langCode;
+
+	for (i = 0; i < NUM_ISOCODES; i++) {
+		int startIndex = i * MODULO_ISOCODES;
+		
+		langCode = (ISOCODES[startIndex] << 8);
+		langCode = langCode + ( (unsigned short) (ISOCODES[startIndex + 1]) );
+
+		if (langCode == wLangID) {
+			char *langStr = (char *)&(ISOCODES[startIndex+2]);
+			strncpy(isoLangCode, langStr, codeLen);
+			return 0;
+		}
+	}
+	return 1;
+}
+
+static char isoLangCode[LANG_CODE_LEN + 1] = "";
+static LANGID wLangID = (LANGID) -1;
+
+static void _setLanguageIfNeeded(void) {
+	
+	// get the language code if we don't have it cached
+	if (!strncmp(isoLangCode,"",LANG_CODE_LEN + 1)) {
+		
+		// if we haven't cached the language id, do the lookup
+		if (wLangID == (LANGID) -1) {
+			wLangID = _getUserLanguage();
+		}
+		
+		// if no ISOCode, set it to DEFAULT_LANG_CODE
+		if (_getISOCode(wLangID, isoLangCode, LANG_CODE_LEN + 1)) {
+			strncpy(isoLangCode, DEFAULT_LANG_CODE, LANG_CODE_LEN+1);
+		}
+	}
+
+}
+
+//// PathForResource
+
+// Gets the PathForResource for handle 0 for the current process
+
+
+static char appPathNameA[MAX_PATH] = "";
+
+int PathForResourceA ( HMODULE module, const char *name, char *locFile, int locFileLen)
+{
+	int ret = 0;
+
+	if ( !strcmp( appPathNameA, "" ) )
+	{
+		char   folder[MAX_PATH];
+		char * ext;
+		char * app;
+
+		GetModuleFileNameA( module, folder, MAX_PATH );
+
+		// Get folder string
+		
+		app = strrchr( folder, '\\' );
+		require_action( app, exit, ret = 0 );
+		*app++ = '\0';
+
+		// Strip the extension
+
+		if ( ( ( ext = strstr( app, ".exe" ) ) != NULL ) || ( ( ext = strstr( app, ".dll" ) ) != NULL ) )
+		{
+			*ext = '\0';
+		}
+
+		snprintf( appPathNameA, MAX_PATH, "%s\\%s", folder, app );
+	}
+
+	ret = PathForResourceWithPathA (appPathNameA, name, locFile, locFileLen);
+
+exit:
+
+	return ret;
+}
+
+static wchar_t appPathNameW[MAX_PATH] = L"";
+
+int PathForResourceW ( HMODULE module, const wchar_t *name, wchar_t *locFile, int locFileLen)
+{
+	int ret = 0;
+
+	if ( !wcscmp( appPathNameW, L"" ) )
+	{
+		wchar_t   folder[MAX_PATH];
+		wchar_t * app;
+		wchar_t * ext;
+
+		GetModuleFileNameW( module, folder, MAX_PATH);
+
+		// Get folder string
+		
+		app = wcsrchr( folder, '\\' );
+		require_action( app, exit, ret = 0 );
+		*app++ = '\0';
+
+		// Strip the extension
+
+		if ( ( ( ext = wcsstr( app, L".exe" ) ) != NULL ) || ( ( ext = wcsstr( app, L".dll" ) ) != NULL ) )
+		{
+			*ext = '\0';
+		}
+
+		swprintf( appPathNameW, MAX_PATH, L"%ls\\%ls", folder, app );
+	}
+
+	ret = PathForResourceWithPathW (appPathNameW, name, locFile, locFileLen);
+
+exit:
+
+	return ret;
+}
+
+
+//// PathForResourceWithPath
+
+#define TMP_BUF_SIZE MAX_PATH
+
+int PathForResourceWithPathA (const char *path, const char *nm, 
+									char *locFile, int locFileLen) {
+	char tmpBuffer[TMP_BUF_SIZE];
+
+	// build the path to the executable in the generic 
+	// resources folder, check there first
+	snprintf(tmpBuffer, MAX_PATH, "%s.Resources\\%s", path, nm);
+
+	if (!PathFileExistsA(tmpBuffer)) {
+
+		// didn't hit generic resource folder, so need to get language codes
+		_setLanguageIfNeeded();
+
+		// test to see if localized directory exists, 
+		// if so, we don't fall back if we don't find the file.
+		snprintf(tmpBuffer, TMP_BUF_SIZE, 
+				 "%s.Resources\\%s.lproj", path, isoLangCode);
+
+		if (PathFileExistsA(tmpBuffer)) {
+			snprintf(tmpBuffer, TMP_BUF_SIZE, "%s\\%s", tmpBuffer, nm);
+
+			if (!PathFileExistsA(tmpBuffer)) return 0;
+
+			strncpy(locFile, tmpBuffer, locFileLen);
+			return (int) strlen(locFile);
+		}
+
+		// fall back on DEFAULT_LANG_CODE if still no good
+		snprintf(tmpBuffer, TMP_BUF_SIZE, "%s.Resources\\%s.lproj\\%s", 
+				path, DEFAULT_LANG_CODE, nm);
+				
+		// we can't find the resource, so return 0
+		if (!PathFileExistsA(tmpBuffer)) return 0;
+	}
+	
+	strncpy(locFile, tmpBuffer, locFileLen);
+	return (int) strlen(locFile);
+
+}
+
+
+int PathForResourceWithPathW (const wchar_t *path, const wchar_t *nm, 
+								wchar_t *locFile, int locFileLen) {
+
+	wchar_t tmpBuffer[TMP_BUF_SIZE];
+
+	// build the path to the executable in the generic
+	// resources folder, check there first
+	swprintf(tmpBuffer, TMP_BUF_SIZE, L"%ls.Resources\\%ls", path, nm);
+
+	if (!PathFileExistsW(tmpBuffer)) {
+		// didn't hit generic resource folder, so need to get language codes
+		_setLanguageIfNeeded();
+
+		// test to see if localized directory exists, 
+		// if so, we don't fall back if we don't find the file.
+		swprintf(tmpBuffer, TMP_BUF_SIZE, 
+				  L"%ls.Resources\\%S.lproj", path, isoLangCode);
+
+		if (PathFileExistsW(tmpBuffer)) {
+			swprintf(tmpBuffer, TMP_BUF_SIZE, L"%ls\\%ls", tmpBuffer, nm);
+
+			if (!PathFileExistsW(tmpBuffer)) return 0;
+
+			wcsncpy(locFile, tmpBuffer, locFileLen);
+			return (int) wcslen(locFile);
+		}
+
+		// fall back on DEFAULT_LANG_CODE if still no good
+		swprintf(tmpBuffer, TMP_BUF_SIZE, L"%ls.Resources\\%S.lproj\\%ls", 
+			path, DEFAULT_LANG_CODE, nm);
+
+		// we can't find the resource, so return 0
+		if (!PathFileExistsW(tmpBuffer)) return 0;
+	}
+	
+	wcsncpy(locFile, tmpBuffer, locFileLen);
+	return (int) wcslen(locFile);
+
+
+}
+
+
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/mdnsresponder/mDNSWindows/loclibrary.h b/mdnsresponder/mDNSWindows/loclibrary.h
new file mode 100755
index 0000000..0a9b7ce
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/loclibrary.h
@@ -0,0 +1,54 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+    
+/* loclibrary.h                                                      
+ * ----------------------------------------------------------------------
+ * Header file for localization library                                       
+ * Originally created by jsantamaria: 3 may 2004                         
+ * ----------------------------------------------------------------------
+ */
+ 
+#ifndef _loclibrary_h_
+#define _loclibrary_h_
+
+
+#ifdef __cplusplus
+extern "C" {
+#endif // __cplusplus
+
+int PathForResourceW ( HMODULE module, const wchar_t *name, wchar_t *locFile, int locFileLen);
+int PathForResourceWithPathW ( const wchar_t *path, const wchar_t *name, wchar_t *locFile, int locFileLen);
+
+int PathForResourceA ( HMODULE module, const char *name, char *locFile, int locFileLen);
+int PathForResourceWithPathA ( const char *path, const char *name, char *locFile, int locFileLen);
+
+
+#ifdef UNICODE
+#define PathForResource PathForResourceW
+#define PathForResourceWithPath PathForResourceWithPathW
+#else
+#define PathForResource PathForResourceA
+#define PathForResourceWithPath PathForResourceWithPathA
+#endif // UNICODE
+
+
+#ifdef __cplusplus
+}
+#endif // __cplusplus
+
+
+#endif // _loclibrary_h_
diff --git a/mdnsresponder/mDNSWindows/mDNSWin32.c b/mdnsresponder/mDNSWindows/mDNSWin32.c
new file mode 100755
index 0000000..0dcc121
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/mDNSWin32.c
@@ -0,0 +1,5200 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+
+	To Do:
+	
+	- Get unicode name of machine for nice name instead of just the host name.
+	- Use the IPv6 Internet Connection Firewall API to allow IPv6 mDNS without manually changing the firewall.
+	- Get DNS server address(es) from Windows and provide them to the uDNS layer.
+	- Implement TCP support for truncated packets (only stubs now).	
+
+*/
+
+#include	<stdarg.h>
+#include	<stddef.h>
+#include	<stdio.h>
+#include	<stdlib.h>
+#include	<crtdbg.h>
+#include	<string.h>
+
+#include	"CommonServices.h"
+#include	"DebugServices.h"
+#include	"Firewall.h"
+#include	"RegNames.h"
+#include	"Secret.h"
+#include	<dns_sd.h>
+
+#include	<Iphlpapi.h>
+#include	<mswsock.h>
+#include	<process.h>
+#include	<ntsecapi.h>
+#include	<lm.h>
+#include	<winioctl.h>
+#include	<ntddndis.h>        // This defines the IOCTL constants.
+
+#include	"mDNSEmbeddedAPI.h"
+#include	"GenLinkedList.h"
+#include	"DNSCommon.h"
+#include	"mDNSWin32.h"
+
+#if 0
+#pragma mark == Constants ==
+#endif
+
+//===========================================================================================================================
+//	Constants
+//===========================================================================================================================
+
+#define	DEBUG_NAME									"[mDNSWin32] "
+
+#define	MDNS_WINDOWS_USE_IPV6_IF_ADDRS				1
+#define	MDNS_WINDOWS_ENABLE_IPV4					1
+#define	MDNS_WINDOWS_ENABLE_IPV6					1
+#define	MDNS_FIX_IPHLPAPI_PREFIX_BUG				1
+#define MDNS_SET_HINFO_STRINGS						0
+
+#define	kMDNSDefaultName							"My Computer"
+
+#define	kWinSockMajorMin							2
+#define	kWinSockMinorMin							2
+
+#define kRegistryMaxKeyLength						255
+#define kRegistryMaxValueName						16383
+
+static GUID											kWSARecvMsgGUID = WSAID_WSARECVMSG;
+
+#define kIPv6IfIndexBase							(10000000L)
+#define SMBPortAsNumber								445
+#define DEVICE_PREFIX								"\\\\.\\"
+
+#if 0
+#pragma mark == Prototypes ==
+#endif
+
+//===========================================================================================================================
+//	Prototypes
+//===========================================================================================================================
+
+mDNSlocal mStatus			SetupNiceName( mDNS * const inMDNS );
+mDNSlocal mStatus			SetupHostName( mDNS * const inMDNS );
+mDNSlocal mStatus			SetupName( mDNS * const inMDNS );
+mDNSlocal mStatus			SetupInterface( mDNS * const inMDNS, const struct ifaddrs *inIFA, mDNSInterfaceData **outIFD );
+mDNSlocal mStatus			TearDownInterface( mDNS * const inMDNS, mDNSInterfaceData *inIFD );
+mDNSlocal void CALLBACK		FreeInterface( mDNSInterfaceData *inIFD );
+mDNSlocal mStatus			SetupSocket( mDNS * const inMDNS, const struct sockaddr *inAddr, mDNSIPPort port, SocketRef *outSocketRef  );
+mDNSlocal mStatus			SockAddrToMDNSAddr( const struct sockaddr * const inSA, mDNSAddr *outIP, mDNSIPPort *outPort );
+mDNSlocal OSStatus			GetWindowsVersionString( char *inBuffer, size_t inBufferSize );
+mDNSlocal int				getifaddrs( struct ifaddrs **outAddrs );
+mDNSlocal void				freeifaddrs( struct ifaddrs *inAddrs );
+
+
+
+// Platform Accessors
+
+#ifdef	__cplusplus
+	extern "C" {
+#endif
+
+typedef struct mDNSPlatformInterfaceInfo	mDNSPlatformInterfaceInfo;
+struct	mDNSPlatformInterfaceInfo
+{
+	const char *		name;
+	mDNSAddr			ip;
+};
+
+
+mDNSexport mStatus	mDNSPlatformInterfaceNameToID( mDNS * const inMDNS, const char *inName, mDNSInterfaceID *outID );
+mDNSexport mStatus	mDNSPlatformInterfaceIDToInfo( mDNS * const inMDNS, mDNSInterfaceID inID, mDNSPlatformInterfaceInfo *outInfo );
+
+// Utilities
+
+#if( MDNS_WINDOWS_USE_IPV6_IF_ADDRS )
+	mDNSlocal int	getifaddrs_ipv6( struct ifaddrs **outAddrs );
+#endif
+
+mDNSlocal int getifaddrs_ipv4( struct ifaddrs **outAddrs );
+
+
+mDNSlocal DWORD				GetPrimaryInterface();
+mDNSlocal mStatus			AddressToIndexAndMask( struct sockaddr * address, uint32_t * index, struct sockaddr * mask );
+mDNSlocal mDNSBool			CanReceiveUnicast( void );
+mDNSlocal mDNSBool			IsPointToPoint( IP_ADAPTER_UNICAST_ADDRESS * addr );
+
+mDNSlocal mStatus			StringToAddress( mDNSAddr * ip, LPSTR string );
+mDNSlocal mStatus			RegQueryString( HKEY key, LPCSTR param, LPSTR * string, DWORD * stringLen, DWORD * enabled );
+mDNSlocal struct ifaddrs*	myGetIfAddrs(int refresh);
+mDNSlocal OSStatus			TCHARtoUTF8( const TCHAR *inString, char *inBuffer, size_t inBufferSize );
+mDNSlocal OSStatus			WindowsLatin1toUTF8( const char *inString, char *inBuffer, size_t inBufferSize );
+mDNSlocal void				TCPDidConnect( mDNS * const inMDNS, HANDLE event, void * context );
+mDNSlocal void				TCPCanRead( TCPSocket * sock );
+mDNSlocal mStatus			TCPBeginRecv( TCPSocket * sock );
+mDNSlocal void CALLBACK		TCPEndRecv( DWORD error, DWORD bytesTransferred, LPWSAOVERLAPPED overlapped, DWORD flags );
+mDNSlocal void				TCPCloseSocket( TCPSocket * socket );
+mDNSlocal void CALLBACK		TCPFreeSocket( TCPSocket *sock );
+mDNSlocal OSStatus			UDPBeginRecv( UDPSocket * socket );
+mDNSlocal void CALLBACK		UDPEndRecv( DWORD err, DWORD bytesTransferred, LPWSAOVERLAPPED overlapped, DWORD flags );
+mDNSlocal void				UDPCloseSocket( UDPSocket * sock );
+mDNSlocal void CALLBACK		UDPFreeSocket( UDPSocket * sock );
+mDNSlocal mStatus           SetupAddr(mDNSAddr *ip, const struct sockaddr *const sa);
+mDNSlocal void				GetDDNSFQDN( domainname *const fqdn );
+#ifdef UNICODE
+mDNSlocal void				GetDDNSDomains( DNameListElem ** domains, LPCWSTR lpSubKey );
+#else
+mDNSlocal void				GetDDNSDomains( DNameListElem ** domains, LPCSTR lpSubKey );
+#endif
+mDNSlocal void				SetDomainSecrets( mDNS * const inMDNS );
+mDNSlocal void				SetDomainSecret( mDNS * const m, const domainname * inDomain );
+mDNSlocal VOID CALLBACK		CheckFileSharesProc( LPVOID arg, DWORD dwTimerLowValue, DWORD dwTimerHighValue );
+mDNSlocal void				CheckFileShares( mDNS * const inMDNS );
+mDNSlocal void				SMBCallback(mDNS *const m, ServiceRecordSet *const srs, mStatus result);
+mDNSlocal mDNSu8			IsWOMPEnabledForAdapter( const char * adapterName );
+mDNSlocal void				DispatchUDPEvent( mDNS * const m, UDPSocket * sock );
+mDNSlocal void				DispatchTCPEvent( mDNS * const m, TCPSocket * sock );
+
+#ifdef	__cplusplus
+	}
+#endif
+
+#if 0
+#pragma mark == Globals ==
+#endif
+
+//===========================================================================================================================
+//	Globals
+//===========================================================================================================================
+
+mDNSlocal mDNS_PlatformSupport	gMDNSPlatformSupport;
+mDNSs32							mDNSPlatformOneSecond	= 0;
+mDNSlocal UDPSocket		*		gUDPSockets				= NULL;
+mDNSlocal int					gUDPNumSockets			= 0;
+mDNSlocal GenLinkedList			gUDPDispatchableSockets;
+mDNSlocal GenLinkedList			gTCPDispatchableSockets;
+
+#if( MDNS_WINDOWS_USE_IPV6_IF_ADDRS )
+
+	typedef DWORD
+		( WINAPI * GetAdaptersAddressesFunctionPtr )( 
+			ULONG 					inFamily, 
+			DWORD 					inFlags, 
+			PVOID 					inReserved, 
+			PIP_ADAPTER_ADDRESSES 	inAdapter, 
+			PULONG					outBufferSize );
+
+	mDNSlocal HMODULE								gIPHelperLibraryInstance			= NULL;
+	mDNSlocal GetAdaptersAddressesFunctionPtr		gGetAdaptersAddressesFunctionPtr	= NULL;
+
+#endif
+
+
+#ifndef HCRYPTPROV
+   typedef ULONG_PTR HCRYPTPROV;    // WinCrypt.h, line 249
+#endif
+
+
+#ifndef CRYPT_MACHINE_KEYSET
+#	define CRYPT_MACHINE_KEYSET    0x00000020
+#endif
+
+#ifndef CRYPT_NEWKEYSET
+#	define CRYPT_NEWKEYSET         0x00000008
+#endif
+
+#ifndef PROV_RSA_FULL
+#  define PROV_RSA_FULL 1
+#endif
+
+typedef BOOL (__stdcall *fnCryptGenRandom)( HCRYPTPROV, DWORD, BYTE* ); 
+typedef BOOL (__stdcall *fnCryptAcquireContext)( HCRYPTPROV*, LPCTSTR, LPCTSTR, DWORD, DWORD);
+typedef BOOL (__stdcall *fnCryptReleaseContext)(HCRYPTPROV, DWORD);
+
+static fnCryptAcquireContext g_lpCryptAcquireContext 	= NULL;
+static fnCryptReleaseContext g_lpCryptReleaseContext 	= NULL;
+static fnCryptGenRandom		 g_lpCryptGenRandom 		= NULL;
+static HINSTANCE			 g_hAAPI32 					= NULL;
+static HCRYPTPROV			 g_hProvider 				= ( ULONG_PTR ) NULL;
+
+
+typedef DNSServiceErrorType ( DNSSD_API *DNSServiceRegisterFunc )
+    (
+    DNSServiceRef                       *sdRef,
+    DNSServiceFlags                     flags,
+    uint32_t                            interfaceIndex,
+    const char                          *name,         /* may be NULL */
+    const char                          *regtype,
+    const char                          *domain,       /* may be NULL */
+    const char                          *host,         /* may be NULL */
+    uint16_t                            port,
+    uint16_t                            txtLen,
+    const void                          *txtRecord,    /* may be NULL */
+    DNSServiceRegisterReply             callBack,      /* may be NULL */
+    void                                *context       /* may be NULL */
+    );
+
+
+typedef void ( DNSSD_API *DNSServiceRefDeallocateFunc )( DNSServiceRef sdRef );
+
+mDNSlocal HMODULE					gDNSSDLibrary				= NULL;
+mDNSlocal DNSServiceRegisterFunc	gDNSServiceRegister			= NULL;
+mDNSlocal DNSServiceRefDeallocateFunc gDNSServiceRefDeallocate	= NULL;
+mDNSlocal HANDLE					gSMBThread					= NULL;
+mDNSlocal HANDLE					gSMBThreadRegisterEvent		= NULL;
+mDNSlocal HANDLE					gSMBThreadDeregisterEvent	= NULL;
+mDNSlocal HANDLE					gSMBThreadStopEvent			= NULL;
+mDNSlocal HANDLE					gSMBThreadQuitEvent			= NULL;
+
+#define	kSMBStopEvent				( WAIT_OBJECT_0 + 0 )
+#define	kSMBRegisterEvent			( WAIT_OBJECT_0 + 1 )
+#define kSMBDeregisterEvent			( WAIT_OBJECT_0 + 2 )
+
+
+#if 0
+#pragma mark -
+#pragma mark == Platform Support ==
+#endif
+
+//===========================================================================================================================
+//	mDNSPlatformInit
+//===========================================================================================================================
+
+mDNSexport mStatus	mDNSPlatformInit( mDNS * const inMDNS )
+{
+	mStatus		err;
+	WSADATA		wsaData;
+	int			supported;
+	struct sockaddr_in	sa4;
+	struct sockaddr_in6 sa6;
+	int					sa4len;
+	int					sa6len;
+	DWORD				size;
+	DWORD				val;
+	
+	dlog( kDebugLevelTrace, DEBUG_NAME "platform init\n" );
+	
+	// Initialize variables. If the PlatformSupport pointer is not null then just assume that a non-Apple client is 
+	// calling mDNS_Init and wants to provide its own storage for the platform-specific data so do not overwrite it.
+	
+	mDNSPlatformMemZero( &gMDNSPlatformSupport, sizeof( gMDNSPlatformSupport ) );
+	if( !inMDNS->p ) inMDNS->p				= &gMDNSPlatformSupport;
+	inMDNS->p->mainThread					= OpenThread( THREAD_ALL_ACCESS, FALSE, GetCurrentThreadId() );
+	require_action( inMDNS->p->mainThread, exit, err = mStatus_UnknownErr );
+	inMDNS->p->checkFileSharesTimer = CreateWaitableTimer( NULL, FALSE, NULL );
+	require_action( inMDNS->p->checkFileSharesTimer, exit, err = mStatus_UnknownErr );
+	inMDNS->p->checkFileSharesTimeout		= 10;		// Retry time for CheckFileShares() in seconds
+	mDNSPlatformOneSecond 					= 1000;		// Use milliseconds as the quantum of time
+	InitLinkedList( &gTCPDispatchableSockets, offsetof( TCPSocket, nextDispatchable ) );
+	InitLinkedList( &gUDPDispatchableSockets, offsetof( UDPSocket, nextDispatchable ) );
+	
+	// Startup WinSock 2.2 or later.
+	
+	err = WSAStartup( MAKEWORD( kWinSockMajorMin, kWinSockMinorMin ), &wsaData );
+	require_noerr( err, exit );
+	
+	supported = ( ( LOBYTE( wsaData.wVersion ) == kWinSockMajorMin ) && ( HIBYTE( wsaData.wVersion ) == kWinSockMinorMin ) );
+	require_action( supported, exit, err = mStatus_UnsupportedErr );
+	
+	inMDNS->CanReceiveUnicastOn5353 = CanReceiveUnicast();
+	
+	// Setup the HINFO HW strings.
+	//<rdar://problem/7245119> device-info should have model=Windows
+
+	strcpy_s( ( char* ) &inMDNS->HIHardware.c[ 1 ], sizeof( inMDNS->HIHardware.c ) - 2, "Windows" );
+	inMDNS->HIHardware.c[ 0 ] = ( mDNSu8 ) mDNSPlatformStrLen( &inMDNS->HIHardware.c[ 1 ] );
+	dlog( kDebugLevelInfo, DEBUG_NAME "HIHardware: %#s\n", inMDNS->HIHardware.c );
+
+	// Setup the HINFO SW strings.
+#if ( MDNS_SET_HINFO_STRINGS )
+	mDNS_snprintf( (char *) &inMDNS->HISoftware.c[ 1 ], sizeof( inMDNS->HISoftware.c ) - 2, 
+		"mDNSResponder (%s %s)", __DATE__, __TIME__ );
+	inMDNS->HISoftware.c[ 0 ] = (mDNSu8) mDNSPlatformStrLen( &inMDNS->HISoftware.c[ 1 ] );
+	dlog( kDebugLevelInfo, DEBUG_NAME "HISoftware: %#s\n", inMDNS->HISoftware.c );
+#endif
+
+	// Set the thread global overlapped flag
+
+	val = 0;
+	err = setsockopt( INVALID_SOCKET, SOL_SOCKET, SO_OPENTYPE, ( char* ) &val, sizeof( val ) );
+	err = translate_errno( err != SOCKET_ERROR, WSAGetLastError(), kUnknownErr );
+	require_noerr( err, exit );
+
+	// Set up the IPv4 unicast socket
+
+	inMDNS->p->unicastSock4.fd			= INVALID_SOCKET;
+	inMDNS->p->unicastSock4.recvMsgPtr	= NULL;
+	inMDNS->p->unicastSock4.ifd			= NULL;
+	inMDNS->p->unicastSock4.overlapped.pending = FALSE;
+	inMDNS->p->unicastSock4.next		= NULL;
+	inMDNS->p->unicastSock4.m			= inMDNS;
+
+#if ( MDNS_WINDOWS_ENABLE_IPV4 )
+
+	sa4.sin_family		= AF_INET;
+	sa4.sin_addr.s_addr = INADDR_ANY;
+	err = SetupSocket( inMDNS, (const struct sockaddr*) &sa4, zeroIPPort, &inMDNS->p->unicastSock4.fd );
+	check_noerr( err );
+	sa4len = sizeof( sa4 );
+	err = getsockname( inMDNS->p->unicastSock4.fd, (struct sockaddr*) &sa4, &sa4len );
+	require_noerr( err, exit );
+	inMDNS->p->unicastSock4.port.NotAnInteger = sa4.sin_port;
+	inMDNS->UnicastPort4 = inMDNS->p->unicastSock4.port;
+	err = WSAIoctl( inMDNS->p->unicastSock4.fd, SIO_GET_EXTENSION_FUNCTION_POINTER, &kWSARecvMsgGUID, sizeof( kWSARecvMsgGUID ), &inMDNS->p->unicastSock4.recvMsgPtr, sizeof( inMDNS->p->unicastSock4.recvMsgPtr ), &size, NULL, NULL );
+		
+	if ( err )
+	{
+		inMDNS->p->unicastSock4.recvMsgPtr = NULL;
+	}
+
+	err = UDPBeginRecv( &inMDNS->p->unicastSock4 );
+	require_noerr( err, exit ); 
+
+#endif
+
+	// Set up the IPv6 unicast socket
+
+	inMDNS->p->unicastSock6.fd			= INVALID_SOCKET;
+	inMDNS->p->unicastSock6.recvMsgPtr	= NULL;
+	inMDNS->p->unicastSock6.ifd			= NULL;
+	inMDNS->p->unicastSock6.overlapped.pending = FALSE;
+	inMDNS->p->unicastSock6.next		= NULL;
+	inMDNS->p->unicastSock6.m			= inMDNS;
+
+#if ( MDNS_WINDOWS_ENABLE_IPV6 )
+
+	sa6.sin6_family		= AF_INET6;
+	sa6.sin6_addr		= in6addr_any;
+	sa6.sin6_scope_id	= 0;
+
+	// This call will fail if the machine hasn't installed IPv6.  In that case,
+	// the error will be WSAEAFNOSUPPORT.
+
+	err = SetupSocket( inMDNS, (const struct sockaddr*) &sa6, zeroIPPort, &inMDNS->p->unicastSock6.fd );
+	require_action( !err || ( err == WSAEAFNOSUPPORT ), exit, err = (mStatus) WSAGetLastError() );
+	err = kNoErr;
+	
+	// If we weren't able to create the socket (because IPv6 hasn't been installed) don't do this
+
+	if ( inMDNS->p->unicastSock6.fd != INVALID_SOCKET )
+	{
+		sa6len = sizeof( sa6 );
+		err = getsockname( inMDNS->p->unicastSock6.fd, (struct sockaddr*) &sa6, &sa6len );
+		require_noerr( err, exit );
+		inMDNS->p->unicastSock6.port.NotAnInteger = sa6.sin6_port;
+		inMDNS->UnicastPort6 = inMDNS->p->unicastSock6.port;
+
+		err = WSAIoctl( inMDNS->p->unicastSock6.fd, SIO_GET_EXTENSION_FUNCTION_POINTER, &kWSARecvMsgGUID, sizeof( kWSARecvMsgGUID ), &inMDNS->p->unicastSock6.recvMsgPtr, sizeof( inMDNS->p->unicastSock6.recvMsgPtr ), &size, NULL, NULL );
+		
+		if ( err != 0 )
+		{
+			inMDNS->p->unicastSock6.recvMsgPtr = NULL;
+		}
+
+		err = UDPBeginRecv( &inMDNS->p->unicastSock6 );
+		require_noerr( err, exit );
+	} 
+
+#endif
+
+	// Notify core of domain secret keys
+
+	SetDomainSecrets( inMDNS );
+	
+	// Success!
+
+	mDNSCoreInitComplete( inMDNS, err );
+
+	
+exit:
+
+	if ( err )
+	{
+		mDNSPlatformClose( inMDNS );
+	}
+
+	dlog( kDebugLevelTrace, DEBUG_NAME "platform init done (err=%d %m)\n", err, err );
+	return( err );
+}
+
+//===========================================================================================================================
+//	mDNSPlatformClose
+//===========================================================================================================================
+
+mDNSexport void	mDNSPlatformClose( mDNS * const inMDNS )
+{
+	mStatus		err;
+	
+	dlog( kDebugLevelTrace, DEBUG_NAME "platform close\n" );
+	check( inMDNS );
+
+	if ( gSMBThread != NULL )
+	{
+		dlog( kDebugLevelTrace, DEBUG_NAME "tearing down smb registration thread\n" );
+		SetEvent( gSMBThreadStopEvent );
+		
+		if ( WaitForSingleObject( gSMBThreadQuitEvent, 5 * 1000 ) == WAIT_OBJECT_0 )
+		{
+			if ( gSMBThreadQuitEvent )
+			{
+				CloseHandle( gSMBThreadQuitEvent );
+				gSMBThreadQuitEvent = NULL;
+			}
+
+			if ( gSMBThreadStopEvent )
+			{
+				CloseHandle( gSMBThreadStopEvent );
+				gSMBThreadStopEvent = NULL;
+			}
+
+			if ( gSMBThreadDeregisterEvent )
+			{
+				CloseHandle( gSMBThreadDeregisterEvent );
+				gSMBThreadDeregisterEvent = NULL;
+			}
+
+			if ( gSMBThreadRegisterEvent )
+			{
+				CloseHandle( gSMBThreadRegisterEvent );
+				gSMBThreadRegisterEvent = NULL;
+			}
+
+			if ( gDNSSDLibrary )
+			{
+				FreeLibrary( gDNSSDLibrary );
+				gDNSSDLibrary = NULL;
+			}	
+		}
+		else
+		{
+			LogMsg( "Unable to stop SMBThread" );
+		}
+
+		inMDNS->p->smbFileSharing = mDNSfalse;
+		inMDNS->p->smbPrintSharing = mDNSfalse;
+	}
+
+	// Tear everything down in reverse order to how it was set up.
+	
+	err = TearDownInterfaceList( inMDNS );
+	check_noerr( err );
+	check( !inMDNS->p->inactiveInterfaceList );
+
+#if ( MDNS_WINDOWS_ENABLE_IPV4 )
+
+	UDPCloseSocket( &inMDNS->p->unicastSock4 );
+
+#endif
+	
+#if ( MDNS_WINDOWS_ENABLE_IPV6 )
+
+	UDPCloseSocket( &inMDNS->p->unicastSock6 );
+
+#endif
+
+	// Free the DLL needed for IPv6 support.
+	
+#if( MDNS_WINDOWS_USE_IPV6_IF_ADDRS )
+	if( gIPHelperLibraryInstance )
+	{
+		gGetAdaptersAddressesFunctionPtr = NULL;
+		
+		FreeLibrary( gIPHelperLibraryInstance );
+		gIPHelperLibraryInstance = NULL;
+	}
+#endif
+
+	if ( g_hAAPI32 )
+	{
+		// Release any resources
+
+		if ( g_hProvider && g_lpCryptReleaseContext )
+		{
+			( g_lpCryptReleaseContext )( g_hProvider, 0 );
+		}
+
+		// Free the AdvApi32.dll
+
+		FreeLibrary( g_hAAPI32 );
+
+		// And reset all the data
+
+		g_lpCryptAcquireContext = NULL;
+		g_lpCryptReleaseContext = NULL;
+		g_lpCryptGenRandom 		= NULL;
+		g_hProvider 			= ( ULONG_PTR ) NULL;
+		g_hAAPI32				= NULL;
+	}
+
+	// Clear out the APC queue
+
+	while ( SleepEx( 0, TRUE ) == WAIT_IO_COMPLETION )
+	{
+		DispatchSocketEvents( inMDNS );
+	}
+
+	WSACleanup();
+	
+	dlog( kDebugLevelTrace, DEBUG_NAME "platform close done\n" );
+}
+
+
+//===========================================================================================================================
+//	mDNSPlatformLock
+//===========================================================================================================================
+
+mDNSexport void	mDNSPlatformLock( const mDNS * const inMDNS )
+{
+	( void ) inMDNS;
+}
+
+//===========================================================================================================================
+//	mDNSPlatformUnlock
+//===========================================================================================================================
+
+mDNSexport void	mDNSPlatformUnlock( const mDNS * const inMDNS )
+{
+	( void ) inMDNS;
+}
+
+//===========================================================================================================================
+//	mDNSPlatformStrCopy
+//===========================================================================================================================
+
+mDNSexport void	mDNSPlatformStrCopy( void *inDst, const void *inSrc )
+{
+	check( inSrc );
+	check( inDst );
+	
+	strcpy( (char *) inDst, (const char*) inSrc );
+}
+
+//===========================================================================================================================
+//	mDNSPlatformStrLCopy
+//===========================================================================================================================
+
+mDNSexport mDNSu32 mDNSPlatformStrLCopy(void *inDst, const void *inSrc, mDNSu32 inSize)
+{
+	const char *		src = (const char *) inSrc;
+	
+	if( inSize > 0 )
+	{
+		size_t		n;
+		char *		dst = (char *) inDst;
+		
+		for( n = inSize - 1; n > 0; --n )
+		{
+			if( ( *dst++ = *src++ ) == '\0' )
+			{
+				// Null terminator encountered, so exit.
+				goto exit;
+			}
+		}
+		*dst = '\0';
+	}
+	
+	while( *src++ != '\0' )
+	{
+		// Stop at null terminator.
+	}
+	
+exit:
+	return( (mDNSu32)( src - (const char *) inSrc ) - 1 );
+}
+
+//===========================================================================================================================
+//	mDNSPlatformStrLen
+//===========================================================================================================================
+
+mDNSexport mDNSu32	mDNSPlatformStrLen( const void *inSrc )
+{
+	check( inSrc );
+	
+	return( (mDNSu32) strlen( (const char *) inSrc ) );
+}
+
+//===========================================================================================================================
+//	mDNSPlatformMemCopy
+//===========================================================================================================================
+
+mDNSexport void	mDNSPlatformMemCopy( void *inDst, const void *inSrc, mDNSu32 inSize )
+{
+	check( inSrc );
+	check( inDst );
+	
+	memcpy( inDst, inSrc, inSize );
+}
+
+//===========================================================================================================================
+//	mDNSPlatformMemSame
+//===========================================================================================================================
+
+mDNSexport mDNSBool	mDNSPlatformMemSame( const void *inDst, const void *inSrc, mDNSu32 inSize )
+{
+	check( inSrc );
+	check( inDst );
+	
+	return( (mDNSBool)( memcmp( inSrc, inDst, inSize ) == 0 ) );
+}
+
+//===========================================================================================================================
+//	mDNSPlatformMemZero
+//===========================================================================================================================
+
+mDNSexport void	mDNSPlatformMemZero( void *inDst, mDNSu32 inSize )
+{
+	check( inDst );
+	
+	memset( inDst, 0, inSize );
+}
+
+//===========================================================================================================================
+//	mDNSPlatformMemAllocate
+//===========================================================================================================================
+
+mDNSexport void *	mDNSPlatformMemAllocate( mDNSu32 inSize )
+{
+	void *		mem;
+	
+	check( inSize > 0 );
+	
+	mem = malloc( inSize );
+	check( mem );
+	
+	return( mem );
+}
+
+//===========================================================================================================================
+//	mDNSPlatformMemFree
+//===========================================================================================================================
+
+mDNSexport void	mDNSPlatformMemFree( void *inMem )
+{
+	check( inMem );
+	
+	free( inMem );
+}
+
+//===========================================================================================================================
+//	mDNSPlatformRandomNumber
+//===========================================================================================================================
+
+mDNSexport mDNSu32 mDNSPlatformRandomNumber(void)
+{
+	mDNSu32		randomNumber = 0;
+	BOOL		bResult;
+	OSStatus	err = 0;
+
+	if ( !g_hAAPI32 )
+	{
+		g_hAAPI32 = LoadLibrary( TEXT("AdvAPI32.dll") );
+		err = translate_errno( g_hAAPI32 != NULL, GetLastError(), mStatus_UnknownErr );
+		require_noerr( err, exit );
+	}
+
+	// Function Pointer: CryptAcquireContext
+
+	if ( !g_lpCryptAcquireContext )
+	{
+		g_lpCryptAcquireContext = ( fnCryptAcquireContext )
+#ifdef UNICODE
+			( GetProcAddress( g_hAAPI32, "CryptAcquireContextW" ) );
+#else
+			( GetProcAddress( g_hAAPI32, "CryptAcquireContextA" ) );
+#endif
+		err = translate_errno( g_lpCryptAcquireContext != NULL, GetLastError(), mStatus_UnknownErr );
+		require_noerr( err, exit );
+	}
+
+	// Function Pointer: CryptReleaseContext
+
+	if ( !g_lpCryptReleaseContext )
+	{
+		g_lpCryptReleaseContext = ( fnCryptReleaseContext )
+         ( GetProcAddress( g_hAAPI32, "CryptReleaseContext" ) );
+		err = translate_errno( g_lpCryptReleaseContext != NULL, GetLastError(), mStatus_UnknownErr );
+		require_noerr( err, exit );
+	}
+      
+	// Function Pointer: CryptGenRandom
+
+	if ( !g_lpCryptGenRandom )
+	{
+		g_lpCryptGenRandom = ( fnCryptGenRandom )
+          ( GetProcAddress( g_hAAPI32, "CryptGenRandom" ) );
+		err = translate_errno( g_lpCryptGenRandom != NULL, GetLastError(), mStatus_UnknownErr );
+		require_noerr( err, exit );
+	}
+
+	// Setup
+
+	if ( !g_hProvider )
+	{
+		bResult = (*g_lpCryptAcquireContext)( &g_hProvider, NULL, NULL, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET );
+
+		if ( !bResult )
+		{
+			bResult =  ( *g_lpCryptAcquireContext)( &g_hProvider, NULL, NULL, PROV_RSA_FULL, CRYPT_MACHINE_KEYSET | CRYPT_NEWKEYSET );
+			err = translate_errno( bResult, GetLastError(), mStatus_UnknownErr );
+			require_noerr( err, exit );
+		}
+	}
+
+	bResult = (*g_lpCryptGenRandom)( g_hProvider, sizeof( randomNumber ), ( BYTE* ) &randomNumber );
+	err = translate_errno( bResult, GetLastError(), mStatus_UnknownErr );
+	require_noerr( err, exit );
+
+exit:
+
+	if ( err )
+	{
+		randomNumber = rand();
+	}
+
+	return randomNumber;
+}
+
+//===========================================================================================================================
+//	mDNSPlatformTimeInit
+//===========================================================================================================================
+
+mDNSexport mStatus	mDNSPlatformTimeInit( void )
+{
+	// No special setup is required on Windows -- we just use GetTickCount().
+	return( mStatus_NoError );
+}
+
+//===========================================================================================================================
+//	mDNSPlatformRawTime
+//===========================================================================================================================
+
+mDNSexport mDNSs32	mDNSPlatformRawTime( void )
+{
+	return( (mDNSs32) GetTickCount() );
+}
+
+//===========================================================================================================================
+//	mDNSPlatformUTC
+//===========================================================================================================================
+
+mDNSexport mDNSs32	mDNSPlatformUTC( void )
+{
+	return ( mDNSs32 ) time( NULL );
+}
+
+//===========================================================================================================================
+//	mDNSPlatformInterfaceNameToID
+//===========================================================================================================================
+
+mDNSexport mStatus	mDNSPlatformInterfaceNameToID( mDNS * const inMDNS, const char *inName, mDNSInterfaceID *outID )
+{
+	mStatus					err;
+	mDNSInterfaceData *		ifd;
+	
+	check( inMDNS );
+	check( inMDNS->p );
+	check( inName );
+	
+	// Search for an interface with the specified name,
+	
+	for( ifd = inMDNS->p->interfaceList; ifd; ifd = ifd->next )
+	{
+		if( strcmp( ifd->name, inName ) == 0 )
+		{
+			break;
+		}
+	}
+	require_action_quiet( ifd, exit, err = mStatus_NoSuchNameErr );
+	
+	// Success!
+	
+	if( outID )
+	{
+		*outID = (mDNSInterfaceID) ifd;
+	}
+	err = mStatus_NoError;
+	
+exit:
+	return( err );
+}
+
+//===========================================================================================================================
+//	mDNSPlatformInterfaceIDToInfo
+//===========================================================================================================================
+
+mDNSexport mStatus	mDNSPlatformInterfaceIDToInfo( mDNS * const inMDNS, mDNSInterfaceID inID, mDNSPlatformInterfaceInfo *outInfo )
+{
+	mStatus					err;
+	mDNSInterfaceData *		ifd;
+	
+	check( inMDNS );
+	check( inID );
+	check( outInfo );
+	
+	// Search for an interface with the specified ID,
+	
+	for( ifd = inMDNS->p->interfaceList; ifd; ifd = ifd->next )
+	{
+		if( ifd == (mDNSInterfaceData *) inID )
+		{
+			break;
+		}
+	}
+	require_action_quiet( ifd, exit, err = mStatus_NoSuchNameErr );
+	
+	// Success!
+	
+	outInfo->name 	= ifd->name;
+	outInfo->ip 	= ifd->interfaceInfo.ip;
+	err 			= mStatus_NoError;
+	
+exit:
+	return( err );
+}
+
+//===========================================================================================================================
+//	mDNSPlatformInterfaceIDfromInterfaceIndex
+//===========================================================================================================================
+
+mDNSexport mDNSInterfaceID	mDNSPlatformInterfaceIDfromInterfaceIndex( mDNS * const inMDNS, mDNSu32 inIndex )
+{
+	mDNSInterfaceID		id;
+	
+	id = mDNSNULL;
+	if( inIndex == kDNSServiceInterfaceIndexLocalOnly )
+	{
+		id = mDNSInterface_LocalOnly;
+	}
+	/* uncomment if Windows ever supports P2P
+	else if( inIndex == kDNSServiceInterfaceIndexP2P )
+	{
+		id = mDNSInterface_P2P;
+	}
+	*/
+	else if( inIndex != 0 )
+	{
+		mDNSInterfaceData *		ifd;
+		
+		for( ifd = inMDNS->p->interfaceList; ifd; ifd = ifd->next )
+		{
+			if( ( ifd->scopeID == inIndex ) && ifd->interfaceInfo.InterfaceActive )
+			{
+				id = ifd->interfaceInfo.InterfaceID;
+				break;
+			}
+		}
+		check( ifd );
+	}
+	return( id );
+}
+
+//===========================================================================================================================
+//	mDNSPlatformInterfaceIndexfromInterfaceID
+//===========================================================================================================================
+	
+mDNSexport mDNSu32	mDNSPlatformInterfaceIndexfromInterfaceID( mDNS * const inMDNS, mDNSInterfaceID inID, mDNSBool suppressNetworkChange )
+{
+	mDNSu32		index;
+	(void) suppressNetworkChange; // Unused
+	
+	index = 0;
+	if( inID == mDNSInterface_LocalOnly )
+	{
+		index = (mDNSu32) kDNSServiceInterfaceIndexLocalOnly;
+	}
+	/* uncomment if Windows ever supports P2P
+	else if( inID == mDNSInterface_P2P )
+	{
+		index = (mDNSu32) kDNSServiceInterfaceIndexP2P;
+	}
+	*/
+	else if( inID )
+	{
+		mDNSInterfaceData *		ifd;
+		
+		// Search active interfaces.
+		for( ifd = inMDNS->p->interfaceList; ifd; ifd = ifd->next )
+		{
+			if( (mDNSInterfaceID) ifd == inID )
+			{
+				index = ifd->scopeID;
+				break;
+			}
+		}
+		
+		// Search inactive interfaces too so remove events for inactive interfaces report the old interface index.
+		
+		if( !ifd )
+		{
+			for( ifd = inMDNS->p->inactiveInterfaceList; ifd; ifd = ifd->next )
+			{
+				if( (mDNSInterfaceID) ifd == inID )
+				{
+					index = ifd->scopeID;
+					break;
+				}
+			}
+		}
+		check( ifd );
+	}
+	return( index );
+}
+
+
+//===========================================================================================================================
+//	mDNSPlatformTCPSocket
+//===========================================================================================================================
+
+TCPSocket *
+mDNSPlatformTCPSocket
+	(
+	mDNS			* const m,
+	TCPSocketFlags		flags,
+	mDNSIPPort			*	port 
+	)
+{
+	TCPSocket *		sock    = NULL;
+	u_long				on		= 1;  // "on" for setsockopt
+	struct sockaddr_in	saddr;
+	int					len;
+	mStatus				err		= mStatus_NoError;
+
+	DEBUG_UNUSED( m );
+
+	require_action( flags == 0, exit, err = mStatus_UnsupportedErr );
+
+	// Setup connection data object
+
+	sock = (TCPSocket *) malloc( sizeof( TCPSocket ) );
+	require_action( sock, exit, err = mStatus_NoMemoryErr );
+	mDNSPlatformMemZero( sock, sizeof( TCPSocket ) );
+	sock->fd		= INVALID_SOCKET;
+	sock->flags		= flags;
+	sock->m			= m;
+
+	mDNSPlatformMemZero(&saddr, sizeof(saddr));
+	saddr.sin_family		= AF_INET;
+	saddr.sin_addr.s_addr	= htonl( INADDR_ANY );
+	saddr.sin_port			= port->NotAnInteger;
+	
+	// Create the socket
+
+	sock->fd = socket(AF_INET, SOCK_STREAM, 0);
+	err = translate_errno( sock->fd != INVALID_SOCKET, WSAGetLastError(), mStatus_UnknownErr );
+	require_noerr( err, exit );
+
+	// bind
+
+	err = bind( sock->fd, ( struct sockaddr* ) &saddr, sizeof( saddr )  );
+	err = translate_errno( err == 0, WSAGetLastError(), mStatus_UnknownErr );
+	require_noerr( err, exit );
+
+	// Set it to be non-blocking
+
+	err = ioctlsocket( sock->fd, FIONBIO, &on );
+	err = translate_errno( err == 0, WSAGetLastError(), mStatus_UnknownErr );
+	require_noerr( err, exit );
+
+	// Get port number
+
+	mDNSPlatformMemZero( &saddr, sizeof( saddr ) );
+	len = sizeof( saddr );
+
+	err = getsockname( sock->fd, ( struct sockaddr* ) &saddr, &len );
+	err = translate_errno( err == 0, WSAGetLastError(), mStatus_UnknownErr );
+	require_noerr( err, exit );
+
+	port->NotAnInteger = saddr.sin_port;
+
+exit:
+
+	if ( err && sock )
+	{
+		TCPFreeSocket( sock );
+		sock = mDNSNULL;
+	}
+
+	return sock;
+} 
+
+//===========================================================================================================================
+//	mDNSPlatformTCPConnect
+//===========================================================================================================================
+
+mStatus
+mDNSPlatformTCPConnect
+	(
+	TCPSocket			*	sock,
+	const mDNSAddr		*	inDstIP, 
+	mDNSOpaque16 			inDstPort, 
+	domainname          *   hostname,
+	mDNSInterfaceID			inInterfaceID,
+	TCPConnectionCallback	inCallback, 
+	void *					inContext
+	)
+{
+	struct sockaddr_in	saddr;
+	mStatus				err		= mStatus_NoError;
+
+	DEBUG_UNUSED( inInterfaceID );
+	( void ) hostname;
+
+	if ( inDstIP->type != mDNSAddrType_IPv4 )
+	{
+		LogMsg("ERROR: mDNSPlatformTCPConnect - attempt to connect to an IPv6 address: operation not supported");
+		return mStatus_UnknownErr;
+	}
+
+	// Setup connection data object
+
+	sock->readEventHandler	= TCPCanRead;
+	sock->userCallback		= inCallback;
+	sock->userContext		= inContext;
+
+	mDNSPlatformMemZero(&saddr, sizeof(saddr));
+	saddr.sin_family	= AF_INET;
+	saddr.sin_port		= inDstPort.NotAnInteger;
+	memcpy(&saddr.sin_addr, &inDstIP->ip.v4.NotAnInteger, sizeof(saddr.sin_addr));
+
+	// Try and do connect
+
+	err = connect( sock->fd, ( struct sockaddr* ) &saddr, sizeof( saddr ) );
+	require_action( !err || ( WSAGetLastError() == WSAEWOULDBLOCK ), exit, err = mStatus_ConnFailed );
+	sock->connected	= !err ? TRUE : FALSE;
+
+	if ( sock->connected )
+	{
+		err = TCPAddSocket( sock->m, sock );
+		require_noerr( err, exit );
+	}
+	else
+	{
+		require_action( sock->m->p->registerWaitableEventFunc != NULL, exit, err = mStatus_ConnFailed );
+
+		sock->connectEvent	= CreateEvent( NULL, FALSE, FALSE, NULL );
+		err = translate_errno( sock->connectEvent, GetLastError(), mStatus_UnknownErr );
+		require_noerr( err, exit );
+
+		err = WSAEventSelect( sock->fd, sock->connectEvent, FD_CONNECT );
+		require_noerr( err, exit );
+
+		err = sock->m->p->registerWaitableEventFunc( sock->m, sock->connectEvent, sock, TCPDidConnect );
+		require_noerr( err, exit );
+	}
+
+exit:
+
+	if ( !err )
+	{
+		err = sock->connected ? mStatus_ConnEstablished : mStatus_ConnPending;
+	}
+
+	return err;
+}
+
+//===========================================================================================================================
+//	mDNSPlatformTCPAccept
+//===========================================================================================================================
+
+mDNSexport 
+mDNSexport TCPSocket *mDNSPlatformTCPAccept( TCPSocketFlags flags, int fd )
+	{
+	TCPSocket	*	sock = NULL;
+	mStatus							err = mStatus_NoError;
+
+	require_action( !flags, exit, err = mStatus_UnsupportedErr );
+
+	sock = malloc( sizeof( TCPSocket ) );
+	require_action( sock, exit, err = mStatus_NoMemoryErr );
+	
+	mDNSPlatformMemZero( sock, sizeof( *sock ) );
+
+	sock->fd	= fd;
+	sock->flags = flags;
+
+exit:
+
+	if ( err && sock )
+	{
+		free( sock );
+		sock = NULL;
+	}
+
+	return sock;
+	}
+
+
+//===========================================================================================================================
+//	mDNSPlatformTCPCloseConnection
+//===========================================================================================================================
+
+mDNSexport void	mDNSPlatformTCPCloseConnection( TCPSocket *sock )
+{
+	check( sock );
+
+	if ( sock->connectEvent && sock->m->p->unregisterWaitableEventFunc )
+	{
+		sock->m->p->unregisterWaitableEventFunc( sock->m, sock->connectEvent );
+	}
+
+	if ( sock->fd != INVALID_SOCKET )
+	{
+		TCPCloseSocket( sock );
+
+		QueueUserAPC( ( PAPCFUNC ) TCPFreeSocket, sock->m->p->mainThread, ( ULONG_PTR ) sock );
+	}
+}
+
+
+//===========================================================================================================================
+//	mDNSPlatformReadTCP
+//===========================================================================================================================
+
+mDNSexport long	mDNSPlatformReadTCP( TCPSocket *sock, void *inBuffer, unsigned long inBufferSize, mDNSBool * closed )
+{
+	unsigned long	bytesLeft;
+	int				wsaError;
+	long			ret;
+
+	*closed = sock->closed;
+	wsaError = sock->lastError;
+	ret = -1;
+
+	if ( *closed )
+	{
+		ret = 0;
+	}
+	else if ( sock->lastError == 0 )
+	{
+		// First check to see if we have any data left in our buffer
+
+		bytesLeft = ( DWORD ) ( sock->eptr - sock->bptr );
+
+		if ( bytesLeft )
+		{
+			unsigned long bytesToCopy = ( bytesLeft < inBufferSize ) ? bytesLeft : inBufferSize;
+
+			memcpy( inBuffer, sock->bptr, bytesToCopy );
+			sock->bptr += bytesToCopy;
+
+			if ( !sock->overlapped.pending && ( sock->bptr == sock->eptr ) )
+			{
+				sock->bptr = sock->bbuf;
+				sock->eptr = sock->bbuf;
+			}
+
+			ret = bytesToCopy;
+		}
+		else
+		{
+			wsaError = WSAEWOULDBLOCK;
+		}
+	}
+
+	// Always set the last winsock error, so that we don't inadvertently use a previous one		
+	
+	WSASetLastError( wsaError );
+
+	return ret;
+}
+
+
+//===========================================================================================================================
+//	mDNSPlatformWriteTCP
+//===========================================================================================================================
+
+mDNSexport long	mDNSPlatformWriteTCP( TCPSocket *sock, const char *inMsg, unsigned long inMsgSize )
+{
+	int			nsent;
+	OSStatus	err;
+
+	nsent = send( sock->fd, inMsg, inMsgSize, 0 );
+
+	err = translate_errno( ( nsent >= 0 ) || ( WSAGetLastError() == WSAEWOULDBLOCK ), WSAGetLastError(), mStatus_UnknownErr );
+	require_noerr( err, exit );
+
+	if ( nsent < 0)
+	{
+		nsent = 0;
+	}
+		
+exit:
+
+	return nsent;
+}
+
+//===========================================================================================================================
+//	mDNSPlatformTCPGetFD
+//===========================================================================================================================
+
+mDNSexport int mDNSPlatformTCPGetFD(TCPSocket *sock )
+{
+	return ( int ) sock->fd;
+}
+
+
+//===========================================================================================================================
+//	TCPAddConnection
+//===========================================================================================================================
+
+mStatus TCPAddSocket( mDNS * const inMDNS, TCPSocket *sock )
+{
+	mStatus err;
+
+	( void ) inMDNS;
+
+	sock->bptr	= sock->bbuf;
+	sock->eptr	= sock->bbuf;
+	sock->ebuf	= sock->bbuf + sizeof( sock->bbuf );
+
+	dlog( kDebugLevelChatty, DEBUG_NAME "adding TCPSocket 0x%x:%d\n", sock, sock->fd );
+	err = TCPBeginRecv( sock );
+	require_noerr( err, exit );
+
+exit:
+
+	return err;
+}
+
+
+//===========================================================================================================================
+//	TCPDidConnect
+//===========================================================================================================================
+
+mDNSlocal void TCPDidConnect( mDNS * const inMDNS, HANDLE event, void * context )
+{
+	TCPSocket * sock = ( TCPSocket* ) context;
+	TCPConnectionCallback callback = NULL;
+	WSANETWORKEVENTS sockEvent;
+	int err = kNoErr;
+
+	if ( inMDNS->p->unregisterWaitableEventFunc )
+	{
+		inMDNS->p->unregisterWaitableEventFunc( inMDNS, event );
+	}
+
+	if ( sock )
+	{
+		callback = ( TCPConnectionCallback ) sock->userCallback;
+		err = WSAEnumNetworkEvents( sock->fd, sock->connectEvent, &sockEvent );
+		require_noerr( err, exit );
+		require_action( sockEvent.lNetworkEvents & FD_CONNECT, exit, err = mStatus_UnknownErr );
+		require_action( sockEvent.iErrorCode[ FD_CONNECT_BIT ] == 0, exit, err = sockEvent.iErrorCode[ FD_CONNECT_BIT ] );
+
+		sock->connected	= mDNStrue;
+
+		if ( sock->fd != INVALID_SOCKET )
+		{
+			err = TCPAddSocket( sock->m, sock );
+			require_noerr( err, exit );
+		}
+
+		if ( callback )
+		{
+			callback( sock, sock->userContext, TRUE, 0 );
+		}
+	}
+
+exit:
+
+	if ( err && callback )
+	{
+		callback( sock, sock->userContext, TRUE, err );
+	}
+}
+
+
+
+//===========================================================================================================================
+//	TCPCanRead
+//===========================================================================================================================
+
+mDNSlocal void TCPCanRead( TCPSocket * sock )
+{
+	TCPConnectionCallback callback = ( TCPConnectionCallback ) sock->userCallback;
+
+	if ( callback )
+	{
+		callback( sock, sock->userContext, mDNSfalse, sock->lastError );
+	}
+}
+
+
+//===========================================================================================================================
+//	TCPBeginRecv
+//===========================================================================================================================
+
+mDNSlocal mStatus TCPBeginRecv( TCPSocket * sock )
+{
+	DWORD	bytesReceived	= 0;
+	DWORD	flags			= 0;
+	mStatus err;
+
+	dlog( kDebugLevelChatty, DEBUG_NAME "%s: sock = %d\n", __ROUTINE__, sock->fd );
+
+	check( !sock->overlapped.pending );
+
+	ZeroMemory( &sock->overlapped.data, sizeof( sock->overlapped.data ) );
+	sock->overlapped.data.hEvent = sock;
+
+	sock->overlapped.wbuf.buf = ( char* ) sock->eptr;
+	sock->overlapped.wbuf.len = ( ULONG) ( sock->ebuf - sock->eptr );
+	
+	err = WSARecv( sock->fd, &sock->overlapped.wbuf, 1, &bytesReceived, &flags, &sock->overlapped.data, ( LPWSAOVERLAPPED_COMPLETION_ROUTINE ) TCPEndRecv );
+	err = translate_errno( ( err == 0 ) || ( WSAGetLastError() == WSA_IO_PENDING ), WSAGetLastError(), kUnknownErr );
+	require_noerr( err, exit );
+
+	sock->overlapped.pending = TRUE;
+
+exit:
+
+	return err;
+}
+
+
+//===========================================================================================================================
+//	TCPEndRecv
+//===========================================================================================================================
+
+mDNSlocal void CALLBACK TCPEndRecv( DWORD error, DWORD bytesTransferred, LPWSAOVERLAPPED overlapped, DWORD flags )
+{
+	TCPSocket * sock;
+
+	( void ) flags;
+
+	dlog( kDebugLevelChatty, DEBUG_NAME "%s: error = %d, bytesTransferred = %d\n", __ROUTINE__, error, bytesTransferred );
+	sock = ( overlapped != NULL ) ? overlapped->hEvent : NULL;
+	require_action( sock, exit, error = ( DWORD ) mStatus_BadStateErr );
+	dlog( kDebugLevelChatty, DEBUG_NAME "%s: sock = %d\n", __ROUTINE__, sock->fd );
+	sock->overlapped.error				= error;
+	sock->overlapped.bytesTransferred	= bytesTransferred;
+	check( sock->overlapped.pending );
+	sock->overlapped.pending			= FALSE;
+
+	// Queue this socket
+
+	AddToTail( &gTCPDispatchableSockets, sock );
+
+exit:
+
+	return;
+}
+
+
+	
+//===========================================================================================================================
+//	mDNSPlatformUDPSocket
+//===========================================================================================================================
+
+mDNSexport UDPSocket* mDNSPlatformUDPSocket(mDNS *const m, const mDNSIPPort requestedport)
+{
+	UDPSocket*	sock	= NULL;
+	mDNSIPPort	port	= requestedport;
+	mStatus		err		= mStatus_NoError;
+	unsigned	i;
+
+	// Setup connection data object
+
+	sock = ( UDPSocket* ) malloc(sizeof( UDPSocket ) );
+	require_action( sock, exit, err = mStatus_NoMemoryErr );
+	memset( sock, 0, sizeof( UDPSocket ) );
+
+	// Create the socket
+
+	sock->fd					= INVALID_SOCKET;
+	sock->recvMsgPtr			= m->p->unicastSock4.recvMsgPtr;
+	sock->addr					= m->p->unicastSock4.addr;
+	sock->ifd					= NULL;
+	sock->overlapped.pending	= FALSE;
+	sock->m						= m;
+
+	// Try at most 10000 times to get a unique random port
+
+	for (i=0; i<10000; i++)
+	{
+		struct sockaddr_in saddr;
+
+		saddr.sin_family		= AF_INET;
+		saddr.sin_addr.s_addr	= 0;
+
+		// The kernel doesn't do cryptographically strong random port
+		// allocation, so we do it ourselves here
+
+        if (mDNSIPPortIsZero(requestedport))
+		{
+			port = mDNSOpaque16fromIntVal( ( mDNSu16 ) ( 0xC000 + mDNSRandom(0x3FFF) ) );
+		}
+
+		saddr.sin_port = port.NotAnInteger;
+
+        err = SetupSocket(m, ( struct sockaddr* ) &saddr, port, &sock->fd );
+        if (!err) break;
+	}
+
+	require_noerr( err, exit );
+
+	// Set the port
+
+	sock->port = port;
+
+	// Arm the completion routine
+
+	err = UDPBeginRecv( sock );
+	require_noerr( err, exit ); 
+
+	// Bookkeeping
+
+	sock->next		= gUDPSockets;
+	gUDPSockets		= sock;
+	gUDPNumSockets++;
+
+exit:
+
+	if ( err && sock )
+	{
+		UDPFreeSocket( sock );
+		sock = NULL;
+	}
+
+	return sock;
+}
+	
+//===========================================================================================================================
+//	mDNSPlatformUDPClose
+//===========================================================================================================================
+	
+mDNSexport void mDNSPlatformUDPClose( UDPSocket *sock )
+{
+	UDPSocket	*	current  = gUDPSockets;
+	UDPSocket	*	last = NULL;
+
+	while ( current )
+	{
+		if ( current == sock )
+		{
+			if ( last == NULL )
+			{
+				gUDPSockets = sock->next;
+			}
+			else
+			{
+				last->next = sock->next;
+			}
+
+			// Alertable I/O is great, except not so much when it comes to closing
+			// the socket.  Anything that has been previously queued for this socket
+			// will stay in the queue after you close the socket.  This is problematic
+			// for obvious reasons. So we'll attempt to workaround this by closing
+			// the socket which will prevent any further queued packets and then not calling
+			// UDPFreeSocket directly, but by queueing it using QueueUserAPC.  The queues
+			// are FIFO, so that will execute *after* any other previous items in the queue
+			//
+			// UDPEndRecv will check if the socket is valid, and if not, it will ignore
+			// the packet
+
+			UDPCloseSocket( sock );
+
+			QueueUserAPC( ( PAPCFUNC ) UDPFreeSocket, sock->m->p->mainThread, ( ULONG_PTR ) sock );
+
+			gUDPNumSockets--;
+
+			break;
+		}
+
+		last	= current;
+		current	= current->next;
+	}
+}
+
+
+//===========================================================================================================================
+//	mDNSPlatformSendUDP
+//===========================================================================================================================
+
+mDNSexport mStatus
+	mDNSPlatformSendUDP( 
+		const mDNS * const			inMDNS, 
+		const void * const	        inMsg, 
+		const mDNSu8 * const		inMsgEnd, 
+		mDNSInterfaceID 			inInterfaceID, 
+		UDPSocket *					inSrcSocket,
+		const mDNSAddr *			inDstIP, 
+		mDNSIPPort 					inDstPort )
+{
+	SOCKET						sendingsocket = INVALID_SOCKET;
+	mStatus						err = mStatus_NoError;
+	mDNSInterfaceData *			ifd = (mDNSInterfaceData*) inInterfaceID;
+	struct sockaddr_storage		addr;
+	int							n;
+	
+	DEBUG_USE_ONLY( inMDNS );
+	
+	n = (int)( inMsgEnd - ( (const mDNSu8 * const) inMsg ) );
+	check( inMDNS );
+	check( inMsg );
+	check( inMsgEnd );
+	check( inDstIP );
+	
+	dlog( kDebugLevelChatty, DEBUG_NAME "platform send %d bytes to %#a:%u\n", n, inDstIP, ntohs( inDstPort.NotAnInteger ) );
+	
+	if( inDstIP->type == mDNSAddrType_IPv4 )
+	{
+		struct sockaddr_in *		sa4;
+		
+		sa4						= (struct sockaddr_in *) &addr;
+		sa4->sin_family			= AF_INET;
+		sa4->sin_port			= inDstPort.NotAnInteger;
+		sa4->sin_addr.s_addr	= inDstIP->ip.v4.NotAnInteger;
+		sendingsocket           = ifd ? ifd->sock.fd : inMDNS->p->unicastSock4.fd;
+
+		if (inSrcSocket) { sendingsocket = inSrcSocket->fd; debugf("mDNSPlatformSendUDP using port %d, static port %d, sock %d", mDNSVal16(inSrcSocket->port), inMDNS->p->unicastSock4.fd, sendingsocket); }
+	}
+	else if( inDstIP->type == mDNSAddrType_IPv6 )
+	{
+		struct sockaddr_in6 *		sa6;
+		
+		sa6					= (struct sockaddr_in6 *) &addr;
+		sa6->sin6_family	= AF_INET6;
+		sa6->sin6_port		= inDstPort.NotAnInteger;
+		sa6->sin6_flowinfo	= 0;
+		sa6->sin6_addr		= *( (struct in6_addr *) &inDstIP->ip.v6 );
+		sa6->sin6_scope_id	= 0;	// Windows requires the scope ID to be zero. IPV6_MULTICAST_IF specifies interface.
+		sendingsocket		= ifd ? ifd->sock.fd : inMDNS->p->unicastSock6.fd;
+	}
+	else
+	{
+		dlog( kDebugLevelError, DEBUG_NAME "%s: dst is not an IPv4 or IPv6 address (type=%d)\n", __ROUTINE__, inDstIP->type );
+		err = mStatus_BadParamErr;
+		goto exit;
+	}
+	
+	if (IsValidSocket(sendingsocket))
+	{
+		n = sendto( sendingsocket, (char *) inMsg, n, 0, (struct sockaddr *) &addr, sizeof( addr ) );
+		err = translate_errno( n > 0, errno_compat(), kWriteErr );
+
+		if ( err )
+		{
+			// Don't report EHOSTDOWN (i.e. ARP failure), ENETDOWN, or no route to host for unicast destinations
+
+			if ( !mDNSAddressIsAllDNSLinkGroup( inDstIP ) && ( WSAGetLastError() == WSAEHOSTDOWN || WSAGetLastError() == WSAENETDOWN || WSAGetLastError() == WSAEHOSTUNREACH || WSAGetLastError() == WSAENETUNREACH ) )
+			{
+				err = mStatus_TransientErr;
+			}
+			else
+			{
+				require_noerr( err, exit );
+			}
+		}
+	}
+	
+exit:
+	return( err );
+}
+
+
+mDNSexport void mDNSPlatformUpdateProxyList(mDNS *const m, const mDNSInterfaceID InterfaceID)
+	{
+	DEBUG_UNUSED( m );
+	DEBUG_UNUSED( InterfaceID );
+	}
+
+//===========================================================================================================================
+//	mDNSPlatformSendRawPacket
+//===========================================================================================================================
+	
+mDNSexport void mDNSPlatformSetAllowSleep(mDNS *const m, mDNSBool allowSleep, const char *reason)

+    {

+    DEBUG_UNUSED( m );

+	DEBUG_UNUSED( allowSleep );

+	DEBUG_UNUSED( reason );

+    }

+
+mDNSexport void mDNSPlatformSendRawPacket(const void *const msg, const mDNSu8 *const end, mDNSInterfaceID InterfaceID)
+	{
+	DEBUG_UNUSED( msg );
+	DEBUG_UNUSED( end );
+	DEBUG_UNUSED( InterfaceID );
+	}
+
+mDNSexport void mDNSPlatformReceiveRawPacket(const void *const msg, const mDNSu8 *const end, mDNSInterfaceID InterfaceID)
+	{
+	DEBUG_UNUSED( msg );
+	DEBUG_UNUSED( end );
+	DEBUG_UNUSED( InterfaceID );
+	}
+
+mDNSexport void mDNSPlatformSetLocalAddressCacheEntry(mDNS *const m, const mDNSAddr *const tpa, const mDNSEthAddr *const tha, mDNSInterfaceID InterfaceID)
+	{
+	DEBUG_UNUSED( m );
+	DEBUG_UNUSED( tpa );
+	DEBUG_UNUSED( tha );
+	DEBUG_UNUSED( InterfaceID );
+	}
+
+mDNSexport void mDNSPlatformWriteDebugMsg(const char *msg)
+	{
+	dlog( kDebugLevelInfo, "%s\n", msg );
+	}
+
+mDNSexport void mDNSPlatformWriteLogMsg( const char * ident, const char * msg, mDNSLogLevel_t loglevel )
+	{
+	extern mDNS mDNSStorage;
+	int type;
+	
+	DEBUG_UNUSED( ident );
+
+	type = EVENTLOG_ERROR_TYPE;
+
+	switch (loglevel) 
+	{
+		case MDNS_LOG_MSG:       type = EVENTLOG_ERROR_TYPE;		break;
+		case MDNS_LOG_OPERATION: type = EVENTLOG_WARNING_TYPE;		break;
+		case MDNS_LOG_SPS:       type = EVENTLOG_INFORMATION_TYPE;  break;
+		case MDNS_LOG_INFO:      type = EVENTLOG_INFORMATION_TYPE;	break;
+		case MDNS_LOG_DEBUG:     type = EVENTLOG_INFORMATION_TYPE;	break;
+		default:
+			fprintf(stderr, "Unknown loglevel %d, assuming LOG_ERR\n", loglevel);
+			fflush(stderr);
+			}
+
+	mDNSStorage.p->reportStatusFunc( type, msg );
+	dlog( kDebugLevelInfo, "%s\n", msg );
+	}
+
+mDNSexport void mDNSPlatformSourceAddrForDest( mDNSAddr * const src, const mDNSAddr * const dst )
+	{
+	DEBUG_UNUSED( src );
+	DEBUG_UNUSED( dst );
+	}
+
+//===========================================================================================================================
+//	mDNSPlatformTLSSetupCerts
+//===========================================================================================================================
+
+mDNSexport mStatus
+mDNSPlatformTLSSetupCerts(void)
+{
+	return mStatus_UnsupportedErr;
+}
+
+//===========================================================================================================================
+//	mDNSPlatformTLSTearDownCerts
+//===========================================================================================================================
+
+mDNSexport void
+mDNSPlatformTLSTearDownCerts(void)
+{
+}
+
+//===========================================================================================================================
+//	mDNSPlatformSetDNSConfig
+//===========================================================================================================================
+
+mDNSlocal void SetDNSServers( mDNS *const m );
+mDNSlocal void SetSearchDomainList( void );
+
+mDNSexport void mDNSPlatformSetDNSConfig(mDNS *const m, mDNSBool setservers, mDNSBool setsearch, domainname *const fqdn, DNameListElem **regDomains, DNameListElem **browseDomains)
+{
+	if (setservers) SetDNSServers(m);
+	if (setsearch) SetSearchDomainList();
+	
+	if ( fqdn )
+	{
+		GetDDNSFQDN( fqdn );
+	}
+
+	if ( browseDomains )
+	{
+		GetDDNSDomains( browseDomains, kServiceParametersNode TEXT("\\DynDNS\\Setup\\") kServiceDynDNSBrowseDomains );
+	}
+
+	if ( regDomains )
+	{
+		GetDDNSDomains( regDomains, kServiceParametersNode TEXT("\\DynDNS\\Setup\\") kServiceDynDNSRegistrationDomains );
+	}
+}
+
+
+//===========================================================================================================================
+//	mDNSPlatformDynDNSHostNameStatusChanged
+//===========================================================================================================================
+
+mDNSexport void
+mDNSPlatformDynDNSHostNameStatusChanged(const domainname *const dname, const mStatus status)
+{
+	char		uname[MAX_ESCAPED_DOMAIN_NAME];
+	BYTE		bStatus;
+	LPCTSTR		name;
+	HKEY		key = NULL;
+	mStatus		err;
+	char	*	p;
+	
+	ConvertDomainNameToCString(dname, uname);
+	
+	p = uname;
+
+	while (*p)
+	{
+		*p = (char) tolower(*p);
+		if (!(*(p+1)) && *p == '.') *p = 0; // if last character, strip trailing dot
+		p++;
+	}
+
+	check( strlen( p ) <= MAX_ESCAPED_DOMAIN_NAME );
+	name = kServiceParametersNode TEXT("\\DynDNS\\State\\HostNames");
+	err = RegCreateKey( HKEY_LOCAL_MACHINE, name, &key );
+	require_noerr( err, exit );
+
+	bStatus = ( status ) ? 0 : 1;
+	err = RegSetValueEx( key, kServiceDynDNSStatus, 0, REG_DWORD, (const LPBYTE) &bStatus, sizeof(DWORD) );
+	require_noerr( err, exit );
+
+exit:
+
+	if ( key )
+	{
+		RegCloseKey( key );
+	}
+
+	return;
+}
+
+
+mDNSexport void FreeEtcHosts(mDNS *const m, AuthRecord *const rr, mStatus result)
+    {
+    (void)m;  // unused
+    (void)rr;
+    (void)result;
+    }
+
+
+
+//===========================================================================================================================
+//	SetDomainSecrets
+//===========================================================================================================================
+
+// This routine needs to be called whenever the system secrets database changes.
+// We call it from DynDNSConfigDidChange and mDNSPlatformInit
+
+void
+SetDomainSecrets( mDNS * const m )
+{
+	DomainAuthInfo *ptr;
+	domainname		fqdn;
+	DNameListElem * regDomains = NULL;
+
+	// Rather than immediately deleting all keys now, we mark them for deletion in ten seconds.
+	// In the case where the user simultaneously removes their DDNS host name and the key
+	// for it, this gives mDNSResponder ten seconds to gracefully delete the name from the
+	// server before it loses access to the necessary key. Otherwise, we'd leave orphaned
+	// address records behind that we no longer have permission to delete.
+	
+	for (ptr = m->AuthInfoList; ptr; ptr = ptr->next)
+		ptr->deltime = NonZeroTime(m->timenow + mDNSPlatformOneSecond*10);
+
+	GetDDNSFQDN( &fqdn );
+
+	if ( fqdn.c[ 0 ] )
+	{
+		SetDomainSecret( m, &fqdn );
+	}
+
+	GetDDNSDomains( &regDomains, kServiceParametersNode TEXT("\\DynDNS\\Setup\\") kServiceDynDNSRegistrationDomains );
+
+	while ( regDomains )
+	{
+		DNameListElem * current = regDomains;
+		SetDomainSecret( m, &current->name );
+		regDomains = regDomains->next;
+		free( current );
+	}
+}
+
+
+//===========================================================================================================================
+//	SetSearchDomainList
+//===========================================================================================================================
+
+mDNSlocal void SetDomainFromDHCP( void );
+mDNSlocal void SetReverseMapSearchDomainList( void );
+
+mDNSlocal void
+SetSearchDomainList( void )
+{
+	char			*	searchList	= NULL;
+	DWORD				searchListLen;
+	//DNameListElem	*	head = NULL;
+	//DNameListElem	*	current = NULL;
+	char			*	tok;
+	HKEY				key;
+	mStatus				err;
+
+	err = RegCreateKey( HKEY_LOCAL_MACHINE, TEXT("SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters"), &key );
+	require_noerr( err, exit );
+
+	err = RegQueryString( key, "SearchList", &searchList, &searchListLen, NULL );
+	require_noerr( err, exit );
+
+	// Windows separates the search domains with ','
+
+	tok = strtok( searchList, "," );
+	while ( tok )
+	{
+		if ( ( strcmp( tok, "" ) != 0 ) && ( strcmp( tok, "." ) != 0 ) )
+			mDNS_AddSearchDomain_CString(tok, mDNSNULL);
+		tok = strtok( NULL, "," );
+	}
+
+exit:
+
+	if ( searchList ) 
+	{
+		free( searchList );
+	}
+
+	if ( key )
+	{
+		RegCloseKey( key );
+	}
+
+	SetDomainFromDHCP();
+	SetReverseMapSearchDomainList();
+}
+
+
+//===========================================================================================================================
+//	SetReverseMapSearchDomainList
+//===========================================================================================================================
+
+mDNSlocal void
+SetReverseMapSearchDomainList( void )
+{
+	struct ifaddrs	*	ifa;
+
+	ifa = myGetIfAddrs( 1 );
+	while (ifa)
+	{
+		mDNSAddr addr;
+		
+		if (ifa->ifa_addr->sa_family == AF_INET && !SetupAddr(&addr, ifa->ifa_addr) && !(ifa->ifa_flags & IFF_LOOPBACK) && ifa->ifa_netmask)
+		{
+			mDNSAddr	netmask;
+			char		buffer[256];
+			
+			if (!SetupAddr(&netmask, ifa->ifa_netmask))
+			{
+				sprintf(buffer, "%d.%d.%d.%d.in-addr.arpa.", addr.ip.v4.b[3] & netmask.ip.v4.b[3],
+                                                             addr.ip.v4.b[2] & netmask.ip.v4.b[2],
+                                                             addr.ip.v4.b[1] & netmask.ip.v4.b[1],
+                                                             addr.ip.v4.b[0] & netmask.ip.v4.b[0]);
+				mDNS_AddSearchDomain_CString(buffer, mDNSNULL);
+			}
+		}
+	
+		ifa = ifa->ifa_next;
+	}
+
+	return;
+}
+
+
+//===========================================================================================================================
+//	SetDNSServers
+//===========================================================================================================================
+
+mDNSlocal void
+SetDNSServers( mDNS *const m )
+{
+	PIP_PER_ADAPTER_INFO	pAdapterInfo	=	NULL;
+	FIXED_INFO			*	fixedInfo	= NULL;
+	ULONG					bufLen		= 0;	
+	IP_ADDR_STRING		*	dnsServerList;
+	IP_ADDR_STRING		*	ipAddr;
+	DWORD					index;
+	int						i			= 0;
+	mStatus					err			= kUnknownErr;
+
+	// Get the primary interface.
+
+	index = GetPrimaryInterface();
+
+	// This should have the interface index of the primary index.  Fall back in cases where
+	// it can't be determined.
+
+	if ( index )
+	{
+		bufLen = 0;
+
+		for ( i = 0; i < 100; i++ )
+		{
+			err = GetPerAdapterInfo( index, pAdapterInfo, &bufLen );
+
+			if ( err != ERROR_BUFFER_OVERFLOW )
+			{
+				break;
+			}
+
+			pAdapterInfo = (PIP_PER_ADAPTER_INFO) realloc( pAdapterInfo, bufLen );
+			require_action( pAdapterInfo, exit, err = mStatus_NoMemoryErr );
+		}
+
+		require_noerr( err, exit );
+
+		dnsServerList = &pAdapterInfo->DnsServerList;
+	}
+	else
+	{
+		bufLen = sizeof( FIXED_INFO );
+
+		for ( i = 0; i < 100; i++ )
+		{
+			if ( fixedInfo )
+			{
+				GlobalFree( fixedInfo );
+				fixedInfo = NULL;
+			}
+
+			fixedInfo = (FIXED_INFO*) GlobalAlloc( GPTR, bufLen );
+			require_action( fixedInfo, exit, err = mStatus_NoMemoryErr );
+	   
+			err = GetNetworkParams( fixedInfo, &bufLen );
+
+			if ( err != ERROR_BUFFER_OVERFLOW )
+			{
+				break;
+			}
+		}
+
+		require_noerr( err, exit );
+
+		dnsServerList = &fixedInfo->DnsServerList;
+	}
+
+	for ( ipAddr = dnsServerList; ipAddr; ipAddr = ipAddr->Next )
+	{
+		mDNSAddr addr;
+		err = StringToAddress( &addr, ipAddr->IpAddress.String );
+		if ( !err ) mDNS_AddDNSServer(m, mDNSNULL, mDNSInterface_Any, &addr, UnicastDNSPort, mDNSfalse, 0);
+	}
+
+exit:
+
+	if ( pAdapterInfo )
+	{
+		free( pAdapterInfo );
+	}
+
+	if ( fixedInfo )
+	{
+		GlobalFree( fixedInfo );
+	}
+}
+
+
+//===========================================================================================================================
+//	SetDomainFromDHCP
+//===========================================================================================================================
+
+mDNSlocal void
+SetDomainFromDHCP( void )
+{
+	int					i			= 0;
+	IP_ADAPTER_INFO *	pAdapterInfo;
+	IP_ADAPTER_INFO *	pAdapter;
+	DWORD				bufLen;
+	DWORD				index;
+	HKEY				key = NULL;
+	LPSTR				domain = NULL;
+	DWORD				dwSize;
+	mStatus				err = mStatus_NoError;
+
+	pAdapterInfo	= NULL;
+	
+	for ( i = 0; i < 100; i++ )
+	{
+		err = GetAdaptersInfo( pAdapterInfo, &bufLen);
+
+		if ( err != ERROR_BUFFER_OVERFLOW )
+		{
+			break;
+		}
+
+		pAdapterInfo = (IP_ADAPTER_INFO*) realloc( pAdapterInfo, bufLen );
+		require_action( pAdapterInfo, exit, err = kNoMemoryErr );
+	}
+
+	require_noerr( err, exit );
+
+	index = GetPrimaryInterface();
+
+	for ( pAdapter = pAdapterInfo; pAdapter; pAdapter = pAdapter->Next )
+	{
+		if ( pAdapter->IpAddressList.IpAddress.String &&
+		     pAdapter->IpAddressList.IpAddress.String[0] &&
+		     pAdapter->GatewayList.IpAddress.String &&
+		     pAdapter->GatewayList.IpAddress.String[0] &&
+		     ( !index || ( pAdapter->Index == index ) ) )
+		{
+			// Found one that will work
+
+			char keyName[1024];
+
+			_snprintf( keyName, 1024, "%s%s", "SYSTEM\\CurrentControlSet\\Services\\Tcpip\\Parameters\\Interfaces\\", pAdapter->AdapterName );
+
+			err = RegCreateKeyA( HKEY_LOCAL_MACHINE, keyName, &key );
+			require_noerr( err, exit );
+
+			err = RegQueryString( key, "Domain", &domain, &dwSize, NULL );
+			check_noerr( err );
+
+			if ( !domain || !domain[0] )
+			{
+				if ( domain )
+				{
+					free( domain );
+					domain = NULL;
+				}
+
+				err = RegQueryString( key, "DhcpDomain", &domain, &dwSize, NULL );
+				check_noerr( err );
+			}
+
+			if ( domain && domain[0] ) mDNS_AddSearchDomain_CString(domain, mDNSNULL);
+
+			break;
+		}
+	}
+
+exit:
+
+	if ( pAdapterInfo )
+	{
+		free( pAdapterInfo );
+	}
+
+	if ( domain )
+	{
+		free( domain );
+	}
+
+	if ( key )
+	{
+		RegCloseKey( key );
+	}
+}
+
+
+//===========================================================================================================================
+//	mDNSPlatformGetPrimaryInterface
+//===========================================================================================================================
+
+mDNSexport mStatus
+mDNSPlatformGetPrimaryInterface( mDNS * const m, mDNSAddr * v4, mDNSAddr * v6, mDNSAddr * router )
+{
+	IP_ADAPTER_INFO *	pAdapterInfo;
+	IP_ADAPTER_INFO *	pAdapter;
+	DWORD				bufLen;
+	int					i;
+	BOOL				found;
+	DWORD				index;
+	mStatus				err = mStatus_NoError;
+
+	DEBUG_UNUSED( m );
+
+	*v6 = zeroAddr;
+
+	pAdapterInfo	= NULL;
+	bufLen			= 0;
+	found			= FALSE;
+
+	for ( i = 0; i < 100; i++ )
+	{
+		err = GetAdaptersInfo( pAdapterInfo, &bufLen);
+
+		if ( err != ERROR_BUFFER_OVERFLOW )
+		{
+			break;
+		}
+
+		pAdapterInfo = (IP_ADAPTER_INFO*) realloc( pAdapterInfo, bufLen );
+		require_action( pAdapterInfo, exit, err = kNoMemoryErr );
+	}
+
+	require_noerr( err, exit );
+
+	index = GetPrimaryInterface();
+
+	for ( pAdapter = pAdapterInfo; pAdapter; pAdapter = pAdapter->Next )
+	{
+		if ( pAdapter->IpAddressList.IpAddress.String &&
+		     pAdapter->IpAddressList.IpAddress.String[0] &&
+		     pAdapter->GatewayList.IpAddress.String &&
+		     pAdapter->GatewayList.IpAddress.String[0] &&
+		     ( StringToAddress( v4, pAdapter->IpAddressList.IpAddress.String ) == mStatus_NoError ) &&
+		     ( StringToAddress( router, pAdapter->GatewayList.IpAddress.String ) == mStatus_NoError ) &&
+		     ( !index || ( pAdapter->Index == index ) ) )
+		{
+			// Found one that will work
+
+			if ( pAdapter->AddressLength == sizeof( m->PrimaryMAC ) )
+			{
+				memcpy( &m->PrimaryMAC, pAdapter->Address, pAdapter->AddressLength );
+			}
+
+			found = TRUE;
+			break;
+		}
+	}
+
+exit:
+
+	if ( pAdapterInfo )
+	{
+		free( pAdapterInfo );
+	}
+
+	return err;
+}
+
+mDNSexport void mDNSPlatformSendWakeupPacket(mDNS *const m, mDNSInterfaceID InterfaceID, char *EthAddr, char *IPAddr, int iteration)
+	{
+	(void) m;
+	(void) InterfaceID;
+	(void) EthAddr;
+	(void) IPAddr;
+	(void) iteration;
+	}
+
+mDNSexport mDNSBool mDNSPlatformValidRecordForInterface(AuthRecord *rr, const NetworkInterfaceInfo *intf)
+	{
+	(void) rr;
+	(void) intf;
+
+	return 1;
+	}
+
+
+#if 0
+#pragma mark -
+#endif
+
+//===========================================================================================================================
+//	debugf_
+//===========================================================================================================================
+#if( MDNS_DEBUGMSGS )
+mDNSexport void	debugf_( const char *inFormat, ... )
+{
+	char		buffer[ 512 ];
+    va_list		args;
+    mDNSu32		length;
+	
+	va_start( args, inFormat );
+	length = mDNS_vsnprintf( buffer, sizeof( buffer ), inFormat, args );
+	va_end( args );
+	
+	dlog( kDebugLevelInfo, "%s\n", buffer );
+}
+#endif
+
+//===========================================================================================================================
+//	verbosedebugf_
+//===========================================================================================================================
+
+#if( MDNS_DEBUGMSGS > 1 )
+mDNSexport void	verbosedebugf_( const char *inFormat, ... )
+{
+	char		buffer[ 512 ];
+    va_list		args;
+    mDNSu32		length;
+	
+	va_start( args, inFormat );
+	length = mDNS_vsnprintf( buffer, sizeof( buffer ), inFormat, args );
+	va_end( args );
+	
+	dlog( kDebugLevelVerbose, "%s\n", buffer );
+}
+#endif
+
+
+#if 0
+#pragma mark -
+#pragma mark == Platform Internals  ==
+#endif
+
+
+//===========================================================================================================================
+//	SetupNiceName
+//===========================================================================================================================
+
+mStatus	SetupNiceName( mDNS * const inMDNS )
+{
+	HKEY		descKey = NULL;
+	char		utf8[ 256 ];
+	LPCTSTR		s;
+	LPWSTR		joinName;
+	NETSETUP_JOIN_STATUS joinStatus;
+	mStatus		err = 0;
+	DWORD		namelen;
+	BOOL		ok;
+	
+	check( inMDNS );
+	
+	// Set up the nice name.
+	utf8[0] = '\0';
+
+	// First try and open the registry key that contains the computer description value
+	s = TEXT("SYSTEM\\CurrentControlSet\\Services\\lanmanserver\\parameters");
+	err = RegOpenKeyEx( HKEY_LOCAL_MACHINE, s, 0, KEY_READ, &descKey);
+	check_translated_errno( err == 0, errno_compat(), kNameErr );
+
+	if ( !err )
+	{
+		TCHAR	desc[256];
+		DWORD	descSize = sizeof( desc );
+
+		// look for the computer description
+		err = RegQueryValueEx( descKey, TEXT("srvcomment"), 0, NULL, (LPBYTE) &desc, &descSize);
+		
+		if ( !err )
+		{
+			err = TCHARtoUTF8( desc, utf8, sizeof( utf8 ) );
+		}
+
+		if ( err )
+		{
+			utf8[ 0 ] = '\0';
+		}
+	}
+
+	// if we can't find it in the registry, then use the hostname of the machine
+	if ( err || ( utf8[ 0 ] == '\0' ) )
+	{
+		TCHAR hostname[256];
+		
+		namelen = sizeof( hostname ) / sizeof( TCHAR );
+
+		ok = GetComputerNameExW( ComputerNamePhysicalDnsHostname, hostname, &namelen );
+		err = translate_errno( ok, (mStatus) GetLastError(), kNameErr );
+		check_noerr( err );
+		
+		if( !err )
+		{
+			err = TCHARtoUTF8( hostname, utf8, sizeof( utf8 ) );
+		}
+
+		if ( err )
+		{
+			utf8[ 0 ] = '\0';
+		}
+	}
+
+	// if we can't get the hostname
+	if ( err || ( utf8[ 0 ] == '\0' ) )
+	{
+		// Invalidate name so fall back to a default name.
+		
+		strcpy( utf8, kMDNSDefaultName );
+	}
+
+	utf8[ sizeof( utf8 ) - 1 ]	= '\0';	
+	inMDNS->nicelabel.c[ 0 ]	= (mDNSu8) (strlen( utf8 ) < MAX_DOMAIN_LABEL ? strlen( utf8 ) : MAX_DOMAIN_LABEL);
+	memcpy( &inMDNS->nicelabel.c[ 1 ], utf8, inMDNS->nicelabel.c[ 0 ] );
+	
+	dlog( kDebugLevelInfo, DEBUG_NAME "nice name \"%.*s\"\n", inMDNS->nicelabel.c[ 0 ], &inMDNS->nicelabel.c[ 1 ] );
+	
+	if ( descKey )
+	{
+		RegCloseKey( descKey );
+	}
+
+	ZeroMemory( inMDNS->p->nbname, sizeof( inMDNS->p->nbname ) );
+	ZeroMemory( inMDNS->p->nbdomain, sizeof( inMDNS->p->nbdomain ) );
+
+	namelen = sizeof( inMDNS->p->nbname );
+	ok = GetComputerNameExA( ComputerNamePhysicalNetBIOS, inMDNS->p->nbname, &namelen );
+	check( ok );
+	if ( ok ) dlog( kDebugLevelInfo, DEBUG_NAME "netbios name \"%s\"\n", inMDNS->p->nbname );
+
+	err = NetGetJoinInformation( NULL, &joinName, &joinStatus );
+	check ( err == NERR_Success );
+	if ( err == NERR_Success )
+	{
+		if ( ( joinStatus == NetSetupWorkgroupName ) || ( joinStatus == NetSetupDomainName ) )
+		{
+			err = TCHARtoUTF8( joinName, inMDNS->p->nbdomain, sizeof( inMDNS->p->nbdomain ) );
+			check( !err );
+			if ( !err ) dlog( kDebugLevelInfo, DEBUG_NAME "netbios domain/workgroup \"%s\"\n", inMDNS->p->nbdomain );
+		}
+
+		NetApiBufferFree( joinName );
+		joinName = NULL;
+	}
+
+	err = 0;
+
+	return( err );
+}
+
+//===========================================================================================================================
+//	SetupHostName
+//===========================================================================================================================
+
+mDNSlocal mStatus	SetupHostName( mDNS * const inMDNS )
+{
+	mStatus		err = 0;
+	char		tempString[ 256 ];
+	DWORD		tempStringLen;
+	domainlabel tempLabel;
+	BOOL		ok;
+	
+	check( inMDNS );
+
+	// Set up the nice name.
+	tempString[ 0 ] = '\0';
+
+	// use the hostname of the machine
+	tempStringLen = sizeof( tempString );
+	ok = GetComputerNameExA( ComputerNamePhysicalDnsHostname, tempString, &tempStringLen );
+	err = translate_errno( ok, (mStatus) GetLastError(), kNameErr );
+	check_noerr( err );
+
+	// if we can't get the hostname
+	if( err || ( tempString[ 0 ] == '\0' ) )
+	{
+		// Invalidate name so fall back to a default name.
+		
+		strcpy( tempString, kMDNSDefaultName );
+	}
+
+	tempString[ sizeof( tempString ) - 1 ] = '\0';
+	tempLabel.c[ 0 ] = (mDNSu8) (strlen( tempString ) < MAX_DOMAIN_LABEL ? strlen( tempString ) : MAX_DOMAIN_LABEL );
+	memcpy( &tempLabel.c[ 1 ], tempString, tempLabel.c[ 0 ] );
+	
+	// Set up the host name.
+	
+	ConvertUTF8PstringToRFC1034HostLabel( tempLabel.c, &inMDNS->hostlabel );
+	if( inMDNS->hostlabel.c[ 0 ] == 0 )
+	{
+		// Nice name has no characters that are representable as an RFC1034 name (e.g. Japanese) so use the default.
+		
+		MakeDomainLabelFromLiteralString( &inMDNS->hostlabel, kMDNSDefaultName );
+	}
+
+	check( inMDNS->hostlabel.c[ 0 ] != 0 );
+	
+	mDNS_SetFQDN( inMDNS );
+	
+	dlog( kDebugLevelInfo, DEBUG_NAME "host name \"%.*s\"\n", inMDNS->hostlabel.c[ 0 ], &inMDNS->hostlabel.c[ 1 ] );
+	
+	return( err );
+}
+
+//===========================================================================================================================
+//	SetupName
+//===========================================================================================================================
+
+mDNSlocal mStatus	SetupName( mDNS * const inMDNS )
+{
+	mStatus		err = 0;
+	
+	check( inMDNS );
+	
+	err = SetupNiceName( inMDNS );
+	check_noerr( err );
+
+	err = SetupHostName( inMDNS );
+	check_noerr( err );
+
+	return err;
+}
+
+
+//===========================================================================================================================
+//	SetupInterfaceList
+//===========================================================================================================================
+
+mStatus	SetupInterfaceList( mDNS * const inMDNS )
+{
+	mStatus						err;
+	mDNSInterfaceData **		next;
+	mDNSInterfaceData *			ifd;
+	struct ifaddrs *			addrs;
+	struct ifaddrs *			p;
+	struct ifaddrs *			loopbackv4;
+	struct ifaddrs *			loopbackv6;
+	u_int						flagMask;
+	u_int						flagTest;
+	mDNSBool					foundv4;
+	mDNSBool					foundv6;
+	mDNSBool					foundUnicastSock4DestAddr;
+	mDNSBool					foundUnicastSock6DestAddr;
+	
+	dlog( kDebugLevelTrace, DEBUG_NAME "setting up interface list\n" );
+	check( inMDNS );
+	check( inMDNS->p );
+	
+	inMDNS->p->registeredLoopback4	= mDNSfalse;
+	inMDNS->p->nextDHCPLeaseExpires = 0x7FFFFFFF;
+	addrs							= NULL;
+	foundv4							= mDNSfalse;
+	foundv6							= mDNSfalse;
+	foundUnicastSock4DestAddr		= mDNSfalse;
+	foundUnicastSock6DestAddr		= mDNSfalse;
+	
+	// Tear down any existing interfaces that may be set up.
+	
+	TearDownInterfaceList( inMDNS );
+
+	// Set up the name of this machine.
+	
+	err = SetupName( inMDNS );
+	check_noerr( err );
+
+	// Set up IPv4 interface(s). We have to set up IPv4 first so any IPv6 interface with an IPv4-routable address
+	// can refer to the IPv4 interface when it registers to allow DNS AAAA records over the IPv4 interface.
+	
+	err = getifaddrs( &addrs );
+	require_noerr( err, exit );
+	
+	loopbackv4	= NULL;
+	loopbackv6	= NULL;
+	next		= &inMDNS->p->interfaceList;
+
+	flagMask = IFF_UP | IFF_MULTICAST;
+	flagTest = IFF_UP | IFF_MULTICAST;
+	
+#if( MDNS_WINDOWS_ENABLE_IPV4 )
+	for( p = addrs; p; p = p->ifa_next )
+	{
+		if( !p->ifa_addr || ( p->ifa_addr->sa_family != AF_INET ) || ( ( p->ifa_flags & flagMask ) != flagTest ) )
+		{
+			continue;
+		}
+		if( p->ifa_flags & IFF_LOOPBACK )
+		{
+			if( !loopbackv4 )
+			{
+				loopbackv4 = p;
+			}
+			continue;
+		}
+		dlog( kDebugLevelVerbose, DEBUG_NAME "Interface %40s (0x%08X) %##a\n", 
+			p->ifa_name ? p->ifa_name : "<null>", p->ifa_extra.index, p->ifa_addr );
+		
+		err = SetupInterface( inMDNS, p, &ifd );
+		require_noerr( err, exit );
+
+		// If this guy is point-to-point (ifd->interfaceInfo.McastTxRx == 0 ) we still want to
+		// register him, but we also want to note that we haven't found a v4 interface
+		// so that we register loopback so same host operations work
+ 		
+		if ( ifd->interfaceInfo.McastTxRx == mDNStrue )
+		{
+			foundv4 = mDNStrue;
+		}
+
+		if ( p->ifa_dhcpEnabled && ( p->ifa_dhcpLeaseExpires < inMDNS->p->nextDHCPLeaseExpires ) )
+		{
+			inMDNS->p->nextDHCPLeaseExpires = p->ifa_dhcpLeaseExpires;
+		}
+
+		// If we're on a platform that doesn't have WSARecvMsg(), there's no way
+		// of determing the destination address of a packet that is sent to us.
+		// For multicast packets, that's easy to determine.  But for the unicast
+		// sockets, we'll fake it by taking the address of the first interface
+		// that is successfully setup.
+
+		if ( !foundUnicastSock4DestAddr )
+		{
+			inMDNS->p->unicastSock4.addr = ifd->interfaceInfo.ip;
+			foundUnicastSock4DestAddr = TRUE;
+		}
+			
+		*next = ifd;
+		next  = &ifd->next;
+		++inMDNS->p->interfaceCount;
+	}
+#endif
+	
+	// Set up IPv6 interface(s) after IPv4 is set up (see IPv4 notes above for reasoning).
+	
+#if( MDNS_WINDOWS_ENABLE_IPV6 )
+	for( p = addrs; p; p = p->ifa_next )
+	{
+		if( !p->ifa_addr || ( p->ifa_addr->sa_family != AF_INET6 ) || ( ( p->ifa_flags & flagMask ) != flagTest ) )
+		{
+			continue;
+		}
+		if( p->ifa_flags & IFF_LOOPBACK )
+		{
+			if( !loopbackv6 )
+			{
+				loopbackv6 = p;
+			}
+			continue;
+		}
+		dlog( kDebugLevelVerbose, DEBUG_NAME "Interface %40s (0x%08X) %##a\n", 
+			p->ifa_name ? p->ifa_name : "<null>", p->ifa_extra.index, p->ifa_addr );
+		
+		err = SetupInterface( inMDNS, p, &ifd );
+		require_noerr( err, exit );
+				
+		// If this guy is point-to-point (ifd->interfaceInfo.McastTxRx == 0 ) we still want to
+		// register him, but we also want to note that we haven't found a v4 interface
+		// so that we register loopback so same host operations work
+ 		
+		if ( ifd->interfaceInfo.McastTxRx == mDNStrue )
+		{
+			foundv6 = mDNStrue;
+		}
+
+		// If we're on a platform that doesn't have WSARecvMsg(), there's no way
+		// of determing the destination address of a packet that is sent to us.
+		// For multicast packets, that's easy to determine.  But for the unicast
+		// sockets, we'll fake it by taking the address of the first interface
+		// that is successfully setup.
+
+		if ( !foundUnicastSock6DestAddr )
+		{
+			inMDNS->p->unicastSock6.addr = ifd->interfaceInfo.ip;
+			foundUnicastSock6DestAddr = TRUE;
+		}
+
+		*next = ifd;
+		next  = &ifd->next;
+		++inMDNS->p->interfaceCount;
+	}
+#endif
+
+	// If there are no real interfaces, but there is a loopback interface, use that so same-machine operations work.
+
+#if( !MDNS_WINDOWS_ENABLE_IPV4 && !MDNS_WINDOWS_ENABLE_IPV6 )
+	
+	flagMask |= IFF_LOOPBACK;
+	flagTest |= IFF_LOOPBACK;
+	
+	for( p = addrs; p; p = p->ifa_next )
+	{
+		if( !p->ifa_addr || ( ( p->ifa_flags & flagMask ) != flagTest ) )
+		{
+			continue;
+		}
+		if( ( p->ifa_addr->sa_family != AF_INET ) && ( p->ifa_addr->sa_family != AF_INET6 ) )
+		{
+			continue;
+		}
+		
+		v4loopback = p;
+		break;
+	}
+	
+#endif
+	
+	if ( !foundv4 && loopbackv4 )
+	{
+		dlog( kDebugLevelInfo, DEBUG_NAME "Interface %40s (0x%08X) %##a\n", 
+			loopbackv4->ifa_name ? loopbackv4->ifa_name : "<null>", loopbackv4->ifa_extra.index, loopbackv4->ifa_addr );
+		
+		err = SetupInterface( inMDNS, loopbackv4, &ifd );
+		require_noerr( err, exit );
+
+		inMDNS->p->registeredLoopback4 = mDNStrue;
+		
+#if( MDNS_WINDOWS_ENABLE_IPV4 )
+
+		// If we're on a platform that doesn't have WSARecvMsg(), there's no way
+		// of determing the destination address of a packet that is sent to us.
+		// For multicast packets, that's easy to determine.  But for the unicast
+		// sockets, we'll fake it by taking the address of the first interface
+		// that is successfully setup.
+
+		if ( !foundUnicastSock4DestAddr )
+		{
+			inMDNS->p->unicastSock4.addr = ifd->sock.addr;
+			foundUnicastSock4DestAddr = TRUE;
+		}
+#endif
+
+		*next = ifd;
+		next  = &ifd->next;
+		++inMDNS->p->interfaceCount;
+	}
+
+	if ( !foundv6 && loopbackv6 )
+	{
+		dlog( kDebugLevelInfo, DEBUG_NAME "Interface %40s (0x%08X) %##a\n", 
+			loopbackv6->ifa_name ? loopbackv6->ifa_name : "<null>", loopbackv6->ifa_extra.index, loopbackv6->ifa_addr );
+		
+		err = SetupInterface( inMDNS, loopbackv6, &ifd );
+		require_noerr( err, exit );
+		
+#if( MDNS_WINDOWS_ENABLE_IPV6 )
+
+		// If we're on a platform that doesn't have WSARecvMsg(), there's no way
+		// of determing the destination address of a packet that is sent to us.
+		// For multicast packets, that's easy to determine.  But for the unicast
+		// sockets, we'll fake it by taking the address of the first interface
+		// that is successfully setup.
+
+		if ( !foundUnicastSock6DestAddr )
+		{
+			inMDNS->p->unicastSock6.addr = ifd->sock.addr;
+			foundUnicastSock6DestAddr = TRUE;
+		}
+#endif
+
+		*next = ifd;
+		next  = &ifd->next;
+		++inMDNS->p->interfaceCount;
+	}
+
+	CheckFileShares( inMDNS );
+
+exit:
+	if( err )
+	{
+		TearDownInterfaceList( inMDNS );
+	}
+	if( addrs )
+	{
+		freeifaddrs( addrs );
+	}
+	dlog( kDebugLevelTrace, DEBUG_NAME "setting up interface list done (err=%d %m)\n", err, err );
+	return( err );
+}
+
+//===========================================================================================================================
+//	TearDownInterfaceList
+//===========================================================================================================================
+
+mStatus	TearDownInterfaceList( mDNS * const inMDNS )
+{
+	mDNSInterfaceData **		p;
+	mDNSInterfaceData *		ifd;
+	
+	dlog( kDebugLevelTrace, DEBUG_NAME "tearing down interface list\n" );
+	check( inMDNS );
+	check( inMDNS->p );
+
+	// Free any interfaces that were previously marked inactive and are no longer referenced by the mDNS cache.
+	// Interfaces are marked inactive, but not deleted immediately if they were still referenced by the mDNS cache
+	// so that remove events that occur after an interface goes away can still report the correct interface.
+
+	p = &inMDNS->p->inactiveInterfaceList;
+	while( *p )
+	{
+		ifd = *p;
+		if( NumCacheRecordsForInterfaceID( inMDNS, (mDNSInterfaceID) ifd ) > 0 )
+		{
+			p = &ifd->next;
+			continue;
+		}
+		
+		dlog( kDebugLevelInfo, DEBUG_NAME "freeing unreferenced, inactive interface %#p %#a\n", ifd, &ifd->interfaceInfo.ip );
+		*p = ifd->next;
+
+		QueueUserAPC( ( PAPCFUNC ) FreeInterface, inMDNS->p->mainThread, ( ULONG_PTR ) ifd );
+	}
+
+	// Tear down all the interfaces.
+	
+	while( inMDNS->p->interfaceList )
+	{
+		ifd = inMDNS->p->interfaceList;
+		inMDNS->p->interfaceList = ifd->next;
+		
+		TearDownInterface( inMDNS, ifd );
+	}
+	inMDNS->p->interfaceCount = 0;
+	
+	dlog( kDebugLevelTrace, DEBUG_NAME "tearing down interface list done\n" );
+	return( mStatus_NoError );
+}
+
+//===========================================================================================================================
+//	SetupInterface
+//===========================================================================================================================
+
+mDNSlocal mStatus	SetupInterface( mDNS * const inMDNS, const struct ifaddrs *inIFA, mDNSInterfaceData **outIFD )
+{
+	mDNSInterfaceData	*	ifd;
+	mDNSInterfaceData	*	p;
+	mStatus					err;
+	
+	ifd = NULL;
+	dlog( kDebugLevelTrace, DEBUG_NAME "setting up interface\n" );
+	check( inMDNS );
+	check( inMDNS->p );
+	check( inIFA );
+	check( inIFA->ifa_addr );
+	check( outIFD );
+	
+	// Allocate memory for the interface and initialize it.
+	
+	ifd = (mDNSInterfaceData *) calloc( 1, sizeof( *ifd ) );
+	require_action( ifd, exit, err = mStatus_NoMemoryErr );
+	ifd->sock.fd		= kInvalidSocketRef;
+	ifd->sock.overlapped.pending = FALSE;
+	ifd->sock.ifd		= ifd;
+	ifd->sock.next		= NULL;
+	ifd->sock.m			= inMDNS;
+	ifd->index			= inIFA->ifa_extra.index;
+	ifd->scopeID		= inIFA->ifa_extra.index;
+	check( strlen( inIFA->ifa_name ) < sizeof( ifd->name ) );
+	strncpy( ifd->name, inIFA->ifa_name, sizeof( ifd->name ) - 1 );
+	ifd->name[ sizeof( ifd->name ) - 1 ] = '\0';
+	
+	strncpy(ifd->interfaceInfo.ifname, inIFA->ifa_name, sizeof(ifd->interfaceInfo.ifname));
+	ifd->interfaceInfo.ifname[sizeof(ifd->interfaceInfo.ifname)-1] = 0;
+	
+	// We always send and receive using IPv4, but to reduce traffic, we send and receive using IPv6 only on interfaces 
+	// that have no routable IPv4 address. Having a routable IPv4 address assigned is a reasonable indicator of being 
+	// on a large configured network, which means there's a good chance that most or all the other devices on that 
+	// network should also have v4. By doing this we lose the ability to talk to true v6-only devices on that link, 
+	// but we cut the packet rate in half. At this time, reducing the packet rate is more important than v6-only 
+	// devices on a large configured network, so we are willing to make that sacrifice.
+	
+	ifd->interfaceInfo.McastTxRx   = ( ( inIFA->ifa_flags & IFF_MULTICAST ) && !( inIFA->ifa_flags & IFF_POINTTOPOINT ) ) ? mDNStrue : mDNSfalse;
+	ifd->interfaceInfo.InterfaceID = NULL;
+
+	for( p = inMDNS->p->interfaceList; p; p = p->next )
+	{
+		if ( strcmp( p->name, ifd->name ) == 0 )
+		{
+			if (!ifd->interfaceInfo.InterfaceID)
+			{
+				ifd->interfaceInfo.InterfaceID	= (mDNSInterfaceID) p;
+			}
+
+			if ( ( inIFA->ifa_addr->sa_family != AF_INET ) &&
+			     ( p->interfaceInfo.ip.type == mDNSAddrType_IPv4 ) &&
+			     ( p->interfaceInfo.ip.ip.v4.b[ 0 ] != 169 || p->interfaceInfo.ip.ip.v4.b[ 1 ] != 254 ) )
+			{
+				ifd->interfaceInfo.McastTxRx = mDNSfalse;
+			}
+
+			break;
+		}
+	}
+
+	if ( !ifd->interfaceInfo.InterfaceID )
+	{
+		ifd->interfaceInfo.InterfaceID = (mDNSInterfaceID) ifd;
+	}
+
+	// Set up a socket for this interface (if needed).
+	
+	if( ifd->interfaceInfo.McastTxRx )
+	{
+		DWORD size;
+			
+		err = SetupSocket( inMDNS, inIFA->ifa_addr, MulticastDNSPort, &ifd->sock.fd );
+		require_noerr( err, exit );
+		ifd->sock.addr = ( inIFA->ifa_addr->sa_family == AF_INET6 ) ? AllDNSLinkGroup_v6 : AllDNSLinkGroup_v4;
+		ifd->sock.port = MulticastDNSPort;
+		
+		// Get a ptr to the WSARecvMsg function, if supported. Otherwise, we'll fallback to recvfrom.
+
+		err = WSAIoctl( ifd->sock.fd, SIO_GET_EXTENSION_FUNCTION_POINTER, &kWSARecvMsgGUID, sizeof( kWSARecvMsgGUID ), &ifd->sock.recvMsgPtr, sizeof( ifd->sock.recvMsgPtr ), &size, NULL, NULL );
+
+		if ( err )
+		{
+			ifd->sock.recvMsgPtr = NULL;
+		}
+	}
+
+	if ( inIFA->ifa_dhcpEnabled && ( inIFA->ifa_dhcpLeaseExpires < inMDNS->p->nextDHCPLeaseExpires ) )
+	{
+		inMDNS->p->nextDHCPLeaseExpires = inIFA->ifa_dhcpLeaseExpires;
+	}
+
+	ifd->interfaceInfo.NetWake = inIFA->ifa_womp;
+
+	// Register this interface with mDNS.
+	
+	err = SockAddrToMDNSAddr( inIFA->ifa_addr, &ifd->interfaceInfo.ip, NULL );
+	require_noerr( err, exit );
+	
+	err = SockAddrToMDNSAddr( inIFA->ifa_netmask, &ifd->interfaceInfo.mask, NULL );
+	require_noerr( err, exit );
+
+	memcpy( ifd->interfaceInfo.MAC.b, inIFA->ifa_physaddr, sizeof( ifd->interfaceInfo.MAC.b ) );
+	
+	ifd->interfaceInfo.Advertise = ( mDNSu8 ) inMDNS->AdvertiseLocalAddresses;
+
+	if ( ifd->sock.fd != kInvalidSocketRef )
+	{
+		err = UDPBeginRecv( &ifd->sock );
+		require_noerr( err, exit );
+	}
+
+	err = mDNS_RegisterInterface( inMDNS, &ifd->interfaceInfo, mDNSfalse );
+	require_noerr( err, exit );
+	ifd->hostRegistered = mDNStrue;
+	
+	dlog( kDebugLevelInfo, DEBUG_NAME "Registered interface %##a with mDNS\n", inIFA->ifa_addr );
+	
+	// Success!
+	
+	*outIFD = ifd;
+	ifd = NULL;
+	
+exit:
+
+	if( ifd )
+	{
+		TearDownInterface( inMDNS, ifd );
+	}
+	dlog( kDebugLevelTrace, DEBUG_NAME "setting up interface done (err=%d %m)\n", err, err );
+	return( err );
+}
+
+//===========================================================================================================================
+//	TearDownInterface
+//===========================================================================================================================
+
+mDNSlocal mStatus	TearDownInterface( mDNS * const inMDNS, mDNSInterfaceData *inIFD )
+{	
+	check( inMDNS );
+	check( inIFD );
+	
+	// Deregister this interface with mDNS.
+	
+	dlog( kDebugLevelInfo, DEBUG_NAME "Deregistering interface %#a with mDNS\n", &inIFD->interfaceInfo.ip );
+	
+	if( inIFD->hostRegistered )
+	{
+		inIFD->hostRegistered = mDNSfalse;
+		mDNS_DeregisterInterface( inMDNS, &inIFD->interfaceInfo, mDNSfalse );
+	}
+	
+	// Tear down the multicast socket.
+	
+	UDPCloseSocket( &inIFD->sock );
+
+	// If the interface is still referenced by items in the mDNS cache then put it on the inactive list. This keeps 
+	// the InterfaceID valid so remove events report the correct interface. If it is no longer referenced, free it.
+
+	if( NumCacheRecordsForInterfaceID( inMDNS, (mDNSInterfaceID) inIFD ) > 0 )
+	{
+		inIFD->next = inMDNS->p->inactiveInterfaceList;
+		inMDNS->p->inactiveInterfaceList = inIFD;
+		dlog( kDebugLevelInfo, DEBUG_NAME "deferring free of interface %#p %#a\n", inIFD, &inIFD->interfaceInfo.ip );
+	}
+	else
+	{
+		dlog( kDebugLevelInfo, DEBUG_NAME "freeing interface %#p %#a immediately\n", inIFD, &inIFD->interfaceInfo.ip );
+		QueueUserAPC( ( PAPCFUNC ) FreeInterface, inMDNS->p->mainThread, ( ULONG_PTR ) inIFD );
+	}
+
+	return( mStatus_NoError );
+}
+
+mDNSlocal void CALLBACK FreeInterface( mDNSInterfaceData *inIFD )
+{
+	free( inIFD );
+}
+
+//===========================================================================================================================
+//	SetupSocket
+//===========================================================================================================================
+
+mDNSlocal mStatus	SetupSocket( mDNS * const inMDNS, const struct sockaddr *inAddr, mDNSIPPort port, SocketRef *outSocketRef  )
+{
+	mStatus			err;
+	SocketRef		sock;
+	int				option;
+	DWORD			bytesReturned = 0;
+	BOOL			behavior = FALSE;
+	
+	DEBUG_UNUSED( inMDNS );
+	
+	dlog( kDebugLevelTrace, DEBUG_NAME "setting up socket %##a\n", inAddr );
+	check( inMDNS );
+	check( outSocketRef );
+	
+	// Set up an IPv4 or IPv6 UDP socket.
+	
+	sock = socket( inAddr->sa_family, SOCK_DGRAM, IPPROTO_UDP );
+	err = translate_errno( IsValidSocket( sock ), errno_compat(), kUnknownErr );
+	require_noerr( err, exit );
+		
+	// Turn on reuse address option so multiple servers can listen for Multicast DNS packets,
+	// if we're creating a multicast socket
+	
+	if ( port.NotAnInteger )
+	{
+		option = 1;
+		err = setsockopt( sock, SOL_SOCKET, SO_REUSEADDR, (char *) &option, sizeof( option ) );
+		check_translated_errno( err == 0, errno_compat(), kOptionErr );
+	}
+
+	// <rdar://problem/7894393> Bonjour for Windows broken on Windows XP
+	//
+	// Not sure why, but the default behavior for sockets is to behave incorrectly
+	// when using them in Overlapped I/O mode on XP. According to MSDN:
+	//
+	// SIO_UDP_CONNRESET (opcode setting: I, T==3)
+	//     Windows XP:  Controls whether UDP PORT_UNREACHABLE messages are reported. Set to TRUE to enable reporting.
+	//     Set to FALSE to disable reporting.
+	//
+	// Packet traces from misbehaving Bonjour installations showed that ICMP port unreachable
+	// messages were being sent to us after we sent out packets to a multicast address. This is clearly
+	// incorrect behavior, but should be harmless. However, after receiving a port unreachable error, WinSock
+	// will no longer receive any packets from that socket, which is not harmless. This behavior is only
+	// seen on XP.
+	//
+	// So we turn off port unreachable reporting to make sure our sockets that are reading
+	// multicast packets function correctly under all circumstances.
+
+	err = WSAIoctl( sock, SIO_UDP_CONNRESET, &behavior, sizeof(behavior), NULL, 0, &bytesReturned, NULL, NULL );
+	check_translated_errno( err == 0, errno_compat(), kOptionErr );
+
+	if( inAddr->sa_family == AF_INET )
+	{
+		mDNSv4Addr				ipv4;
+		struct sockaddr_in		sa4;
+		struct ip_mreq			mreqv4;
+		
+		// Bind the socket to the desired port
+		
+		ipv4.NotAnInteger 	= ( (const struct sockaddr_in *) inAddr )->sin_addr.s_addr;
+		mDNSPlatformMemZero( &sa4, sizeof( sa4 ) );
+		sa4.sin_family 		= AF_INET;
+		sa4.sin_port 		= port.NotAnInteger;
+		sa4.sin_addr.s_addr	= ipv4.NotAnInteger;
+		
+		err = bind( sock, (struct sockaddr *) &sa4, sizeof( sa4 ) );
+		check_translated_errno( err == 0, errno_compat(), kUnknownErr );
+		
+		// Turn on option to receive destination addresses and receiving interface.
+		
+		option = 1;
+		err = setsockopt( sock, IPPROTO_IP, IP_PKTINFO, (char *) &option, sizeof( option ) );
+		check_translated_errno( err == 0, errno_compat(), kOptionErr );
+		
+		if (port.NotAnInteger)
+		{
+			// Join the all-DNS multicast group so we receive Multicast DNS packets
+
+			mreqv4.imr_multiaddr.s_addr = AllDNSLinkGroup_v4.ip.v4.NotAnInteger;
+			mreqv4.imr_interface.s_addr = ipv4.NotAnInteger;
+			err = setsockopt( sock, IPPROTO_IP, IP_ADD_MEMBERSHIP, (char *) &mreqv4, sizeof( mreqv4 ) );
+			check_translated_errno( err == 0, errno_compat(), kOptionErr );
+		
+			// Specify the interface to send multicast packets on this socket.
+		
+			sa4.sin_addr.s_addr = ipv4.NotAnInteger;
+			err = setsockopt( sock, IPPROTO_IP, IP_MULTICAST_IF, (char *) &sa4.sin_addr, sizeof( sa4.sin_addr ) );
+			check_translated_errno( err == 0, errno_compat(), kOptionErr );
+		
+			// Enable multicast loopback so we receive multicast packets we send (for same-machine operations).
+		
+			option = 1;
+			err = setsockopt( sock, IPPROTO_IP, IP_MULTICAST_LOOP, (char *) &option, sizeof( option ) );
+			check_translated_errno( err == 0, errno_compat(), kOptionErr );
+		}
+
+		// Send unicast packets with TTL 255 (helps against spoofing).
+		
+		option = 255;
+		err = setsockopt( sock, IPPROTO_IP, IP_TTL, (char *) &option, sizeof( option ) );
+		check_translated_errno( err == 0, errno_compat(), kOptionErr );
+
+		// Send multicast packets with TTL 255 (helps against spoofing).
+		
+		option = 255;
+		err = setsockopt( sock, IPPROTO_IP, IP_MULTICAST_TTL, (char *) &option, sizeof( option ) );
+		check_translated_errno( err == 0, errno_compat(), kOptionErr );
+
+	}
+	else if( inAddr->sa_family == AF_INET6 )
+	{
+		struct sockaddr_in6 *		sa6p;
+		struct sockaddr_in6			sa6;
+		struct ipv6_mreq			mreqv6;
+		
+		sa6p = (struct sockaddr_in6 *) inAddr;
+		
+		// Bind the socket to the desired port
+		
+		mDNSPlatformMemZero( &sa6, sizeof( sa6 ) );
+		sa6.sin6_family		= AF_INET6;
+		sa6.sin6_port		= port.NotAnInteger;
+		sa6.sin6_flowinfo	= 0;
+		sa6.sin6_addr		= sa6p->sin6_addr;
+		sa6.sin6_scope_id	= sa6p->sin6_scope_id;
+		
+		err = bind( sock, (struct sockaddr *) &sa6, sizeof( sa6 ) );
+		check_translated_errno( err == 0, errno_compat(), kUnknownErr );
+		
+		// Turn on option to receive destination addresses and receiving interface.
+		
+		option = 1;
+		err = setsockopt( sock, IPPROTO_IPV6, IPV6_PKTINFO, (char *) &option, sizeof( option ) );
+		check_translated_errno( err == 0, errno_compat(), kOptionErr );
+		
+		// We only want to receive IPv6 packets (not IPv4-mapped IPv6 addresses) because we have a separate socket 
+		// for IPv4, but the IPv6 stack in Windows currently doesn't support IPv4-mapped IPv6 addresses and doesn't
+		// support the IPV6_V6ONLY socket option so the following code would typically not be executed (or needed).
+		
+		#if( defined( IPV6_V6ONLY ) )
+			option = 1;
+			err = setsockopt( sock, IPPROTO_IPV6, IPV6_V6ONLY, (char *) &option, sizeof( option ) );
+			check_translated_errno( err == 0, errno_compat(), kOptionErr );		
+		#endif
+		
+		if ( port.NotAnInteger )
+		{
+			// Join the all-DNS multicast group so we receive Multicast DNS packets.
+		
+			mreqv6.ipv6mr_multiaddr = *( (struct in6_addr *) &AllDNSLinkGroup_v6.ip.v6 );
+			mreqv6.ipv6mr_interface = sa6p->sin6_scope_id;
+			err = setsockopt( sock, IPPROTO_IPV6, IPV6_JOIN_GROUP, (char *) &mreqv6, sizeof( mreqv6 ) );
+			check_translated_errno( err == 0, errno_compat(), kOptionErr );
+		
+			// Specify the interface to send multicast packets on this socket.
+		
+			option = (int) sa6p->sin6_scope_id;
+			err = setsockopt( sock, IPPROTO_IPV6, IPV6_MULTICAST_IF, (char *) &option, sizeof( option ) );
+			check_translated_errno( err == 0, errno_compat(), kOptionErr );
+		
+			// Enable multicast loopback so we receive multicast packets we send (for same-machine operations).
+			
+			option = 1;
+			err = setsockopt( sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, (char *) &option, sizeof( option ) );
+			check_translated_errno( err == 0, errno_compat(), kOptionErr );
+		}
+
+		// Send unicast packets with TTL 255 (helps against spoofing).
+		
+		option = 255;
+		err = setsockopt( sock, IPPROTO_IPV6, IPV6_UNICAST_HOPS, (char *) &option, sizeof( option ) );
+		check_translated_errno( err == 0, errno_compat(), kOptionErr );
+
+		// Send multicast packets with TTL 255 (helps against spoofing).
+			
+		option = 255;
+		err = setsockopt( sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, (char *) &option, sizeof( option ) );
+		check_translated_errno( err == 0, errno_compat(), kOptionErr );
+	}
+	else
+	{
+		dlog( kDebugLevelError, DEBUG_NAME "%s: unsupport socket family (%d)\n", __ROUTINE__, inAddr->sa_family );
+		err = kUnsupportedErr;
+		goto exit;
+	}
+	
+	// Success!
+	
+	*outSocketRef = sock;
+	sock = kInvalidSocketRef;
+	err = mStatus_NoError;
+	
+exit:
+	if( IsValidSocket( sock ) )
+	{
+		close_compat( sock );
+	}
+	return( err );
+}
+
+//===========================================================================================================================
+//	SetupSocket
+//===========================================================================================================================
+
+mDNSlocal mStatus	SockAddrToMDNSAddr( const struct sockaddr * const inSA, mDNSAddr *outIP, mDNSIPPort *outPort )
+{
+	mStatus		err;
+	
+	check( inSA );
+	check( outIP );
+	
+	if( inSA->sa_family == AF_INET )
+	{
+		struct sockaddr_in *		sa4;
+		
+		sa4 						= (struct sockaddr_in *) inSA;
+		outIP->type 				= mDNSAddrType_IPv4;
+		outIP->ip.v4.NotAnInteger	= sa4->sin_addr.s_addr;
+		if( outPort )
+		{
+			outPort->NotAnInteger	= sa4->sin_port;
+		}
+		err = mStatus_NoError;
+	}
+	else if( inSA->sa_family == AF_INET6 )
+	{
+		struct sockaddr_in6 *		sa6;
+		
+		sa6 			= (struct sockaddr_in6 *) inSA;
+		outIP->type 	= mDNSAddrType_IPv6;
+		outIP->ip.v6 	= *( (mDNSv6Addr *) &sa6->sin6_addr );
+		if( IN6_IS_ADDR_LINKLOCAL( &sa6->sin6_addr ) )
+		{
+			outIP->ip.v6.w[ 1 ] = 0;
+		}
+		if( outPort )
+		{
+			outPort->NotAnInteger = sa6->sin6_port;
+		}
+		err = mStatus_NoError;
+	}
+	else
+	{
+		dlog( kDebugLevelError, DEBUG_NAME "%s: invalid sa_family %d", __ROUTINE__, inSA->sa_family );
+		err = mStatus_BadParamErr;
+	}
+	return( err );
+}
+
+
+#if 0
+#pragma mark -
+#endif
+
+//===========================================================================================================================
+//	UDPBeginRecv
+//===========================================================================================================================
+
+mDNSlocal OSStatus UDPBeginRecv( UDPSocket * sock )
+{
+	DWORD	size;
+	DWORD	numTries;
+	mStatus	err;
+
+	dlog( kDebugLevelChatty, DEBUG_NAME "%s: sock = %d\n", __ROUTINE__, sock->fd );
+	
+	require_action( sock != NULL, exit, err = mStatus_BadStateErr );
+	check( !sock->overlapped.pending );
+	
+	// Initialize the buffer structure
+
+	sock->overlapped.wbuf.buf	= (char *) &sock->packet;
+	sock->overlapped.wbuf.len	= (u_long) sizeof( sock->packet );
+	sock->srcAddrLen			= sizeof( sock->srcAddr );
+
+	// Initialize the overlapped structure
+
+	ZeroMemory( &sock->overlapped.data, sizeof( OVERLAPPED ) );
+	sock->overlapped.data.hEvent = sock;
+
+	numTries = 0;
+
+	do
+	{
+		if ( sock->recvMsgPtr )
+		{
+			sock->wmsg.name				= ( LPSOCKADDR ) &sock->srcAddr;
+			sock->wmsg.namelen			= sock->srcAddrLen;
+			sock->wmsg.lpBuffers		= &sock->overlapped.wbuf;
+			sock->wmsg.dwBufferCount	= 1;
+			sock->wmsg.Control.buf		= ( CHAR* ) sock->controlBuffer;
+			sock->wmsg.Control.len		= sizeof( sock->controlBuffer );
+			sock->wmsg.dwFlags			= 0;
+
+			err = sock->recvMsgPtr( sock->fd, &sock->wmsg, &size, &sock->overlapped.data, ( LPWSAOVERLAPPED_COMPLETION_ROUTINE ) UDPEndRecv );
+			err = translate_errno( ( err == 0 ) || ( WSAGetLastError() == WSA_IO_PENDING ), (OSStatus) WSAGetLastError(), kUnknownErr ); 
+
+			// <rdar://problem/7824093> iTunes 9.1 fails to install with Bonjour service on Windows 7 Ultimate
+			//
+			// There seems to be a bug in some network device drivers that involves calling WSARecvMsg() in
+			// overlapped i/o mode. Although all the parameters to WSARecvMsg() are correct, it returns a
+			// WSAEFAULT error code when there is no actual error. We have found experientially that falling
+			// back to using WSARecvFrom() when this happens will work correctly.
+
+			if ( err == WSAEFAULT ) sock->recvMsgPtr = NULL;
+		}
+		else
+		{
+			DWORD flags = 0;
+
+			err = WSARecvFrom( sock->fd, &sock->overlapped.wbuf, 1, NULL, &flags, ( LPSOCKADDR ) &sock->srcAddr, &sock->srcAddrLen, &sock->overlapped.data, ( LPWSAOVERLAPPED_COMPLETION_ROUTINE ) UDPEndRecv );
+			err = translate_errno( ( err == 0 ) || ( WSAGetLastError() == WSA_IO_PENDING ), ( OSStatus ) WSAGetLastError(), kUnknownErr );
+		}
+
+		// According to MSDN <http://msdn.microsoft.com/en-us/library/ms741687(VS.85).aspx>:
+		//
+		// "WSAECONNRESET: For a UDP datagram socket, this error would indicate that a previous
+		//                 send operation resulted in an ICMP "Port Unreachable" message."
+		//
+		// Because this is the case, we want to ignore this error and try again.  Just in case
+		// this is some kind of pathological condition, we'll break out of the retry loop 
+		// after 100 iterations
+
+		require_action( !err || ( err == WSAECONNRESET ) || ( err == WSAEFAULT ), exit, err = WSAGetLastError() );
+	}
+	while ( ( ( err == WSAECONNRESET ) || ( err == WSAEFAULT ) ) && ( numTries++ < 100 ) );
+
+	sock->overlapped.pending = TRUE;
+
+exit:
+
+	if ( err )
+	{
+		LogMsg( "WSARecvMsg failed (%d)\n", err );
+	}
+
+	return err;
+}
+
+
+//===========================================================================================================================
+//	UDPEndRecv
+//===========================================================================================================================
+
+mDNSlocal void CALLBACK UDPEndRecv( DWORD err, DWORD bytesTransferred, LPWSAOVERLAPPED overlapped, DWORD flags )
+{
+	UDPSocket * sock = NULL;
+
+	( void ) flags;
+	
+	dlog( kDebugLevelChatty, DEBUG_NAME "%s: err = %d, bytesTransferred = %d\n", __ROUTINE__, err, bytesTransferred );
+	require_action_quiet( err != WSA_OPERATION_ABORTED, exit, err = ( DWORD ) kUnknownErr );
+	require_noerr( err, exit );
+	sock = ( overlapped != NULL ) ? overlapped->hEvent : NULL;
+	require_action( sock != NULL, exit, err = ( DWORD ) kUnknownErr );
+	dlog( kDebugLevelChatty, DEBUG_NAME "%s: sock = %d\n", __ROUTINE__, sock->fd );
+	sock->overlapped.error				= err;
+	sock->overlapped.bytesTransferred	= bytesTransferred;
+	check( sock->overlapped.pending );
+	sock->overlapped.pending			= FALSE;
+	
+	// Translate the source of this packet into mDNS data types
+
+	SockAddrToMDNSAddr( (struct sockaddr *) &sock->srcAddr, &sock->overlapped.srcAddr, &sock->overlapped.srcPort );
+	
+	// Initialize the destination of this packet. Just in case
+	// we can't determine this info because we couldn't call
+	// WSARecvMsg (recvMsgPtr)
+
+	sock->overlapped.dstAddr = sock->addr;
+	sock->overlapped.dstPort = sock->port;
+
+	if ( sock->recvMsgPtr )
+	{
+		LPWSACMSGHDR	header;
+		LPWSACMSGHDR	last = NULL;
+		int				count = 0;
+		
+		// Parse the control information. Reject packets received on the wrong interface.
+		
+		// <rdar://problem/7832196> INSTALL: Bonjour 2.0 on Windows can not start / stop
+		// 
+		// There seems to be an interaction between Bullguard and this next bit of code.
+		// When a user's machine is running Bullguard, the control information that is
+		// returned is corrupted, and the code would go into an infinite loop. We'll add
+		// two bits of defensive coding here. The first will check that each pointer to
+		// the LPWSACMSGHDR that is returned in the for loop is different than the last.
+		// This fixes the problem with Bullguard. The second will break out of this loop
+		// after 100 iterations, just in case the corruption isn't caught by the first
+		// check.
+
+		for ( header = WSA_CMSG_FIRSTHDR( &sock->wmsg ); header; header = WSA_CMSG_NXTHDR( &sock->wmsg, header ) )
+		{
+			if ( ( header != last ) && ( ++count < 100 ) )
+			{
+				last = header;
+					
+				if ( ( header->cmsg_level == IPPROTO_IP ) && ( header->cmsg_type == IP_PKTINFO ) )
+				{
+					IN_PKTINFO * ipv4PacketInfo;
+					
+					ipv4PacketInfo = (IN_PKTINFO *) WSA_CMSG_DATA( header );
+
+					if ( sock->ifd != NULL )
+					{
+						require_action( ipv4PacketInfo->ipi_ifindex == sock->ifd->index, exit, err = ( DWORD ) kMismatchErr );
+					}
+
+					sock->overlapped.dstAddr.type 				= mDNSAddrType_IPv4;
+					sock->overlapped.dstAddr.ip.v4.NotAnInteger	= ipv4PacketInfo->ipi_addr.s_addr;
+				}
+				else if( ( header->cmsg_level == IPPROTO_IPV6 ) && ( header->cmsg_type == IPV6_PKTINFO ) )
+				{
+					IN6_PKTINFO * ipv6PacketInfo;
+						
+					ipv6PacketInfo = (IN6_PKTINFO *) WSA_CMSG_DATA( header );
+		
+					if ( sock->ifd != NULL )
+					{
+						require_action( ipv6PacketInfo->ipi6_ifindex == ( sock->ifd->index - kIPv6IfIndexBase ), exit, err = ( DWORD ) kMismatchErr );
+					}
+
+					sock->overlapped.dstAddr.type	= mDNSAddrType_IPv6;
+					sock->overlapped.dstAddr.ip.v6	= *( (mDNSv6Addr *) &ipv6PacketInfo->ipi6_addr );
+				}
+			}
+			else
+			{
+				static BOOL loggedMessage = FALSE;
+
+				if ( !loggedMessage )
+				{
+					LogMsg( "UDPEndRecv: WSARecvMsg control information error." );
+					loggedMessage = TRUE;
+				}
+
+				break;
+			}
+		}
+	}
+
+	dlog( kDebugLevelChatty, DEBUG_NAME "packet received\n" );
+	dlog( kDebugLevelChatty, DEBUG_NAME "    size      = %d\n", bytesTransferred );
+	dlog( kDebugLevelChatty, DEBUG_NAME "    src       = %#a:%u\n", &sock->overlapped.srcAddr, ntohs( sock->overlapped.srcPort.NotAnInteger ) );
+	dlog( kDebugLevelChatty, DEBUG_NAME "    dst       = %#a:%u\n", &sock->overlapped.dstAddr, ntohs( sock->overlapped.dstPort.NotAnInteger ) );
+	
+	if ( sock->ifd != NULL )
+	{
+		dlog( kDebugLevelChatty, DEBUG_NAME "    interface = %#a (index=0x%08X)\n", &sock->ifd->interfaceInfo.ip, sock->ifd->index );
+	}
+
+	dlog( kDebugLevelChatty, DEBUG_NAME "\n" );
+
+	// Queue this socket
+	
+	AddToTail( &gUDPDispatchableSockets, sock );
+
+exit:
+
+	return;
+}
+
+
+//===========================================================================================================================
+//	InterfaceListDidChange
+//===========================================================================================================================
+void InterfaceListDidChange( mDNS * const inMDNS )
+{
+	mStatus err;
+	
+	dlog( kDebugLevelInfo, DEBUG_NAME "interface list changed\n" );
+	check( inMDNS );
+	
+	// Tear down the existing interfaces and set up new ones using the new IP info.
+	
+	err = TearDownInterfaceList( inMDNS );
+	check_noerr( err );
+	
+	err = SetupInterfaceList( inMDNS );
+	check_noerr( err );
+		
+	err = uDNS_SetupDNSConfig( inMDNS );
+	check_noerr( err );
+	
+	// Inform clients of the change.
+	
+	mDNS_ConfigChanged(inMDNS);
+	
+	// Force mDNS to update.
+	
+	mDNSCoreMachineSleep( inMDNS, mDNSfalse ); // What is this for? Mac OS X does not do this
+}
+
+
+//===========================================================================================================================
+//	ComputerDescriptionDidChange
+//===========================================================================================================================
+void ComputerDescriptionDidChange( mDNS * const inMDNS )
+{	
+	dlog( kDebugLevelInfo, DEBUG_NAME "computer description has changed\n" );
+	check( inMDNS );
+
+	// redo the names
+	SetupNiceName( inMDNS );
+}
+
+
+//===========================================================================================================================
+//	TCPIPConfigDidChange
+//===========================================================================================================================
+void TCPIPConfigDidChange( mDNS * const inMDNS )
+{
+	mStatus		err;
+	
+	dlog( kDebugLevelInfo, DEBUG_NAME "TCP/IP config has changed\n" );
+	check( inMDNS );
+
+	err = uDNS_SetupDNSConfig( inMDNS );
+	check_noerr( err );
+}
+
+
+//===========================================================================================================================
+//	DynDNSConfigDidChange
+//===========================================================================================================================
+void DynDNSConfigDidChange( mDNS * const inMDNS )
+{
+	mStatus		err;
+	
+	dlog( kDebugLevelInfo, DEBUG_NAME "DynDNS config has changed\n" );
+	check( inMDNS );
+
+	SetDomainSecrets( inMDNS );
+
+	err = uDNS_SetupDNSConfig( inMDNS );
+	check_noerr( err );
+}
+
+
+//===========================================================================================================================
+//	FileSharingDidChange
+//===========================================================================================================================
+void FileSharingDidChange( mDNS * const inMDNS )
+{	
+	dlog( kDebugLevelInfo, DEBUG_NAME "File shares has changed\n" );
+	check( inMDNS );
+
+	CheckFileShares( inMDNS );
+}
+
+
+//===========================================================================================================================
+//	FilewallDidChange
+//===========================================================================================================================
+void FirewallDidChange( mDNS * const inMDNS )
+{	
+	dlog( kDebugLevelInfo, DEBUG_NAME "Firewall has changed\n" );
+	check( inMDNS );
+
+	CheckFileShares( inMDNS );
+}
+
+
+#if 0
+#pragma mark -
+#pragma mark == Utilities ==
+#endif
+
+//===========================================================================================================================
+//	getifaddrs
+//===========================================================================================================================
+
+mDNSlocal int	getifaddrs( struct ifaddrs **outAddrs )
+{
+	int		err;
+	
+#if( MDNS_WINDOWS_USE_IPV6_IF_ADDRS )
+	
+	// Try to the load the GetAdaptersAddresses function from the IP Helpers DLL. This API is only available on Windows
+	// XP or later. Looking up the symbol at runtime allows the code to still work on older systems without that API.
+	
+	if( !gIPHelperLibraryInstance )
+	{
+		gIPHelperLibraryInstance = LoadLibrary( TEXT( "Iphlpapi" ) );
+		if( gIPHelperLibraryInstance )
+		{
+			gGetAdaptersAddressesFunctionPtr = 
+				(GetAdaptersAddressesFunctionPtr) GetProcAddress( gIPHelperLibraryInstance, "GetAdaptersAddresses" );
+			if( !gGetAdaptersAddressesFunctionPtr )
+			{
+				BOOL		ok;
+				
+				ok = FreeLibrary( gIPHelperLibraryInstance );
+				check_translated_errno( ok, GetLastError(), kUnknownErr );
+				gIPHelperLibraryInstance = NULL;
+			}
+		}
+	}
+	
+	// Use the new IPv6-capable routine if supported. Otherwise, fall back to the old and compatible IPv4-only code.
+	// <rdar://problem/4278934>  Fall back to using getifaddrs_ipv4 if getifaddrs_ipv6 fails
+	// <rdar://problem/6145913>  Fall back to using getifaddrs_ipv4 if getifaddrs_ipv6 returns no addrs
+
+	if( !gGetAdaptersAddressesFunctionPtr || ( ( ( err = getifaddrs_ipv6( outAddrs ) ) != mStatus_NoError ) || ( ( outAddrs != NULL ) && ( *outAddrs == NULL ) ) ) )
+	{
+		err = getifaddrs_ipv4( outAddrs );
+		require_noerr( err, exit );
+	}
+	
+#else
+
+	err = getifaddrs_ipv4( outAddrs );
+	require_noerr( err, exit );
+
+#endif
+
+exit:
+	return( err );
+}
+
+#if( MDNS_WINDOWS_USE_IPV6_IF_ADDRS )
+//===========================================================================================================================
+//	getifaddrs_ipv6
+//===========================================================================================================================
+
+mDNSlocal int	getifaddrs_ipv6( struct ifaddrs **outAddrs )
+{
+	DWORD						err;
+	int							i;
+	DWORD						flags;
+	struct ifaddrs *			head;
+	struct ifaddrs **			next;
+	IP_ADAPTER_ADDRESSES *		iaaList;
+	ULONG						iaaListSize;
+	IP_ADAPTER_ADDRESSES *		iaa;
+	size_t						size;
+	struct ifaddrs *			ifa;
+	
+	check( gGetAdaptersAddressesFunctionPtr );
+	
+	head	= NULL;
+	next	= &head;
+	iaaList	= NULL;
+	
+	// Get the list of interfaces. The first call gets the size and the second call gets the actual data.
+	// This loops to handle the case where the interface changes in the window after getting the size, but before the
+	// second call completes. A limit of 100 retries is enforced to prevent infinite loops if something else is wrong.
+	
+	flags = GAA_FLAG_INCLUDE_PREFIX | GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME;
+	i = 0;
+	for( ;; )
+	{
+		iaaListSize = 0;
+		err = gGetAdaptersAddressesFunctionPtr( AF_UNSPEC, flags, NULL, NULL, &iaaListSize );
+		check( err == ERROR_BUFFER_OVERFLOW );
+		check( iaaListSize >= sizeof( IP_ADAPTER_ADDRESSES ) );
+		
+		iaaList = (IP_ADAPTER_ADDRESSES *) malloc( iaaListSize );
+		require_action( iaaList, exit, err = ERROR_NOT_ENOUGH_MEMORY );
+		
+		err = gGetAdaptersAddressesFunctionPtr( AF_UNSPEC, flags, NULL, iaaList, &iaaListSize );
+		if( err == ERROR_SUCCESS ) break;
+		
+		free( iaaList );
+		iaaList = NULL;
+		++i;
+		require( i < 100, exit );
+		dlog( kDebugLevelWarning, "%s: retrying GetAdaptersAddresses after %d failure(s) (%d %m)\n", __ROUTINE__, i, err, err );
+	}
+	
+	for( iaa = iaaList; iaa; iaa = iaa->Next )
+	{
+		int								addrIndex;
+		IP_ADAPTER_UNICAST_ADDRESS	*	addr;
+		DWORD							ipv6IfIndex;
+		IP_ADAPTER_PREFIX			*	firstPrefix;
+
+		if( iaa->IfIndex > 0xFFFFFF )
+		{
+			dlog( kDebugLevelAlert, DEBUG_NAME "%s: IPv4 ifindex out-of-range (0x%08X)\n", __ROUTINE__, iaa->IfIndex );
+		}
+		if( iaa->Ipv6IfIndex > 0xFF )
+		{
+			dlog( kDebugLevelAlert, DEBUG_NAME "%s: IPv6 ifindex out-of-range (0x%08X)\n", __ROUTINE__, iaa->Ipv6IfIndex );
+		}
+
+		// For IPv4 interfaces, there seems to be a bug in iphlpapi.dll that causes the 
+		// following code to crash when iterating through the prefix list.  This seems
+		// to occur when iaa->Ipv6IfIndex != 0 when IPv6 is not installed on the host.
+		// This shouldn't happen according to Microsoft docs which states:
+		//
+		//     "Ipv6IfIndex contains 0 if IPv6 is not available on the interface."
+		//
+		// So the data structure seems to be corrupted when we return from
+		// GetAdaptersAddresses(). The bug seems to occur when iaa->Length <
+		// sizeof(IP_ADAPTER_ADDRESSES), so when that happens, we'll manually
+		// modify iaa to have the correct values.
+
+		if ( iaa->Length >= sizeof( IP_ADAPTER_ADDRESSES ) )
+		{
+			ipv6IfIndex = iaa->Ipv6IfIndex;
+			firstPrefix = iaa->FirstPrefix;
+		}
+		else
+		{
+			ipv6IfIndex	= 0;
+			firstPrefix = NULL;
+		}
+
+		// Skip pseudo and tunnel interfaces.
+		
+		if( ( ( ipv6IfIndex == 1 ) && ( iaa->IfType != IF_TYPE_SOFTWARE_LOOPBACK ) ) || ( iaa->IfType == IF_TYPE_TUNNEL ) )
+		{
+			continue;
+		}
+		
+		// Add each address as a separate interface to emulate the way getifaddrs works.
+		
+		for( addrIndex = 0, addr = iaa->FirstUnicastAddress; addr; ++addrIndex, addr = addr->Next )
+		{			
+			int						family;
+			int						prefixIndex;
+			IP_ADAPTER_PREFIX *		prefix;
+			ULONG					prefixLength;
+			uint32_t				ipv4Index;
+			struct sockaddr_in		ipv4Netmask;
+
+			family = addr->Address.lpSockaddr->sa_family;
+			if( ( family != AF_INET ) && ( family != AF_INET6 ) ) continue;
+			
+			// <rdar://problem/6220642> iTunes 8: Bonjour doesn't work after upgrading iTunes 8
+			// Seems as if the problem here is a buggy implementation of some network interface
+			// driver. It is reporting that is has a link-local address when it is actually
+			// disconnected. This was causing a problem in AddressToIndexAndMask.
+			// The solution is to call AddressToIndexAndMask first, and if unable to lookup
+			// the address, to ignore that address.
+
+			ipv4Index = 0;
+			memset( &ipv4Netmask, 0, sizeof( ipv4Netmask ) );
+			
+			if ( family == AF_INET )
+			{
+				err = AddressToIndexAndMask( addr->Address.lpSockaddr, &ipv4Index, ( struct sockaddr* ) &ipv4Netmask );
+				
+				if ( err )
+				{
+					err = 0;
+					continue;
+				}
+			}
+
+			ifa = (struct ifaddrs *) calloc( 1, sizeof( struct ifaddrs ) );
+			require_action( ifa, exit, err = WSAENOBUFS );
+			
+			*next = ifa;
+			next  = &ifa->ifa_next;
+			
+			// Get the name.
+			
+			size = strlen( iaa->AdapterName ) + 1;
+			ifa->ifa_name = (char *) malloc( size );
+			require_action( ifa->ifa_name, exit, err = WSAENOBUFS );
+			memcpy( ifa->ifa_name, iaa->AdapterName, size );
+			
+			// Get interface flags.
+			
+			ifa->ifa_flags = 0;
+			if( iaa->OperStatus == IfOperStatusUp ) 		ifa->ifa_flags |= IFF_UP;
+			if( iaa->IfType == IF_TYPE_SOFTWARE_LOOPBACK )	ifa->ifa_flags |= IFF_LOOPBACK;
+			else if ( IsPointToPoint( addr ) )				ifa->ifa_flags |= IFF_POINTTOPOINT;
+			if( !( iaa->Flags & IP_ADAPTER_NO_MULTICAST ) )	ifa->ifa_flags |= IFF_MULTICAST;
+
+			
+			// <rdar://problem/4045657> Interface index being returned is 512
+			//
+			// Windows does not have a uniform scheme for IPv4 and IPv6 interface indexes.
+			// This code used to shift the IPv4 index up to ensure uniqueness between
+			// it and IPv6 indexes.  Although this worked, it was somewhat confusing to developers, who
+			// then see interface indexes passed back that don't correspond to anything
+			// that is seen in Win32 APIs or command line tools like "route".  As a relatively
+			// small percentage of developers are actively using IPv6, it seems to 
+			// make sense to make our use of IPv4 as confusion free as possible.
+			// So now, IPv6 interface indexes will be shifted up by a
+			// constant value which will serve to uniquely identify them, and we will
+			// leave IPv4 interface indexes unmodified.
+			
+			switch( family )
+			{
+				case AF_INET:  ifa->ifa_extra.index = iaa->IfIndex; break;
+				case AF_INET6: ifa->ifa_extra.index = ipv6IfIndex + kIPv6IfIndexBase;	 break;
+				default: break;
+			}
+
+			// Get lease lifetime
+
+			if ( ( iaa->IfType != IF_TYPE_SOFTWARE_LOOPBACK ) && ( addr->LeaseLifetime != 0 ) && ( addr->ValidLifetime != 0xFFFFFFFF ) )
+			{
+				ifa->ifa_dhcpEnabled		= TRUE;
+				ifa->ifa_dhcpLeaseExpires	= time( NULL ) + addr->ValidLifetime;
+			}
+			else
+			{
+				ifa->ifa_dhcpEnabled		= FALSE;
+				ifa->ifa_dhcpLeaseExpires	= 0;
+			}
+
+			if ( iaa->PhysicalAddressLength == sizeof( ifa->ifa_physaddr ) )
+			{
+				memcpy( ifa->ifa_physaddr, iaa->PhysicalAddress, iaa->PhysicalAddressLength );
+			}
+
+			// Because we don't get notified of womp changes, we're going to just assume
+			// that all wired interfaces have it enabled. Before we go to sleep, we'll check
+			// if the interface actually supports it, and update mDNS->SystemWakeOnLANEnabled
+			// accordingly
+
+			ifa->ifa_womp = ( iaa->IfType == IF_TYPE_ETHERNET_CSMACD ) ? mDNStrue : mDNSfalse;
+			
+			// Get address.
+			
+			switch( family )
+			{
+				case AF_INET:
+				case AF_INET6:
+					ifa->ifa_addr = (struct sockaddr *) calloc( 1, (size_t) addr->Address.iSockaddrLength );
+					require_action( ifa->ifa_addr, exit, err = WSAENOBUFS );
+					memcpy( ifa->ifa_addr, addr->Address.lpSockaddr, (size_t) addr->Address.iSockaddrLength );
+					break;
+				
+				default:
+					break;
+			}
+			check( ifa->ifa_addr );
+			
+			// Get subnet mask (IPv4)/link prefix (IPv6). It is specified as a bit length (e.g. 24 for 255.255.255.0).
+			
+			prefixLength = 0;
+			for( prefixIndex = 0, prefix = firstPrefix; prefix; ++prefixIndex, prefix = prefix->Next )
+			{
+				if( ( prefix->Address.lpSockaddr->sa_family == family ) && ( prefixIndex == addrIndex ) )
+				{
+					check_string( prefix->Address.lpSockaddr->sa_family == family, "addr family != netmask family" );
+					prefixLength = prefix->PrefixLength;
+					break;
+				}
+			}
+			switch( family )
+			{
+				case AF_INET:
+				{
+					struct sockaddr_in * sa4;
+					
+					sa4 = (struct sockaddr_in *) calloc( 1, sizeof( *sa4 ) );
+					require_action( sa4, exit, err = WSAENOBUFS );
+					sa4->sin_family = AF_INET;
+					sa4->sin_addr.s_addr = ipv4Netmask.sin_addr.s_addr;
+
+					dlog( kDebugLevelInfo, DEBUG_NAME "%s: IPv4 mask = %s\n", __ROUTINE__, inet_ntoa( sa4->sin_addr ) );
+					ifa->ifa_netmask = (struct sockaddr *) sa4;
+					break;
+				}
+				
+				case AF_INET6:
+				{
+					struct sockaddr_in6 *		sa6;
+					int							len;
+					int							maskIndex;
+					uint8_t						maskByte;
+					
+					require_action( prefixLength <= 128, exit, err = ERROR_INVALID_DATA );
+					
+					sa6 = (struct sockaddr_in6 *) calloc( 1, sizeof( *sa6 ) );
+					require_action( sa6, exit, err = WSAENOBUFS );
+					sa6->sin6_family = AF_INET6;
+					
+					if( prefixLength == 0 )
+					{
+						dlog( kDebugLevelWarning, DEBUG_NAME "%s: IPv6 link prefix 0, defaulting to /128\n", __ROUTINE__ );
+						prefixLength = 128;
+					}
+					maskIndex = 0;
+					for( len = (int) prefixLength; len > 0; len -= 8 )
+					{
+						if( len >= 8 ) maskByte = 0xFF;
+						else		   maskByte = (uint8_t)( ( 0xFFU << ( 8 - len ) ) & 0xFFU );
+						sa6->sin6_addr.s6_addr[ maskIndex++ ] = maskByte;
+					}
+					ifa->ifa_netmask = (struct sockaddr *) sa6;
+					break;
+				}
+				
+				default:
+					break;
+			}
+		}
+	}
+	
+	// Success!
+	
+	if( outAddrs )
+	{
+		*outAddrs = head;
+		head = NULL;
+	}
+	err = ERROR_SUCCESS;
+	
+exit:
+	if( head )
+	{
+		freeifaddrs( head );
+	}
+	if( iaaList )
+	{
+		free( iaaList );
+	}
+	return( (int) err );
+}
+
+#endif	// MDNS_WINDOWS_USE_IPV6_IF_ADDRS
+
+//===========================================================================================================================
+//	getifaddrs_ipv4
+//===========================================================================================================================
+
+mDNSlocal int	getifaddrs_ipv4( struct ifaddrs **outAddrs )
+{
+	int						err;
+	SOCKET					sock;
+	DWORD					size;
+	DWORD					actualSize;
+	INTERFACE_INFO *		buffer;
+	INTERFACE_INFO *		tempBuffer;
+	INTERFACE_INFO *		ifInfo;
+	int						n;
+	int						i;
+	struct ifaddrs *		head;
+	struct ifaddrs **		next;
+	struct ifaddrs *		ifa;
+	
+	sock	= INVALID_SOCKET;
+	buffer	= NULL;
+	head	= NULL;
+	next	= &head;
+	
+	// Get the interface list. WSAIoctl is called with SIO_GET_INTERFACE_LIST, but since this does not provide a 
+	// way to determine the size of the interface list beforehand, we have to start with an initial size guess and
+	// call WSAIoctl repeatedly with increasing buffer sizes until it succeeds. Limit this to 100 tries for safety.
+	
+	sock = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
+	err = translate_errno( IsValidSocket( sock ), errno_compat(), kUnknownErr );
+	require_noerr( err, exit );
+		
+	n = 0;
+	size = 16 * sizeof( INTERFACE_INFO );
+	for( ;; )
+	{
+		tempBuffer = (INTERFACE_INFO *) realloc( buffer, size );
+		require_action( tempBuffer, exit, err = WSAENOBUFS );
+		buffer = tempBuffer;
+		
+		err = WSAIoctl( sock, SIO_GET_INTERFACE_LIST, NULL, 0, buffer, size, &actualSize, NULL, NULL );
+		if( err == 0 )
+		{
+			break;
+		}
+		
+		++n;
+		require_action( n < 100, exit, err = WSAEADDRNOTAVAIL );
+		
+		size += ( 16 * sizeof( INTERFACE_INFO ) );
+	}
+	check( actualSize <= size );
+	check( ( actualSize % sizeof( INTERFACE_INFO ) ) == 0 );
+	n = (int)( actualSize / sizeof( INTERFACE_INFO ) );
+	
+	// Process the raw interface list and build a linked list of IPv4 interfaces.
+	
+	for( i = 0; i < n; ++i )
+	{
+		uint32_t ifIndex;
+		struct sockaddr_in netmask;
+		
+		ifInfo = &buffer[ i ];
+		if( ifInfo->iiAddress.Address.sa_family != AF_INET )
+		{
+			continue;
+		}
+		
+		// <rdar://problem/6220642> iTunes 8: Bonjour doesn't work after upgrading iTunes 8
+		// See comment in getifaddrs_ipv6
+
+		ifIndex = 0;
+		memset( &netmask, 0, sizeof( netmask ) );
+		err = AddressToIndexAndMask( ( struct sockaddr* ) &ifInfo->iiAddress.AddressIn, &ifIndex, ( struct sockaddr* ) &netmask );
+
+		if ( err )
+		{
+			continue;
+		}
+
+		ifa = (struct ifaddrs *) calloc( 1, sizeof( struct ifaddrs ) );
+		require_action( ifa, exit, err = WSAENOBUFS );
+		
+		*next = ifa;
+		next  = &ifa->ifa_next;
+		
+		// Get the name.
+		
+		ifa->ifa_name = (char *) malloc( 16 );
+		require_action( ifa->ifa_name, exit, err = WSAENOBUFS );
+		sprintf( ifa->ifa_name, "%d", i + 1 );
+		
+		// Get interface flags.
+		
+		ifa->ifa_flags = (u_int) ifInfo->iiFlags;
+		
+		// Get addresses.
+		
+		if ( ifInfo->iiAddress.Address.sa_family == AF_INET )
+		{
+			struct sockaddr_in *		sa4;
+			
+			sa4 = &ifInfo->iiAddress.AddressIn;
+			ifa->ifa_addr = (struct sockaddr *) calloc( 1, sizeof( *sa4 ) );
+			require_action( ifa->ifa_addr, exit, err = WSAENOBUFS );
+			memcpy( ifa->ifa_addr, sa4, sizeof( *sa4 ) );
+
+			ifa->ifa_netmask = (struct sockaddr*) calloc(1, sizeof( *sa4 ) );
+			require_action( ifa->ifa_netmask, exit, err = WSAENOBUFS );
+
+			// <rdar://problem/4076478> Service won't start on Win2K. The address
+			// family field was not being initialized.
+
+			ifa->ifa_netmask->sa_family = AF_INET;
+			( ( struct sockaddr_in* ) ifa->ifa_netmask )->sin_addr = netmask.sin_addr;
+			ifa->ifa_extra.index = ifIndex;
+		}
+		else
+		{
+			// Emulate an interface index.
+		
+			ifa->ifa_extra.index = (uint32_t)( i + 1 );
+		}
+	}
+	
+	// Success!
+	
+	if( outAddrs )
+	{
+		*outAddrs = head;
+		head = NULL;
+	}
+	err = 0;
+	
+exit:
+
+	if( head )
+	{
+		freeifaddrs( head );
+	}
+	if( buffer )
+	{
+		free( buffer );
+	}
+	if( sock != INVALID_SOCKET )
+	{
+		closesocket( sock );
+	}
+	return( err );
+}
+
+//===========================================================================================================================
+//	freeifaddrs
+//===========================================================================================================================
+
+mDNSlocal void	freeifaddrs( struct ifaddrs *inIFAs )
+{
+	struct ifaddrs *		p;
+	struct ifaddrs *		q;
+	
+	// Free each piece of the structure. Set to null after freeing to handle macro-aliased fields.
+	
+	for( p = inIFAs; p; p = q )
+	{
+		q = p->ifa_next;
+		
+		if( p->ifa_name )
+		{
+			free( p->ifa_name );
+			p->ifa_name = NULL;
+		}
+		if( p->ifa_addr )
+		{
+			free( p->ifa_addr );
+			p->ifa_addr = NULL;
+		}
+		if( p->ifa_netmask )
+		{
+			free( p->ifa_netmask );
+			p->ifa_netmask = NULL;
+		}
+		if( p->ifa_broadaddr )
+		{
+			free( p->ifa_broadaddr );
+			p->ifa_broadaddr = NULL;
+		}
+		if( p->ifa_dstaddr )
+		{
+			free( p->ifa_dstaddr );
+			p->ifa_dstaddr = NULL;
+		}
+		if( p->ifa_data )
+		{
+			free( p->ifa_data );
+			p->ifa_data = NULL;
+		}
+		free( p );
+	}
+}
+
+
+//===========================================================================================================================
+//	GetPrimaryInterface
+//===========================================================================================================================
+
+mDNSlocal DWORD
+GetPrimaryInterface()
+{
+	PMIB_IPFORWARDTABLE	pIpForwardTable	= NULL;
+	DWORD				dwSize			= 0;
+	BOOL				bOrder			= FALSE;
+	OSStatus			err;
+	DWORD				index			= 0;
+	DWORD				metric			= 0;
+	unsigned long int	i;
+
+	// Find out how big our buffer needs to be.
+
+	err = GetIpForwardTable(NULL, &dwSize, bOrder);
+	require_action( err == ERROR_INSUFFICIENT_BUFFER, exit, err = kUnknownErr );
+
+	// Allocate the memory for the table
+
+	pIpForwardTable = (PMIB_IPFORWARDTABLE) malloc( dwSize );
+	require_action( pIpForwardTable, exit, err = kNoMemoryErr );
+  
+	// Now get the table.
+
+	err = GetIpForwardTable(pIpForwardTable, &dwSize, bOrder);
+	require_noerr( err, exit );
+
+
+	// Search for the row in the table we want.
+
+	for ( i = 0; i < pIpForwardTable->dwNumEntries; i++)
+	{
+		// Look for a default route
+
+		if ( pIpForwardTable->table[i].dwForwardDest == 0 )
+		{
+			if ( index && ( pIpForwardTable->table[i].dwForwardMetric1 >= metric ) )
+			{
+				continue;
+			}
+
+			index	= pIpForwardTable->table[i].dwForwardIfIndex;
+			metric	= pIpForwardTable->table[i].dwForwardMetric1;
+		}
+	}
+
+exit:
+
+	if ( pIpForwardTable != NULL )
+	{
+		free( pIpForwardTable );
+	}
+
+	return index;
+}
+
+
+//===========================================================================================================================
+//	AddressToIndexAndMask
+//===========================================================================================================================
+
+mDNSlocal mStatus
+AddressToIndexAndMask( struct sockaddr * addr, uint32_t * ifIndex, struct sockaddr * mask  )
+{
+	// Before calling AddIPAddress we use GetIpAddrTable to get
+	// an adapter to which we can add the IP.
+	
+	PMIB_IPADDRTABLE	pIPAddrTable	= NULL;
+	DWORD				dwSize			= 0;
+	mStatus				err				= mStatus_UnknownErr;
+	DWORD				i;
+
+	// For now, this is only for IPv4 addresses.  That is why we can safely cast
+	// addr's to sockaddr_in.
+
+	require_action( addr->sa_family == AF_INET, exit, err = mStatus_UnknownErr );
+
+	// Make an initial call to GetIpAddrTable to get the
+	// necessary size into the dwSize variable
+
+	for ( i = 0; i < 100; i++ )
+	{
+		err = GetIpAddrTable( pIPAddrTable, &dwSize, 0 );
+
+		if ( err != ERROR_INSUFFICIENT_BUFFER )
+		{
+			break;
+		}
+
+		pIPAddrTable = (MIB_IPADDRTABLE *) realloc( pIPAddrTable, dwSize );
+		require_action( pIPAddrTable, exit, err = WSAENOBUFS );
+	}
+
+	require_noerr( err, exit );
+	err = mStatus_UnknownErr;
+
+	for ( i = 0; i < pIPAddrTable->dwNumEntries; i++ )
+	{
+		if ( ( ( struct sockaddr_in* ) addr )->sin_addr.s_addr == pIPAddrTable->table[i].dwAddr )
+		{
+			*ifIndex											= pIPAddrTable->table[i].dwIndex;
+			( ( struct sockaddr_in*) mask )->sin_addr.s_addr	= pIPAddrTable->table[i].dwMask;
+			err													= mStatus_NoError;
+			break;
+		}
+	}
+
+exit:
+
+	if ( pIPAddrTable )
+	{
+		free( pIPAddrTable );
+	}
+
+	return err;
+}
+
+
+//===========================================================================================================================
+//	CanReceiveUnicast
+//===========================================================================================================================
+
+mDNSlocal mDNSBool	CanReceiveUnicast( void )
+{
+	mDNSBool				ok;
+	SocketRef				sock;
+	struct sockaddr_in		addr;
+	
+	// Try to bind to the port without the SO_REUSEADDR option to test if someone else has already bound to it.
+	
+	sock = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );
+	check_translated_errno( IsValidSocket( sock ), errno_compat(), kUnknownErr );
+	ok = IsValidSocket( sock );
+	if( ok )
+	{
+		mDNSPlatformMemZero( &addr, sizeof( addr ) );
+		addr.sin_family			= AF_INET;
+		addr.sin_port			= MulticastDNSPort.NotAnInteger;
+		addr.sin_addr.s_addr	= htonl( INADDR_ANY );
+		
+		ok = ( bind( sock, (struct sockaddr *) &addr, sizeof( addr ) ) == 0 );
+		close_compat( sock );
+	}
+	
+	dlog( kDebugLevelInfo, DEBUG_NAME "Unicast UDP responses %s\n", ok ? "okay" : "*not allowed*" );
+	return( ok );
+}
+
+
+//===========================================================================================================================
+//	IsPointToPoint
+//===========================================================================================================================
+
+mDNSlocal mDNSBool IsPointToPoint( IP_ADAPTER_UNICAST_ADDRESS * addr )
+{
+	struct ifaddrs	*	addrs	=	NULL;
+	struct ifaddrs	*	p		=	NULL;
+	OSStatus			err;
+	mDNSBool			ret		=	mDNSfalse;
+
+	// For now, only works for IPv4 interfaces
+
+	if ( addr->Address.lpSockaddr->sa_family == AF_INET )
+	{
+		// The getifaddrs_ipv4 call will give us correct information regarding IFF_POINTTOPOINT flags.
+
+		err = getifaddrs_ipv4( &addrs );
+		require_noerr( err, exit );
+
+		for ( p = addrs; p; p = p->ifa_next )
+		{
+			if ( ( addr->Address.lpSockaddr->sa_family == p->ifa_addr->sa_family ) &&
+			     ( ( ( struct sockaddr_in* ) addr->Address.lpSockaddr )->sin_addr.s_addr == ( ( struct sockaddr_in* ) p->ifa_addr )->sin_addr.s_addr ) )
+			{
+				ret = ( p->ifa_flags & IFF_POINTTOPOINT ) ? mDNStrue : mDNSfalse;
+				break;
+			}
+		}
+	}
+
+exit:
+
+	if ( addrs )
+	{
+		freeifaddrs( addrs );
+	}
+
+	return ret;
+}
+
+
+//===========================================================================================================================
+//	GetWindowsVersionString
+//===========================================================================================================================
+
+mDNSlocal OSStatus	GetWindowsVersionString( char *inBuffer, size_t inBufferSize )
+{
+#if( !defined( VER_PLATFORM_WIN32_CE ) )
+	#define VER_PLATFORM_WIN32_CE		3
+#endif
+
+	OSStatus				err;
+	OSVERSIONINFO			osInfo;
+	BOOL					ok;
+	const char *			versionString;
+	DWORD					platformID;
+	DWORD					majorVersion;
+	DWORD					minorVersion;
+	DWORD					buildNumber;
+	
+	versionString = "unknown Windows version";
+	
+	osInfo.dwOSVersionInfoSize = sizeof( OSVERSIONINFO );
+	ok = GetVersionEx( &osInfo );
+	err = translate_errno( ok, (OSStatus) GetLastError(), kUnknownErr );
+	require_noerr( err, exit );
+	
+	platformID		= osInfo.dwPlatformId;
+	majorVersion	= osInfo.dwMajorVersion;
+	minorVersion	= osInfo.dwMinorVersion;
+	buildNumber		= osInfo.dwBuildNumber & 0xFFFF;
+	
+	if( ( platformID == VER_PLATFORM_WIN32_WINDOWS ) && ( majorVersion == 4 ) )
+	{
+		if( ( minorVersion < 10 ) && ( buildNumber == 950 ) )
+		{
+			versionString	= "Windows 95";
+		}
+		else if( ( minorVersion < 10 ) && ( ( buildNumber > 950 ) && ( buildNumber <= 1080 ) ) )
+		{
+			versionString	= "Windows 95 SP1";
+		}
+		else if( ( minorVersion < 10 ) && ( buildNumber > 1080 ) )
+		{
+			versionString	= "Windows 95 OSR2";
+		}
+		else if( ( minorVersion == 10 ) && ( buildNumber == 1998 ) )
+		{
+			versionString	= "Windows 98";
+		}
+		else if( ( minorVersion == 10 ) && ( ( buildNumber > 1998 ) && ( buildNumber < 2183 ) ) )
+		{
+			versionString	= "Windows 98 SP1";
+		}
+		else if( ( minorVersion == 10 ) && ( buildNumber >= 2183 ) )
+		{
+			versionString	= "Windows 98 SE";
+		}
+		else if( minorVersion == 90 )
+		{
+			versionString	= "Windows ME";
+		}
+	}
+	else if( platformID == VER_PLATFORM_WIN32_NT )
+	{
+		if( ( majorVersion == 3 ) && ( minorVersion == 51 ) )
+		{
+			versionString	= "Windows NT 3.51";
+		}
+		else if( ( majorVersion == 4 ) && ( minorVersion == 0 ) )
+		{
+			versionString	= "Windows NT 4";
+		}
+		else if( ( majorVersion == 5 ) && ( minorVersion == 0 ) )
+		{
+			versionString	= "Windows 2000";
+		}
+		else if( ( majorVersion == 5 ) && ( minorVersion == 1 ) )
+		{
+			versionString	= "Windows XP";
+		}
+		else if( ( majorVersion == 5 ) && ( minorVersion == 2 ) )
+		{
+			versionString	= "Windows Server 2003";
+		}
+	}
+	else if( platformID == VER_PLATFORM_WIN32_CE )
+	{
+		versionString		= "Windows CE";
+	}
+	
+exit:
+	if( inBuffer && ( inBufferSize > 0 ) )
+	{
+		inBufferSize -= 1;
+		strncpy( inBuffer, versionString, inBufferSize );
+		inBuffer[ inBufferSize ] = '\0';
+	}
+	return( err );
+}
+
+
+//===========================================================================================================================
+//	RegQueryString
+//===========================================================================================================================
+
+mDNSlocal mStatus
+RegQueryString( HKEY key, LPCSTR valueName, LPSTR * string, DWORD * stringLen, DWORD * enabled )
+{
+	DWORD	type;
+	int		i;
+	mStatus err;
+
+	*stringLen	= MAX_ESCAPED_DOMAIN_NAME;
+	*string		= NULL;
+	i			= 0;
+
+	do
+	{
+		if ( *string )
+		{
+			free( *string );
+		}
+
+		*string = (char*) malloc( *stringLen );
+		require_action( *string, exit, err = mStatus_NoMemoryErr );
+
+		err = RegQueryValueExA( key, valueName, 0, &type, (LPBYTE) *string, stringLen );
+
+		i++;
+	}
+	while ( ( err == ERROR_MORE_DATA ) && ( i < 100 ) );
+
+	require_noerr_quiet( err, exit );
+
+	if ( enabled )
+	{
+		DWORD dwSize = sizeof( DWORD );
+
+		err = RegQueryValueEx( key, TEXT("Enabled"), NULL, NULL, (LPBYTE) enabled, &dwSize );
+		check_noerr( err );
+
+		err = kNoErr;
+	}
+
+exit:
+
+	return err;
+}
+
+
+//===========================================================================================================================
+//	StringToAddress
+//===========================================================================================================================
+
+mDNSlocal mStatus StringToAddress( mDNSAddr * ip, LPSTR string )
+{
+	struct sockaddr_in6 sa6;
+	struct sockaddr_in	sa4;
+	INT					dwSize;
+	mStatus				err;
+
+	sa6.sin6_family	= AF_INET6;
+	dwSize			= sizeof( sa6 );
+
+	err = WSAStringToAddressA( string, AF_INET6, NULL, (struct sockaddr*) &sa6, &dwSize );
+
+	if ( err == mStatus_NoError )
+	{
+		err = SetupAddr( ip, (struct sockaddr*) &sa6 );
+		require_noerr( err, exit );
+	}
+	else
+	{
+		sa4.sin_family = AF_INET;
+		dwSize = sizeof( sa4 );
+
+		err = WSAStringToAddressA( string, AF_INET, NULL, (struct sockaddr*) &sa4, &dwSize );
+		err = translate_errno( err == 0, WSAGetLastError(), kUnknownErr );
+		require_noerr( err, exit );
+			
+		err = SetupAddr( ip, (struct sockaddr*) &sa4 );
+		require_noerr( err, exit );
+	}
+
+exit:
+
+	return err;
+}
+
+
+//===========================================================================================================================
+//	myGetIfAddrs
+//===========================================================================================================================
+
+mDNSlocal struct ifaddrs*
+myGetIfAddrs(int refresh)
+{
+	static struct ifaddrs *ifa = NULL;
+	
+	if (refresh && ifa)
+	{
+		freeifaddrs(ifa);
+		ifa = NULL;
+	}
+	
+	if (ifa == NULL)
+	{
+		getifaddrs(&ifa);
+	}
+	
+	return ifa;
+}
+
+
+//===========================================================================================================================
+//	TCHARtoUTF8
+//===========================================================================================================================
+
+mDNSlocal OSStatus
+TCHARtoUTF8( const TCHAR *inString, char *inBuffer, size_t inBufferSize )
+{
+#if( defined( UNICODE ) || defined( _UNICODE ) )
+	OSStatus		err;
+	int				len;
+	
+	len = WideCharToMultiByte( CP_UTF8, 0, inString, -1, inBuffer, (int) inBufferSize, NULL, NULL );
+	err = translate_errno( len > 0, errno_compat(), kUnknownErr );
+	require_noerr( err, exit );
+	
+exit:
+	return( err );
+#else
+	return( WindowsLatin1toUTF8( inString, inBuffer, inBufferSize ) );
+#endif
+}
+
+
+//===========================================================================================================================
+//	WindowsLatin1toUTF8
+//===========================================================================================================================
+
+mDNSlocal OSStatus
+WindowsLatin1toUTF8( const char *inString, char *inBuffer, size_t inBufferSize )
+{
+	OSStatus		err;
+	WCHAR *			utf16;
+	int				len;
+	
+	utf16 = NULL;
+	
+	// Windows doesn't support going directly from Latin-1 to UTF-8 so we have to go from Latin-1 to UTF-16 first.
+	
+	len = MultiByteToWideChar( CP_ACP, 0, inString, -1, NULL, 0 );
+	err = translate_errno( len > 0, errno_compat(), kUnknownErr );
+	require_noerr( err, exit );
+	
+	utf16 = (WCHAR *) malloc( len * sizeof( *utf16 ) );
+	require_action( utf16, exit, err = kNoMemoryErr );
+	
+	len = MultiByteToWideChar( CP_ACP, 0, inString, -1, utf16, len );
+	err = translate_errno( len > 0, errno_compat(), kUnknownErr );
+	require_noerr( err, exit );
+	
+	// Now convert the temporary UTF-16 to UTF-8.
+	
+	len = WideCharToMultiByte( CP_UTF8, 0, utf16, -1, inBuffer, (int) inBufferSize, NULL, NULL );
+	err = translate_errno( len > 0, errno_compat(), kUnknownErr );
+	require_noerr( err, exit );
+
+exit:
+	if( utf16 ) free( utf16 );
+	return( err );
+}
+
+
+//===========================================================================================================================
+//	TCPCloseSocket
+//===========================================================================================================================
+
+mDNSlocal void
+TCPCloseSocket( TCPSocket * sock )
+{
+	dlog( kDebugLevelChatty, DEBUG_NAME "closing TCPSocket 0x%x:%d\n", sock, sock->fd );
+
+	RemoveFromList( &gTCPDispatchableSockets, sock );	
+
+	if ( sock->fd != INVALID_SOCKET )
+	{
+		closesocket( sock->fd );
+		sock->fd = INVALID_SOCKET;
+	}
+}
+
+
+//===========================================================================================================================
+//	TCPFreeSocket
+//===========================================================================================================================
+
+mDNSlocal void CALLBACK
+TCPFreeSocket( TCPSocket *sock )
+{
+	check( sock );
+
+	dlog( kDebugLevelChatty, DEBUG_NAME "freeing TCPSocket 0x%x:%d\n", sock, sock->fd );
+	
+	if ( sock->connectEvent )
+	{
+		CloseHandle( sock->connectEvent );
+		sock->connectEvent = NULL;
+	}
+
+	if ( sock->fd != INVALID_SOCKET )
+	{
+		closesocket( sock->fd );
+		sock->fd = INVALID_SOCKET;
+	}
+
+	free( sock );
+}
+
+
+//===========================================================================================================================
+//  UDPCloseSocket
+//===========================================================================================================================
+
+mDNSlocal void
+UDPCloseSocket( UDPSocket * sock )
+{
+	dlog( kDebugLevelChatty, DEBUG_NAME "closing UDPSocket %d\n", sock->fd );
+
+	RemoveFromList( &gUDPDispatchableSockets, sock );
+
+	if ( sock->fd != INVALID_SOCKET )
+	{
+		closesocket( sock->fd );
+		sock->fd = INVALID_SOCKET;
+	}
+}
+
+
+//===========================================================================================================================
+//  UDPFreeSocket
+//===========================================================================================================================
+
+mDNSlocal void CALLBACK
+UDPFreeSocket( UDPSocket * sock )
+{
+    check( sock );
+
+	dlog( kDebugLevelChatty, DEBUG_NAME "freeing UDPSocket %d\n", sock->fd );
+
+    if ( sock->fd != INVALID_SOCKET )
+    {		
+        closesocket( sock->fd );
+		sock->fd = INVALID_SOCKET;
+    }
+
+    free( sock );
+}
+
+//===========================================================================================================================
+//	SetupAddr
+//===========================================================================================================================
+
+mDNSlocal mStatus SetupAddr(mDNSAddr *ip, const struct sockaddr *const sa)
+	{
+	if (!sa) { LogMsg("SetupAddr ERROR: NULL sockaddr"); return(mStatus_Invalid); }
+
+	if (sa->sa_family == AF_INET)
+		{
+		struct sockaddr_in *ifa_addr = (struct sockaddr_in *)sa;
+		ip->type = mDNSAddrType_IPv4;
+		ip->ip.v4.NotAnInteger = ifa_addr->sin_addr.s_addr;
+		return(mStatus_NoError);
+		}
+
+	if (sa->sa_family == AF_INET6)
+		{
+		struct sockaddr_in6 *ifa_addr = (struct sockaddr_in6 *)sa;
+		ip->type = mDNSAddrType_IPv6;
+		if (IN6_IS_ADDR_LINKLOCAL(&ifa_addr->sin6_addr)) ifa_addr->sin6_addr.u.Word[1] = 0;
+		ip->ip.v6 = *(mDNSv6Addr*)&ifa_addr->sin6_addr;
+		return(mStatus_NoError);
+		}
+
+	LogMsg("SetupAddr invalid sa_family %d", sa->sa_family);
+	return(mStatus_Invalid);
+	}
+
+
+mDNSlocal void GetDDNSFQDN( domainname *const fqdn )
+{
+	LPSTR		name = NULL;
+	DWORD		dwSize;
+	DWORD		enabled;
+	HKEY		key = NULL;
+	OSStatus	err;
+
+	check( fqdn );
+
+	// Initialize
+
+	fqdn->c[0] = '\0';
+
+	// Get info from Bonjour registry key
+
+	err = RegCreateKey( HKEY_LOCAL_MACHINE, kServiceParametersNode TEXT("\\DynDNS\\Setup\\") kServiceDynDNSHostNames, &key );
+	require_noerr( err, exit );
+
+	err = RegQueryString( key, "", &name, &dwSize, &enabled );
+	if ( !err && ( name[0] != '\0' ) && enabled )
+	{
+		if ( !MakeDomainNameFromDNSNameString( fqdn, name ) || !fqdn->c[0] )
+		{
+			dlog( kDebugLevelError, "bad DDNS host name in registry: %s", name[0] ? name : "(unknown)");
+		}
+	}
+
+exit:
+
+	if ( key )
+	{
+		RegCloseKey( key );
+		key = NULL;
+	}
+
+	if ( name )
+	{
+		free( name );
+		name = NULL;
+	}
+}
+
+
+#ifdef UNICODE
+mDNSlocal void GetDDNSDomains( DNameListElem ** domains, LPCWSTR lpSubKey )
+#else
+mDNSlocal void GetDDNSConfig( DNameListElem ** domains, LPCSTR lpSubKey )
+#endif
+{
+	char		subKeyName[kRegistryMaxKeyLength + 1];
+	DWORD		cSubKeys = 0;
+	DWORD		cbMaxSubKey;
+	DWORD		cchMaxClass;
+	DWORD		dwSize;
+	HKEY		key = NULL;
+	HKEY		subKey = NULL;
+	domainname	dname;
+	DWORD		i;
+	OSStatus	err;
+
+	check( domains );
+
+	// Initialize
+
+	*domains = NULL;
+
+	err = RegCreateKey( HKEY_LOCAL_MACHINE, lpSubKey, &key );
+	require_noerr( err, exit );
+
+	// Get information about this node
+
+	err = RegQueryInfoKey( key, NULL, NULL, NULL, &cSubKeys, &cbMaxSubKey, &cchMaxClass, NULL, NULL, NULL, NULL, NULL );       
+	require_noerr( err, exit );
+
+	for ( i = 0; i < cSubKeys; i++)
+	{
+		DWORD enabled;
+
+		dwSize = kRegistryMaxKeyLength;
+        
+		err = RegEnumKeyExA( key, i, subKeyName, &dwSize, NULL, NULL, NULL, NULL );
+
+		if ( !err )
+		{
+			err = RegOpenKeyExA( key, subKeyName, 0, KEY_READ, &subKey );
+			require_noerr( err, exit );
+
+			dwSize = sizeof( DWORD );
+			err = RegQueryValueExA( subKey, "Enabled", NULL, NULL, (LPBYTE) &enabled, &dwSize );
+
+			if ( !err && ( subKeyName[0] != '\0' ) && enabled )
+			{
+				if ( !MakeDomainNameFromDNSNameString( &dname, subKeyName ) || !dname.c[0] )
+				{
+					dlog( kDebugLevelError, "bad DDNS domain in registry: %s", subKeyName[0] ? subKeyName : "(unknown)");
+				}
+				else
+				{
+					DNameListElem * domain = (DNameListElem*) malloc( sizeof( DNameListElem ) );
+					require_action( domain, exit, err = mStatus_NoMemoryErr );
+					
+					AssignDomainName(&domain->name, &dname);
+					domain->next = *domains;
+
+					*domains = domain;
+				}
+			}
+
+			RegCloseKey( subKey );
+			subKey = NULL;
+		}
+	}
+
+exit:
+
+	if ( subKey )
+	{
+		RegCloseKey( subKey );
+	}
+
+	if ( key )
+	{
+		RegCloseKey( key );
+	}
+}
+
+
+mDNSlocal void SetDomainSecret( mDNS * const m, const domainname * inDomain )
+{
+	char					domainUTF8[ 256 ];
+	DomainAuthInfo			*foundInList;
+	DomainAuthInfo			*ptr;
+	char					outDomain[ 256 ];
+	char					outKey[ 256 ];
+	char					outSecret[ 256 ];
+	OSStatus				err;
+	
+	ConvertDomainNameToCString( inDomain, domainUTF8 );
+	
+	// If we're able to find a secret for this domain
+
+	if ( LsaGetSecret( domainUTF8, outDomain, sizeof( outDomain ), outKey, sizeof( outKey ), outSecret, sizeof( outSecret ) ) )
+	{
+		domainname domain;
+		domainname key;
+
+		// Tell the core about this secret
+
+		MakeDomainNameFromDNSNameString( &domain, outDomain );
+		MakeDomainNameFromDNSNameString( &key, outKey );
+
+		for (foundInList = m->AuthInfoList; foundInList; foundInList = foundInList->next)
+			if (SameDomainName(&foundInList->domain, &domain ) ) break;
+
+		ptr = foundInList;
+	
+		if (!ptr)
+		{
+			ptr = (DomainAuthInfo*)malloc(sizeof(DomainAuthInfo));
+			require_action( ptr, exit, err = mStatus_NoMemoryErr );
+		}
+
+		err = mDNS_SetSecretForDomain(m, ptr, &domain, &key, outSecret, NULL, 0, NULL );
+		require_action( err != mStatus_BadParamErr, exit, if (!foundInList ) mDNSPlatformMemFree( ptr ) );
+
+		debugf("Setting shared secret for zone %s with key %##s", outDomain, key.c);
+	}
+
+exit:
+
+	return;
+}
+
+
+mDNSlocal VOID CALLBACK
+CheckFileSharesProc( LPVOID arg, DWORD dwTimerLowValue, DWORD dwTimerHighValue )
+{
+	mDNS * const m = ( mDNS * const ) arg;
+
+	( void ) dwTimerLowValue;
+	( void ) dwTimerHighValue;
+
+	CheckFileShares( m );
+}
+
+
+mDNSlocal unsigned __stdcall 
+SMBRegistrationThread( void * arg )
+{
+	mDNS * const m = ( mDNS * const ) arg;
+	DNSServiceRef sref = NULL;
+	HANDLE		handles[ 3 ];
+	mDNSu8		txtBuf[ 256 ];
+	mDNSu8	*	txtPtr;
+	size_t		keyLen;
+	size_t		valLen;
+	mDNSIPPort	port = { { SMBPortAsNumber >> 8, SMBPortAsNumber & 0xFF } };
+	DNSServiceErrorType err;
+
+	DEBUG_UNUSED( arg );
+
+	handles[ 0 ] = gSMBThreadStopEvent;
+	handles[ 1 ] = gSMBThreadRegisterEvent;
+	handles[ 2 ] = gSMBThreadDeregisterEvent;
+
+	memset( txtBuf, 0, sizeof( txtBuf )  );
+	txtPtr = txtBuf;
+	keyLen = strlen( "netbios=" );
+	valLen = strlen( m->p->nbname );
+	require_action( valLen < 32, exit, err = kUnknownErr );	// This should never happen, but check to avoid further memory corruption
+	*txtPtr++ = ( mDNSu8 ) ( keyLen + valLen );
+	memcpy( txtPtr, "netbios=", keyLen );
+	txtPtr += keyLen;
+	if ( valLen ) { memcpy( txtPtr, m->p->nbname, valLen ); txtPtr += ( mDNSu8 ) valLen; }
+	keyLen = strlen( "domain=" );
+	valLen = strlen( m->p->nbdomain );
+	require_action( valLen < 32, exit, err = kUnknownErr );	// This should never happen, but check to avoid further memory corruption
+	*txtPtr++ = ( mDNSu8 )( keyLen + valLen );
+	memcpy( txtPtr, "domain=", keyLen );
+	txtPtr += keyLen;
+	if ( valLen ) { memcpy( txtPtr, m->p->nbdomain, valLen ); txtPtr += valLen; }
+	
+	for ( ;; )
+	{
+		DWORD ret;
+
+		ret = WaitForMultipleObjects( 3, handles, FALSE, INFINITE );
+
+		if ( ret != WAIT_FAILED )
+		{
+			if ( ret == kSMBStopEvent )
+			{
+				break;
+			}
+			else if ( ret == kSMBRegisterEvent )
+			{
+				err = gDNSServiceRegister( &sref, 0, 0, NULL, "_smb._tcp,_file", NULL, NULL, ( uint16_t ) port.NotAnInteger, ( mDNSu16 )( txtPtr - txtBuf ), txtBuf, NULL, NULL );
+
+				if ( err )
+				{
+					LogMsg( "SMBRegistrationThread: DNSServiceRegister returned %d\n", err );
+					sref = NULL;
+					break;
+				}
+			}
+			else if ( ret == kSMBDeregisterEvent )
+			{
+				if ( sref )
+				{
+					gDNSServiceRefDeallocate( sref );
+					sref = NULL;
+				}
+			}
+		}
+		else
+		{
+			LogMsg( "SMBRegistrationThread:  WaitForMultipleObjects returned %d\n", GetLastError() );
+			break;
+		}
+	}
+
+exit:
+
+	if ( sref != NULL )
+	{
+		gDNSServiceRefDeallocate( sref );
+		sref = NULL;
+	}
+
+	SetEvent( gSMBThreadQuitEvent );
+	_endthreadex( 0 );
+	return 0;
+}
+
+
+mDNSlocal void
+CheckFileShares( mDNS * const m )
+{
+	PSHARE_INFO_1	bufPtr = ( PSHARE_INFO_1 ) NULL;
+	DWORD			entriesRead = 0;
+	DWORD			totalEntries = 0;
+	DWORD			resume = 0;
+	mDNSBool		advertise = mDNSfalse;
+	mDNSBool		fileSharing = mDNSfalse;
+	mDNSBool		printSharing = mDNSfalse;
+	HKEY			key = NULL;
+	BOOL			retry = FALSE;
+	NET_API_STATUS  res;
+	mStatus			err;
+
+	check( m );
+
+	// Only do this if we're not shutting down
+
+	require_action_quiet( m->AdvertiseLocalAddresses && !m->ShutdownTime, exit, err = kNoErr );
+
+	err = RegCreateKey( HKEY_LOCAL_MACHINE, kServiceParametersNode L"\\Services\\SMB", &key );
+
+	if ( !err )
+	{
+		DWORD dwSize = sizeof( DWORD );
+		RegQueryValueEx( key, L"Advertise", NULL, NULL, (LPBYTE) &advertise, &dwSize );
+	}
+
+	if ( advertise && mDNSIsFileAndPrintSharingEnabled( &retry ) )
+	{
+		dlog( kDebugLevelTrace, DEBUG_NAME "Sharing is enabled\n" );
+
+		res = NetShareEnum( NULL, 1, ( LPBYTE* )&bufPtr, MAX_PREFERRED_LENGTH, &entriesRead, &totalEntries, &resume );
+
+		if ( ( res == ERROR_SUCCESS ) || ( res == ERROR_MORE_DATA ) )
+		{
+			PSHARE_INFO_1 p = bufPtr;
+			DWORD i;
+
+			for( i = 0; i < entriesRead; i++ ) 
+			{
+				// We are only interested if the user is sharing anything other 
+				// than the built-in "print$" source
+
+				if ( ( p->shi1_type == STYPE_DISKTREE ) && ( wcscmp( p->shi1_netname, TEXT( "print$" ) ) != 0 ) )
+				{
+					fileSharing = mDNStrue;
+				}
+				else if ( p->shi1_type == STYPE_PRINTQ )
+				{
+					printSharing = mDNStrue;
+				}
+
+				p++;
+			}
+
+			NetApiBufferFree( bufPtr );
+			bufPtr = NULL;
+			retry = FALSE;
+		}
+		else if ( res == NERR_ServerNotStarted )
+		{
+			retry = TRUE;
+		}
+	}
+	
+	if ( retry )
+	{
+		__int64			qwTimeout;
+		LARGE_INTEGER   liTimeout;
+
+		qwTimeout = -m->p->checkFileSharesTimeout * 10000000;
+		liTimeout.LowPart  = ( DWORD )( qwTimeout & 0xFFFFFFFF );
+		liTimeout.HighPart = ( LONG )( qwTimeout >> 32 );
+
+		SetWaitableTimer( m->p->checkFileSharesTimer, &liTimeout, 0, CheckFileSharesProc, m, FALSE );
+	}
+
+	if ( !m->p->smbFileSharing && fileSharing )
+	{
+		if ( !gSMBThread )
+		{
+			if ( !gDNSSDLibrary )
+			{
+				gDNSSDLibrary = LoadLibrary( TEXT( "dnssd.dll" ) );
+				require_action( gDNSSDLibrary, exit, err = GetLastError() );
+			}
+
+			if ( !gDNSServiceRegister )
+			{
+				gDNSServiceRegister = ( DNSServiceRegisterFunc ) GetProcAddress( gDNSSDLibrary, "DNSServiceRegister" );
+				require_action( gDNSServiceRegister, exit, err = GetLastError() );
+			}
+
+			if ( !gDNSServiceRefDeallocate )
+			{
+				gDNSServiceRefDeallocate = ( DNSServiceRefDeallocateFunc ) GetProcAddress( gDNSSDLibrary, "DNSServiceRefDeallocate" );
+				require_action( gDNSServiceRefDeallocate, exit, err = GetLastError() );
+			}
+
+			if ( !gSMBThreadRegisterEvent )
+			{
+				gSMBThreadRegisterEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
+				require_action( gSMBThreadRegisterEvent != NULL, exit, err = GetLastError() );
+			}
+
+			if ( !gSMBThreadDeregisterEvent )
+			{
+				gSMBThreadDeregisterEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
+				require_action( gSMBThreadDeregisterEvent != NULL, exit, err = GetLastError() );
+			}
+
+			if ( !gSMBThreadStopEvent )
+			{
+				gSMBThreadStopEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
+				require_action( gSMBThreadStopEvent != NULL, exit, err = GetLastError() );
+			}
+
+			if ( !gSMBThreadQuitEvent )
+			{
+				gSMBThreadQuitEvent = CreateEvent( NULL, FALSE, FALSE, NULL );
+				require_action( gSMBThreadQuitEvent != NULL, exit, err = GetLastError() );
+			}
+
+			gSMBThread = ( HANDLE ) _beginthreadex( NULL, 0, SMBRegistrationThread, m, 0, NULL );
+			require_action( gSMBThread != NULL, exit, err = GetLastError() );
+		}
+
+		SetEvent( gSMBThreadRegisterEvent );
+
+		m->p->smbFileSharing = mDNStrue;
+	}
+	else if ( m->p->smbFileSharing && !fileSharing )
+	{
+		dlog( kDebugLevelTrace, DEBUG_NAME "deregistering smb type\n" );
+
+		if ( gSMBThreadDeregisterEvent != NULL )
+		{
+			SetEvent( gSMBThreadDeregisterEvent );
+		}
+
+		m->p->smbFileSharing = mDNSfalse;
+	}
+
+exit:
+
+	if ( key )
+	{
+		RegCloseKey( key );
+	}
+}
+
+
+BOOL
+IsWOMPEnabled( mDNS * const m )
+{
+	BOOL enabled;
+
+	mDNSInterfaceData * ifd;
+
+	enabled = FALSE;
+
+	for( ifd = m->p->interfaceList; ifd; ifd = ifd->next )
+	{
+		if ( IsWOMPEnabledForAdapter( ifd->name ) )
+		{
+			enabled = TRUE;
+			break;
+		}
+	}
+
+	return enabled;
+}
+
+
+mDNSlocal mDNSu8
+IsWOMPEnabledForAdapter( const char * adapterName )
+{
+	char						fileName[80];
+	NDIS_OID					oid;
+    DWORD						count;
+    HANDLE						handle	= INVALID_HANDLE_VALUE;
+	NDIS_PNP_CAPABILITIES	*	pNPC	= NULL;
+	int							err;
+	mDNSu8						ok		= TRUE;
+
+	require_action( adapterName != NULL, exit, ok = FALSE );
+
+	dlog( kDebugLevelTrace, DEBUG_NAME "IsWOMPEnabledForAdapter: %s\n", adapterName );
+	
+    // Construct a device name to pass to CreateFile
+
+	strncpy_s( fileName, sizeof( fileName ), DEVICE_PREFIX, strlen( DEVICE_PREFIX ) );
+	strcat_s( fileName, sizeof( fileName ), adapterName );
+    handle = CreateFileA( fileName, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, INVALID_HANDLE_VALUE );
+	require_action ( handle != INVALID_HANDLE_VALUE, exit, ok = FALSE );
+
+	// We successfully opened the driver, format the IOCTL to pass the driver.
+		
+	oid = OID_PNP_CAPABILITIES;
+	pNPC = ( NDIS_PNP_CAPABILITIES * ) malloc( sizeof( NDIS_PNP_CAPABILITIES ) );
+	require_action( pNPC != NULL, exit, ok = FALSE );
+	ok = ( mDNSu8 ) DeviceIoControl( handle, IOCTL_NDIS_QUERY_GLOBAL_STATS, &oid, sizeof( oid ), pNPC, sizeof( NDIS_PNP_CAPABILITIES ), &count, NULL );
+	err = translate_errno( ok, GetLastError(), kUnknownErr );
+	require_action( !err, exit, ok = FALSE );
+	ok = ( mDNSu8 ) ( ( count == sizeof( NDIS_PNP_CAPABILITIES ) ) && ( pNPC->Flags & NDIS_DEVICE_WAKE_ON_MAGIC_PACKET_ENABLE ) );
+       
+exit:
+
+	if ( pNPC != NULL )
+	{
+		free( pNPC );
+	}
+
+    if ( handle != INVALID_HANDLE_VALUE )
+    {
+		CloseHandle( handle );
+    }
+
+	dlog( kDebugLevelTrace, DEBUG_NAME "IsWOMPEnabledForAdapter returns %s\n", ok ? "true" : "false" );
+
+	return ( mDNSu8 ) ok;
+}
+
+
+void
+DispatchSocketEvents( mDNS * const inMDNS )
+{
+	UDPSocket * udpSock;
+	TCPSocket * tcpSock;
+
+	while ( ( udpSock = ( UDPSocket* ) gUDPDispatchableSockets.Head ) != NULL )
+	{
+		dlog( kDebugLevelChatty, DEBUG_NAME "%s: calling DispatchUDPEvent on socket %d, error = %d, bytesTransferred = %d\n",
+		                                     __ROUTINE__, udpSock->fd, udpSock->overlapped.error, udpSock->overlapped.bytesTransferred );
+		RemoveFromList( &gUDPDispatchableSockets, udpSock );
+		DispatchUDPEvent( inMDNS, udpSock );
+	}
+		
+	while ( ( tcpSock = ( TCPSocket* ) gTCPDispatchableSockets.Head ) != NULL )
+	{
+		dlog( kDebugLevelChatty, DEBUG_NAME "%s: calling DispatchTCPEvent on socket %d, error = %d, bytesTransferred = %d\n",
+		                                     __ROUTINE__, tcpSock->fd, tcpSock->overlapped.error, tcpSock->overlapped.bytesTransferred );
+		RemoveFromList( &gTCPDispatchableSockets, tcpSock );
+		DispatchTCPEvent( inMDNS, tcpSock );
+	}
+}
+
+
+mDNSlocal void
+DispatchUDPEvent( mDNS * const inMDNS, UDPSocket * sock )
+{
+	( void ) inMDNS;
+
+	// If we've closed the socket, then we want to ignore
+	// this read.  The packet might have been queued before
+	// the socket was closed.
+
+	if ( sock->fd != INVALID_SOCKET )
+	{
+		const mDNSInterfaceID	iid = sock->ifd ? sock->ifd->interfaceInfo.InterfaceID : NULL;
+		mDNSu8				*	end = ( (mDNSu8 *) &sock->packet ) + sock->overlapped.bytesTransferred;
+
+		dlog( kDebugLevelChatty, DEBUG_NAME "calling mDNSCoreReceive on socket: %d\n", sock->fd );
+		mDNSCoreReceive( sock->m, &sock->packet, end, &sock->overlapped.srcAddr, sock->overlapped.srcPort, &sock->overlapped.dstAddr, sock->overlapped.dstPort, iid );
+	}
+
+	// If the socket is still good, then start up another asynchronous read
+
+	if ( sock->fd != INVALID_SOCKET )
+	{
+		int err = UDPBeginRecv( sock );
+		check_noerr( err );
+	}
+}
+
+
+mDNSlocal void
+DispatchTCPEvent( mDNS * const inMDNS, TCPSocket * sock )
+{
+	( void ) inMDNS;
+
+	if ( sock->fd != INVALID_SOCKET )
+	{
+		sock->eptr += sock->overlapped.bytesTransferred;
+		sock->lastError = sock->overlapped.error;
+
+		if ( !sock->overlapped.error && !sock->overlapped.bytesTransferred )
+		{
+			sock->closed = TRUE;
+		}
+
+		if ( sock->readEventHandler != NULL )
+		{
+			dlog( kDebugLevelChatty, DEBUG_NAME "calling TCP read handler  on socket: %d\n", sock->fd );
+			sock->readEventHandler( sock );
+		}
+	}
+
+	// If the socket is still good, then start up another asynchronous read
+
+	if ( !sock->closed && ( sock->fd != INVALID_SOCKET ) )
+	{
+		int err = TCPBeginRecv( sock );
+		check_noerr( err );
+	}
+}
diff --git a/mdnsresponder/mDNSWindows/mDNSWin32.h b/mdnsresponder/mDNSWindows/mDNSWin32.h
new file mode 100755
index 0000000..e08c462
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/mDNSWin32.h
@@ -0,0 +1,208 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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.
+ */
+
+#ifndef	__MDNS_WIN32__
+#define	__MDNS_WIN32__
+
+#include	"CommonServices.h"
+
+#if( !defined( _WIN32_WCE ) )
+	#include	<mswsock.h>
+#endif
+
+#include	"mDNSEmbeddedAPI.h"
+#include	"uDNS.h"
+
+#ifdef	__cplusplus
+	extern "C" {
+#endif
+
+
+typedef struct Overlapped
+{
+	BOOL		pending;
+	OVERLAPPED	data;
+	WSABUF		wbuf;
+	DWORD		error;
+	DWORD		bytesTransferred;
+	mDNSAddr	srcAddr;
+	mDNSIPPort	srcPort;
+	mDNSAddr	dstAddr;
+	mDNSIPPort	dstPort;
+} Overlapped;
+
+
+typedef void ( *TCPReadEventHandler )( TCPSocket * sock );
+typedef void ( *TCPUserCallback )();
+
+struct TCPSocket_struct
+{
+	TCPSocketFlags				flags;		// MUST BE FIRST FIELD -- mDNSCore expects every TCPSocket_struct to begin with TCPSocketFlags flags
+	SOCKET						fd;
+	TCPReadEventHandler			readEventHandler;
+	HANDLE						connectEvent;
+	BOOL						connected;
+	TCPUserCallback				userCallback;
+	void					*	userContext;
+	Overlapped					overlapped;
+	DWORD						lastError;
+	BOOL						closed;

+	uint8_t						bbuf[ 4192 ];
+	uint8_t					*	bptr;
+	uint8_t					*	eptr;
+	uint8_t					*	ebuf;
+	TCPSocket				*	nextDispatchable;
+	mDNS					*	m;
+};
+
+
+struct UDPSocket_struct
+{
+	mDNSIPPort						port; 			// MUST BE FIRST FIELD -- mDNSCoreReceive expects every UDPSocket_struct to begin with mDNSIPPort port
+	mDNSAddr						addr;			// This is initialized by our code. If we don't get the 
+													// dstAddr from WSARecvMsg we use this value instead.
+	SOCKET							fd;
+	LPFN_WSARECVMSG					recvMsgPtr;
+	Overlapped						overlapped;
+	WSAMSG							wmsg;
+	DNSMessage						packet;
+	uint8_t							controlBuffer[ 128 ];
+	struct sockaddr_storage			srcAddr;		// This is filled in by the WSARecv* function
+	INT								srcAddrLen;		// See above
+	struct mDNSInterfaceData	*	ifd;
+	UDPSocket					*	nextDispatchable;
+	UDPSocket					*	next;
+	mDNS						*	m;
+};
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@struct		mDNSInterfaceData
+
+	@abstract	Structure containing interface-specific data.
+*/
+
+typedef struct	mDNSInterfaceData	mDNSInterfaceData;
+struct	mDNSInterfaceData
+{
+	char						name[ 128 ];
+	uint32_t					index;
+	uint32_t					scopeID;
+	struct UDPSocket_struct		sock;
+	NetworkInterfaceInfo		interfaceInfo;
+	mDNSBool					hostRegistered;
+	mDNSInterfaceData		*	next;
+};
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@typedef	RegisterWaitableEventHandler
+*/
+typedef void		(*RegisterWaitableEventHandler)(mDNS * const inMDNS, HANDLE event, void * context );
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@typedef	RegisterWaitableEventFunc
+*/
+typedef mStatus		(*RegisterWaitableEventFunc)(mDNS * const inMDNS, HANDLE event, void * context, RegisterWaitableEventHandler handler );
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@typedef	UnregisterWaitableEventHandler
+*/
+typedef void		(*UnregisterWaitableEventFunc)(mDNS * const inMDNS, HANDLE event );
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@typedef	ReportStatusFunc
+*/
+typedef void		(*ReportStatusFunc)(int inType, const char *inFormat, ...);
+
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@struct		mDNS_PlatformSupport_struct
+
+	@abstract	Structure containing platform-specific data.
+*/
+
+struct	mDNS_PlatformSupport_struct
+{
+	HANDLE						mainThread;
+	HANDLE						checkFileSharesTimer;
+	mDNSs32						checkFileSharesTimeout;
+	RegisterWaitableEventFunc	registerWaitableEventFunc;
+	UnregisterWaitableEventFunc	unregisterWaitableEventFunc;
+	ReportStatusFunc			reportStatusFunc;
+	time_t						nextDHCPLeaseExpires;
+	char						nbname[ 32 ];
+	char						nbdomain[ 32 ];
+	mDNSBool					smbFileSharing;
+	mDNSBool					smbPrintSharing;
+	ServiceRecordSet			smbSRS;
+	AuthRecord					smbSubTypes[ 2 ];
+	mDNSBool					registeredLoopback4;
+	int							interfaceCount;
+	mDNSInterfaceData *			interfaceList;
+	mDNSInterfaceData *			inactiveInterfaceList;
+	struct UDPSocket_struct		unicastSock4;
+	struct UDPSocket_struct		unicastSock6;
+};
+
+//---------------------------------------------------------------------------------------------------------------------------
+/*!	@struct		ifaddrs
+
+	@abstract	Interface information
+*/
+
+struct ifaddrs
+{
+	struct ifaddrs *	ifa_next;
+	char *				ifa_name;
+	u_int				ifa_flags;
+	struct sockaddr	*	ifa_addr;
+	struct sockaddr	*	ifa_netmask;
+	struct sockaddr	*	ifa_broadaddr;
+	struct sockaddr	*	ifa_dstaddr;
+	BYTE				ifa_physaddr[6];
+	BOOL				ifa_dhcpEnabled;
+	time_t				ifa_dhcpLeaseExpires;
+	mDNSu8				ifa_womp;
+	void *				ifa_data;
+	
+	struct
+	{
+		uint32_t		index;
+	
+	}	ifa_extra;
+};
+
+

+extern void		InterfaceListDidChange( mDNS * const inMDNS );
+extern void		ComputerDescriptionDidChange( mDNS * const inMDNS );
+extern void		TCPIPConfigDidChange( mDNS * const inMDNS );
+extern void		DynDNSConfigDidChange( mDNS * const inMDNS );
+extern void		FileSharingDidChange( mDNS * const inMDNS );
+extern void		FirewallDidChange( mDNS * const inMDNS );
+extern mStatus  TCPAddSocket( mDNS * const inMDNS, TCPSocket *sock );
+extern mStatus	SetupInterfaceList( mDNS * const inMDNS );
+extern mStatus	TearDownInterfaceList( mDNS * const inMDNS );
+extern BOOL		IsWOMPEnabled();
+extern void     DispatchSocketEvents( mDNS * const inMDNS );
+
+
+#ifdef	__cplusplus
+	}
+#endif
+
+#endif	// __MDNS_WIN32__
diff --git a/mdnsresponder/mDNSWindows/mdnsNSP/ReadMe.txt b/mdnsresponder/mDNSWindows/mdnsNSP/ReadMe.txt
new file mode 100644
index 0000000..7a6e2cc
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/mdnsNSP/ReadMe.txt
@@ -0,0 +1,15 @@
+The mdnsNSP is a NameSpace Provider. It hooks into the Windows name resolution infrastructure to allow any software using the standard Windows APIs for resolving names to work with DNS-SD. For example, when the mdnsNSP is installed, you can type "http://computer.local./" in Internet Explorer and it will resolve "computer.local." using DNS-SD and go to the web site (assuming you have a computer named "computer" on the local network and advertised via DNS-SD).
+
+NSP's are implemented DLLs and must be installed to work. NSP DLLs export an NSPStartup function, which is called when the NSP is used, and NSPStartup provides information about itself (e.g. version number, compatibility information, and a list of function pointers for each of the supported NSP routines).
+
+If you need to register the mdnsNSP, you can use the NSPTool (sources for it are provided along with the mdnsNSP) with the following line from the DOS command line prompt ("<path>" is the actual parent path of the DLL):
+
+NSPTool -install "mdnsNSP" "B600E6E9-553B-4a19-8696-335E5C896153" "<path>"
+
+You can remove remove the mdnsNSP with the following line:
+
+NSPTool -remove "B600E6E9-553B-4a19-8696-335E5C896153"
+
+For more information, check out the following URL:
+
+<http://msdn.microsoft.com/library/default.asp?url=/library/en-us/winsock/winsock/name_space_service_providers_2.asp>
diff --git a/mdnsresponder/mDNSWindows/mdnsNSP/mdnsNSP.aps b/mdnsresponder/mDNSWindows/mdnsNSP/mdnsNSP.aps
new file mode 100644
index 0000000..2e3b63a
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/mdnsNSP/mdnsNSP.aps
Binary files differ
diff --git a/mdnsresponder/mDNSWindows/mdnsNSP/mdnsNSP.c b/mdnsresponder/mDNSWindows/mdnsNSP/mdnsNSP.c
new file mode 100644
index 0000000..2cd01ef
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/mdnsNSP/mdnsNSP.c
@@ -0,0 +1,2430 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved.
+ *
+ * Licensed 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	<stdio.h>
+#include	<stdlib.h>
+#include	<string.h>
+
+#include	"ClientCommon.h"
+#include	"CommonServices.h"
+#include	"DebugServices.h"
+
+#include	<iphlpapi.h>
+#include	<guiddef.h>
+#include	<ws2spi.h>
+#include	<shlwapi.h>
+
+
+
+#include	"dns_sd.h"
+
+#pragma comment(lib, "DelayImp.lib")
+
+#ifdef _MSC_VER
+#define swprintf _snwprintf
+#define snprintf _snprintf
+#endif
+
+#define MAX_LABELS 128
+
+#if 0
+#pragma mark == Structures ==
+#endif
+
+//===========================================================================================================================
+//	Structures
+//===========================================================================================================================
+
+typedef struct	Query *		QueryRef;
+typedef struct	Query		Query;
+struct	Query
+{
+	QueryRef			next;
+	int					refCount;
+	DWORD				querySetFlags;
+	WSAQUERYSETW *		querySet;
+	size_t				querySetSize;
+	HANDLE				data4Event;
+	HANDLE				data6Event;
+	HANDLE				cancelEvent;
+	HANDLE				waitHandles[ 3 ];
+	DWORD				waitCount;
+	DNSServiceRef		resolver4;
+	DNSServiceRef		resolver6;
+	char				name[ kDNSServiceMaxDomainName ];
+	size_t				nameSize;
+	uint8_t				numValidAddrs;
+	uint32_t			addr4;
+	bool				addr4Valid;
+	uint8_t				addr6[16];
+	u_long				addr6ScopeId;
+	bool				addr6Valid;
+};
+
+#define BUFFER_INITIAL_SIZE		4192
+#define ALIASES_INITIAL_SIZE	5
+
+typedef struct HostsFile
+{
+	int			m_bufferSize;
+	char	*	m_buffer;
+	FILE	*	m_fp;
+} HostsFile;
+
+
+typedef struct HostsFileInfo
+{
+	struct hostent		m_host;
+	struct HostsFileInfo	*	m_next;
+} HostsFileInfo;
+
+
+#if 0
+#pragma mark == Prototypes ==
+#endif
+
+//===========================================================================================================================
+//	Prototypes
+//===========================================================================================================================
+
+// DLL Exports
+
+BOOL WINAPI		DllMain( HINSTANCE inInstance, DWORD inReason, LPVOID inReserved );
+STDAPI			DllRegisterServer( void );
+STDAPI			DllRegisterServer( void );
+
+	
+// NSP SPIs
+
+int	WSPAPI	NSPCleanup( LPGUID inProviderID );
+
+DEBUG_LOCAL int WSPAPI
+	NSPLookupServiceBegin(
+		LPGUID					inProviderID,
+		LPWSAQUERYSETW			inQuerySet,
+		LPWSASERVICECLASSINFOW	inServiceClassInfo,
+		DWORD					inFlags,   
+		LPHANDLE				outLookup );
+
+DEBUG_LOCAL int WSPAPI
+	NSPLookupServiceNext(  
+		HANDLE			inLookup,
+		DWORD			inFlags,
+		LPDWORD			ioBufferLength,
+		LPWSAQUERYSETW	outResults );
+
+DEBUG_LOCAL int WSPAPI	NSPLookupServiceEnd( HANDLE inLookup );
+
+DEBUG_LOCAL int WSPAPI
+	NSPSetService(
+		LPGUID					inProviderID,						
+		LPWSASERVICECLASSINFOW	inServiceClassInfo,   
+		LPWSAQUERYSETW			inRegInfo,				  
+		WSAESETSERVICEOP		inOperation,			   
+		DWORD					inFlags );
+
+DEBUG_LOCAL int WSPAPI	NSPInstallServiceClass( LPGUID inProviderID, LPWSASERVICECLASSINFOW inServiceClassInfo );
+DEBUG_LOCAL int WSPAPI	NSPRemoveServiceClass( LPGUID inProviderID, LPGUID inServiceClassID );
+DEBUG_LOCAL int WSPAPI	NSPGetServiceClassInfo(	LPGUID inProviderID, LPDWORD ioBufSize, LPWSASERVICECLASSINFOW ioServiceClassInfo );
+
+// Private
+
+#define	NSPLock()		EnterCriticalSection( &gLock );
+#define	NSPUnlock()		LeaveCriticalSection( &gLock );
+
+DEBUG_LOCAL OSStatus	QueryCreate( const WSAQUERYSETW *inQuerySet, DWORD inQuerySetFlags, QueryRef *outRef );
+DEBUG_LOCAL OSStatus	QueryRetain( QueryRef inRef );
+DEBUG_LOCAL OSStatus	QueryRelease( QueryRef inRef );
+
+DEBUG_LOCAL void CALLBACK_COMPAT
+	QueryRecordCallback4(
+		DNSServiceRef		inRef,
+		DNSServiceFlags		inFlags,
+		uint32_t			inInterfaceIndex,
+		DNSServiceErrorType	inErrorCode,
+		const char *		inName,    
+		uint16_t			inRRType,
+		uint16_t			inRRClass,
+		uint16_t			inRDataSize,
+		const void *		inRData,
+		uint32_t			inTTL,
+		void *				inContext );
+
+DEBUG_LOCAL void CALLBACK_COMPAT
+	QueryRecordCallback6(
+		DNSServiceRef		inRef,
+		DNSServiceFlags		inFlags,
+		uint32_t			inInterfaceIndex,
+		DNSServiceErrorType	inErrorCode,
+		const char *		inName,    
+		uint16_t			inRRType,
+		uint16_t			inRRClass,
+		uint16_t			inRDataSize,
+		const void *		inRData,
+		uint32_t			inTTL,
+		void *				inContext );
+
+DEBUG_LOCAL OSStatus
+	QueryCopyQuerySet( 
+		QueryRef 				inRef, 
+		const WSAQUERYSETW *	inQuerySet, 
+		DWORD 					inQuerySetFlags, 
+		WSAQUERYSETW **			outQuerySet, 
+		size_t *				outSize );
+
+DEBUG_LOCAL void
+	QueryCopyQuerySetTo( 
+		QueryRef 				inRef, 
+		const WSAQUERYSETW *	inQuerySet, 
+		DWORD 					inQuerySetFlags, 
+		WSAQUERYSETW *			outQuerySet );
+
+DEBUG_LOCAL size_t	QueryCopyQuerySetSize( QueryRef inRef, const WSAQUERYSETW *inQuerySet, DWORD inQuerySetFlags );
+
+#if( DEBUG )
+	void	DebugDumpQuerySet( DebugLevel inLevel, const WSAQUERYSETW *inQuerySet );
+	
+	#define	dlog_query_set( LEVEL, SET )		DebugDumpQuerySet( LEVEL, SET )
+#else
+	#define	dlog_query_set( LEVEL, SET )
+#endif
+
+DEBUG_LOCAL BOOL		InHostsTable( const char * name );
+DEBUG_LOCAL BOOL		IsLocalName( HostsFileInfo * node );
+DEBUG_LOCAL BOOL		IsSameName( HostsFileInfo * node, const char * name );
+DEBUG_LOCAL OSStatus	HostsFileOpen( HostsFile ** self, const char * fname );
+DEBUG_LOCAL OSStatus	HostsFileClose( HostsFile * self );
+DEBUG_LOCAL void		HostsFileInfoFree( HostsFileInfo * info );
+DEBUG_LOCAL OSStatus	HostsFileNext( HostsFile * self, HostsFileInfo ** hInfo );
+DEBUG_LOCAL DWORD		GetScopeId( DWORD ifIndex );
+
+#ifdef ENABLE_REVERSE_LOOKUP
+DEBUG_LOCAL OSStatus	IsReverseLookup( LPCWSTR name, size_t size );
+#endif
+
+
+#if 0
+#pragma mark == Globals ==
+#endif
+
+//===========================================================================================================================
+//	Globals
+//===========================================================================================================================
+
+// {B600E6E9-553B-4a19-8696-335E5C896153}
+DEBUG_LOCAL HINSTANCE				gInstance			= NULL;
+DEBUG_LOCAL wchar_t				*	gNSPName			= L"mdnsNSP";
+DEBUG_LOCAL GUID					gNSPGUID			= { 0xb600e6e9, 0x553b, 0x4a19, { 0x86, 0x96, 0x33, 0x5e, 0x5c, 0x89, 0x61, 0x53 } };
+DEBUG_LOCAL LONG					gRefCount			= 0;
+DEBUG_LOCAL CRITICAL_SECTION		gLock;
+DEBUG_LOCAL bool					gLockInitialized 	= false;
+DEBUG_LOCAL QueryRef				gQueryList	 		= NULL;
+DEBUG_LOCAL HostsFileInfo		*	gHostsFileInfo		= NULL;
+typedef DWORD
+	( WINAPI * GetAdaptersAddressesFunctionPtr )( 
+			ULONG 					inFamily, 
+			DWORD 					inFlags, 
+			PVOID 					inReserved, 
+			PIP_ADAPTER_ADDRESSES 	inAdapter, 
+			PULONG					outBufferSize );
+
+DEBUG_LOCAL HMODULE								gIPHelperLibraryInstance			= NULL;
+DEBUG_LOCAL GetAdaptersAddressesFunctionPtr		gGetAdaptersAddressesFunctionPtr	= NULL;
+
+
+
+#if 0
+#pragma mark -
+#endif
+
+//===========================================================================================================================
+//	DllMain
+//===========================================================================================================================
+
+BOOL APIENTRY	DllMain( HINSTANCE inInstance, DWORD inReason, LPVOID inReserved )
+{
+	DEBUG_USE_ONLY( inInstance );
+	DEBUG_UNUSED( inReserved );
+
+	switch( inReason )
+	{
+		case DLL_PROCESS_ATTACH:			
+			gInstance = inInstance;		
+			gHostsFileInfo	= NULL;
+			debug_initialize( kDebugOutputTypeWindowsEventLog, "mDNS NSP", inInstance );
+			debug_set_property( kDebugPropertyTagPrintLevel, kDebugLevelNotice );
+			dlog( kDebugLevelTrace, "\n" );
+			dlog( kDebugLevelVerbose, "%s: process attach\n", __ROUTINE__ );
+
+			break;
+		
+		case DLL_PROCESS_DETACH:
+			HostsFileInfoFree( gHostsFileInfo );
+			gHostsFileInfo = NULL;
+			dlog( kDebugLevelVerbose, "%s: process detach\n", __ROUTINE__ );
+			break;
+		
+		case DLL_THREAD_ATTACH:
+			dlog( kDebugLevelVerbose, "%s: thread attach\n", __ROUTINE__ );
+			break;
+		
+		case DLL_THREAD_DETACH:
+			dlog( kDebugLevelVerbose, "%s: thread detach\n", __ROUTINE__ );
+			break;
+		
+		default:
+			dlog( kDebugLevelNotice, "%s: unknown reason code (%d)\n", __ROUTINE__, inReason );
+			break;
+	}
+
+	return( TRUE );
+}
+
+
+//===========================================================================================================================
+//	DllRegisterServer
+//===========================================================================================================================
+
+STDAPI	DllRegisterServer( void )
+{
+	WSADATA		wsd;
+	WCHAR		path[ MAX_PATH ];
+	HRESULT		err;
+	
+	dlog( kDebugLevelTrace, "DllRegisterServer\n" );
+
+	err = WSAStartup( MAKEWORD( 2, 2 ), &wsd );
+	err = translate_errno( err == 0, errno_compat(), WSAEINVAL );
+	require_noerr( err, exit );
+
+	// Unregister before registering to workaround an installer
+	// problem during upgrade installs.
+
+	WSCUnInstallNameSpace( &gNSPGUID );
+
+	err = GetModuleFileNameW( gInstance, path, MAX_PATH );
+	err = translate_errno( err != 0, errno_compat(), kUnknownErr );
+	require_noerr( err, exit );
+
+	err = WSCInstallNameSpace( gNSPName, path, NS_DNS, 1, &gNSPGUID );
+	err = translate_errno( err == 0, errno_compat(), WSAEINVAL );
+	require_noerr( err, exit );
+	
+exit:
+
+	WSACleanup();
+	return( err );
+}
+
+//===========================================================================================================================
+//	DllUnregisterServer
+//===========================================================================================================================
+
+STDAPI	DllUnregisterServer( void )
+{
+	WSADATA		wsd;
+	HRESULT err;
+	
+	dlog( kDebugLevelTrace, "DllUnregisterServer\n" );
+	
+	err = WSAStartup( MAKEWORD( 2, 2 ), &wsd );
+	err = translate_errno( err == 0, errno_compat(), WSAEINVAL );
+	require_noerr( err, exit );
+	
+	err = WSCUnInstallNameSpace( &gNSPGUID );
+	err = translate_errno( err == 0, errno_compat(), WSAEINVAL );
+	require_noerr( err, exit );
+		
+exit:
+
+	WSACleanup();
+	return err;
+}
+
+
+//===========================================================================================================================
+//	NSPStartup
+//
+//	This function is called when our namespace DLL is loaded. It sets up the NSP functions we implement and initializes us.
+//===========================================================================================================================
+
+int WSPAPI	NSPStartup( LPGUID inProviderID, LPNSP_ROUTINE outRoutines )
+{
+	OSStatus		err;
+	
+	dlog( kDebugLevelTrace, "%s begin (ticks=%d)\n", __ROUTINE__, GetTickCount() );
+	dlog( kDebugLevelTrace, "%s (GUID=%U, refCount=%ld)\n", __ROUTINE__, inProviderID, gRefCount );
+	
+	// Only initialize if this is the first time NSPStartup is called. 
+	
+	if( InterlockedIncrement( &gRefCount ) != 1 )
+	{
+		err = NO_ERROR;
+		goto exit;
+	}
+	
+	// Initialize our internal state.
+	
+	InitializeCriticalSection( &gLock );
+	gLockInitialized = true;
+	
+	// Set the size to exclude NSPIoctl because we don't implement it.
+	
+	outRoutines->cbSize					= FIELD_OFFSET( NSP_ROUTINE, NSPIoctl );
+	outRoutines->dwMajorVersion			= 4;
+	outRoutines->dwMinorVersion			= 4;
+	outRoutines->NSPCleanup				= NSPCleanup;
+	outRoutines->NSPLookupServiceBegin	= NSPLookupServiceBegin;
+	outRoutines->NSPLookupServiceNext	= NSPLookupServiceNext;
+	outRoutines->NSPLookupServiceEnd	= NSPLookupServiceEnd;
+	outRoutines->NSPSetService			= NSPSetService;
+	outRoutines->NSPInstallServiceClass	= NSPInstallServiceClass;
+	outRoutines->NSPRemoveServiceClass	= NSPRemoveServiceClass;
+	outRoutines->NSPGetServiceClassInfo	= NSPGetServiceClassInfo;
+	
+	// See if we can get the address for the GetAdaptersAddresses() API.  This is only in XP, but we want our
+	// code to run on older versions of Windows
+
+	if ( !gIPHelperLibraryInstance )
+	{
+		gIPHelperLibraryInstance = LoadLibrary( TEXT( "Iphlpapi" ) );
+		if( gIPHelperLibraryInstance )
+		{
+			gGetAdaptersAddressesFunctionPtr = (GetAdaptersAddressesFunctionPtr) GetProcAddress( gIPHelperLibraryInstance, "GetAdaptersAddresses" );
+		}
+	}
+
+	err = NO_ERROR;
+	
+exit:
+	dlog( kDebugLevelTrace, "%s end   (ticks=%d)\n", __ROUTINE__, GetTickCount() );
+	if( err != NO_ERROR )
+	{
+		NSPCleanup( inProviderID );
+		SetLastError( (DWORD) err );
+		return( SOCKET_ERROR );
+	}
+	return( NO_ERROR );
+}
+
+//===========================================================================================================================
+//	NSPCleanup
+//
+//	This function is called when our namespace DLL is unloaded. It cleans up anything we set up in NSPStartup.
+//===========================================================================================================================
+
+int	WSPAPI	NSPCleanup( LPGUID inProviderID )
+{
+	DEBUG_USE_ONLY( inProviderID );
+	
+	dlog( kDebugLevelTrace, "%s begin (ticks=%d)\n", __ROUTINE__, GetTickCount() );
+	dlog( kDebugLevelTrace, "%s (GUID=%U, refCount=%ld)\n", __ROUTINE__, inProviderID, gRefCount );
+	
+	// Only initialize if this is the first time NSPStartup is called.
+	
+	if( InterlockedDecrement( &gRefCount ) != 0 )
+	{
+		goto exit;
+	}
+	
+	// Stop any outstanding queries.
+	
+	if( gLockInitialized )
+	{
+		NSPLock();
+	}
+	while( gQueryList )
+	{
+		check_string( gQueryList->refCount == 1, "NSPCleanup with outstanding queries!" );
+		QueryRelease( gQueryList );
+	}
+	if( gLockInitialized )
+	{
+		NSPUnlock();
+	}
+	
+	if( gLockInitialized )
+	{
+		gLockInitialized = false;
+		DeleteCriticalSection( &gLock );
+	}
+
+	if( gIPHelperLibraryInstance )
+	{
+		BOOL ok;
+				
+		ok = FreeLibrary( gIPHelperLibraryInstance );
+		check_translated_errno( ok, GetLastError(), kUnknownErr );
+		gIPHelperLibraryInstance = NULL;
+	}
+	
+exit:
+	dlog( kDebugLevelTrace, "%s end   (ticks=%d)\n", __ROUTINE__, GetTickCount() );
+	return( NO_ERROR );
+}
+
+//===========================================================================================================================
+//	NSPLookupServiceBegin
+//
+//	This function maps to the WinSock WSALookupServiceBegin function. It starts the lookup process and returns a HANDLE 
+//	that can be used in subsequent operations. Subsequent calls only need to refer to this query by the handle as 
+//	opposed to specifying the query parameters each time.
+//===========================================================================================================================
+
+DEBUG_LOCAL int WSPAPI
+	NSPLookupServiceBegin(
+		LPGUID					inProviderID,
+		LPWSAQUERYSETW			inQuerySet,
+		LPWSASERVICECLASSINFOW	inServiceClassInfo,
+		DWORD					inFlags,   
+		LPHANDLE				outLookup )
+{
+	OSStatus		err;
+	QueryRef		obj;
+	LPCWSTR			name;
+	size_t			size;
+	LPCWSTR			p;
+	DWORD           type;
+	DWORD			n;
+	DWORD			i;
+	INT				family;
+	INT				protocol;
+	
+	DEBUG_UNUSED( inProviderID );
+	DEBUG_UNUSED( inServiceClassInfo );
+	
+	dlog( kDebugLevelTrace, "%s begin (ticks=%d)\n", __ROUTINE__, GetTickCount() );
+	
+	obj = NULL;
+	require_action( inQuerySet, exit, err = WSAEINVAL );
+	name = inQuerySet->lpszServiceInstanceName;
+	require_action_quiet( name, exit, err = WSAEINVAL );
+	require_action( outLookup, exit, err = WSAEINVAL );
+	
+	dlog( kDebugLevelTrace, "%s (flags=0x%08X, name=\"%S\")\n", __ROUTINE__, inFlags, name );
+	dlog_query_set( kDebugLevelVerbose, inQuerySet );
+	
+	// Check if we can handle this type of request and if we support any of the protocols being requested.
+	// We only support the DNS namespace, TCP and UDP protocols, and IPv4. Only blob results are supported.
+	
+	require_action_quiet( inFlags & (LUP_RETURN_ADDR|LUP_RETURN_BLOB), exit, err = WSASERVICE_NOT_FOUND );
+	
+	type = inQuerySet->dwNameSpace;
+	require_action_quiet( ( type == NS_DNS ) || ( type == NS_ALL ), exit, err = WSASERVICE_NOT_FOUND );
+	
+	n = inQuerySet->dwNumberOfProtocols;
+	if( n > 0 )
+	{
+		require_action( inQuerySet->lpafpProtocols, exit, err = WSAEINVAL );
+		for( i = 0; i < n; ++i )
+		{
+			family = inQuerySet->lpafpProtocols[ i ].iAddressFamily;
+			protocol = inQuerySet->lpafpProtocols[ i ].iProtocol;
+			if( ( family == AF_INET ) && ( ( protocol == IPPROTO_UDP ) || ( protocol == IPPROTO_TCP ) ) )
+			{
+				break;
+			}
+		}
+		require_action_quiet( i < n, exit, err = WSASERVICE_NOT_FOUND );
+	}
+	
+	// Check if the name ends in ".local" and if not, exit with an error since we only resolve .local names.
+	// The name may or may not end with a "." (fully qualified) so handle both cases. DNS is also case 
+	// insensitive the check for .local has to be case insensitive (.LoCaL is equivalent to .local). This
+	// manually does the wchar_t strlen and stricmp to avoid needing any special wchar_t versions of the 
+	// libraries. It is probably faster to do the inline compare than invoke functions to do it anyway.
+	
+	for( p = name; *p; ++p ) {}		// Find end of string
+	size = (size_t)( p - name );
+	require_action_quiet( size > sizeof_string( ".local" ), exit, err = WSASERVICE_NOT_FOUND );
+	
+	p = name + ( size - 1 );
+	p = ( *p == '.' ) ? ( p - sizeof_string( ".local" ) ) : ( ( p - sizeof_string( ".local" ) ) + 1 );
+	if	( ( ( p[ 0 ] != '.' )						||
+		( ( p[ 1 ] != 'L' ) && ( p[ 1 ] != 'l' ) )	||
+		( ( p[ 2 ] != 'O' ) && ( p[ 2 ] != 'o' ) )	||
+		( ( p[ 3 ] != 'C' ) && ( p[ 3 ] != 'c' ) )	||
+		( ( p[ 4 ] != 'A' ) && ( p[ 4 ] != 'a' ) )	||
+		( ( p[ 5 ] != 'L' ) && ( p[ 5 ] != 'l' ) ) ) )
+	{
+#ifdef ENABLE_REVERSE_LOOKUP
+
+		err = IsReverseLookup( name, size );
+
+#else
+
+		err = WSASERVICE_NOT_FOUND;
+
+#endif
+
+		require_noerr( err, exit );
+	}
+	else
+	{
+		const char	*	replyDomain;
+		char			translated[ kDNSServiceMaxDomainName ];
+		int				n;
+		int				labels		= 0;
+		const char	*	label[MAX_LABELS];
+		char			text[64];
+
+		n = WideCharToMultiByte( CP_UTF8, 0, name, -1, translated, sizeof( translated ), NULL, NULL );
+		require_action( n > 0, exit, err = WSASERVICE_NOT_FOUND );
+
+		// <rdar://problem/4050633>
+
+		// Don't resolve multi-label name
+
+		// <rdar://problem/5914160> Eliminate use of GetNextLabel in mdnsNSP
+		// Add checks for GetNextLabel returning NULL, individual labels being greater than
+		// 64 bytes, and the number of labels being greater than MAX_LABELS
+		replyDomain = translated;
+
+		while (replyDomain && *replyDomain && labels < MAX_LABELS)
+		{
+			label[labels++]	= replyDomain;
+			replyDomain		= GetNextLabel(replyDomain, text);
+		}
+
+		require_action( labels == 2, exit, err = WSASERVICE_NOT_FOUND );
+
+		// <rdar://problem/3936771>
+		//
+		// Check to see if the name of this host is in the hosts table. If so,
+		// don't try and resolve it
+		
+		require_action( InHostsTable( translated ) == FALSE, exit, err = WSASERVICE_NOT_FOUND );
+	}
+
+	// The name ends in .local ( and isn't in the hosts table ), .0.8.e.f.ip6.arpa, or .254.169.in-addr.arpa so start the resolve operation. Lazy initialize DNS-SD if needed.
+		
+	NSPLock();
+	
+	err = QueryCreate( inQuerySet, inFlags, &obj );
+	NSPUnlock();
+	require_noerr( err, exit );
+	
+	*outLookup = (HANDLE) obj;
+	
+exit:
+	dlog( kDebugLevelTrace, "%s end   (ticks=%d)\n", __ROUTINE__, GetTickCount() );
+	if( err != NO_ERROR )
+	{
+		SetLastError( (DWORD) err );
+		return( SOCKET_ERROR );
+	}
+	return( NO_ERROR );
+}
+
+//===========================================================================================================================
+//	NSPLookupServiceNext
+//
+//	This function maps to the Winsock call WSALookupServiceNext. This routine takes a handle to a previously defined 
+//	query and attempts to locate a service matching the criteria defined by the query. If so, that instance is returned 
+//	in the lpqsResults parameter.
+//===========================================================================================================================
+
+DEBUG_LOCAL int WSPAPI
+	NSPLookupServiceNext(  
+		HANDLE			inLookup,
+		DWORD			inFlags,
+		LPDWORD			ioSize,
+		LPWSAQUERYSETW	outResults )
+{
+	BOOL			data4;
+	BOOL			data6;
+	OSStatus		err;
+	QueryRef		obj;
+	DWORD			waitResult;
+	size_t			size;
+	
+	DEBUG_USE_ONLY( inFlags );
+	
+	dlog( kDebugLevelTrace, "%s begin (ticks=%d)\n", __ROUTINE__, GetTickCount() );
+	
+	data4 = FALSE;
+	data6 = FALSE;
+	obj = NULL;
+	NSPLock();
+	err = QueryRetain( (QueryRef) inLookup );
+	require_noerr( err, exit );
+	obj = (QueryRef) inLookup;
+	require_action( ioSize, exit, err = WSAEINVAL );
+	require_action( outResults, exit, err = WSAEINVAL );
+	
+	dlog( kDebugLevelTrace, "%s (lookup=%#p, flags=0x%08X, *ioSize=%d)\n", __ROUTINE__, inLookup, inFlags, *ioSize );
+	
+	// Wait for data or a cancel. Release the lock while waiting. This is safe because we've retained the query.
+
+	NSPUnlock();
+	waitResult = WaitForMultipleObjects( obj->waitCount, obj->waitHandles, FALSE, 2 * 1000 );
+	NSPLock();
+	require_action_quiet( waitResult != ( WAIT_OBJECT_0 ), exit, err = WSA_E_CANCELLED );
+	err = translate_errno( ( waitResult == WAIT_OBJECT_0 + 1 ) || ( waitResult == WAIT_OBJECT_0 + 2 ), (OSStatus) GetLastError(), WSASERVICE_NOT_FOUND );
+	require_noerr_quiet( err, exit );
+
+	// If we've received an IPv4 reply, then hang out briefly for an IPv6 reply
+
+	if ( waitResult == WAIT_OBJECT_0 + 1 )
+	{
+		data4 = TRUE;
+		data6 = WaitForSingleObject( obj->data6Event, 100 ) == WAIT_OBJECT_0 ? TRUE : FALSE;
+	}
+
+	// Else we've received an IPv6 reply, so hang out briefly for an IPv4 reply
+
+	else if ( waitResult == WAIT_OBJECT_0 + 2 )
+	{
+		data4 = WaitForSingleObject( obj->data4Event, 100 ) == WAIT_OBJECT_0 ? TRUE : FALSE;
+		data6 = TRUE;
+	}
+
+	if ( data4 )
+	{
+		__try
+		{
+			err = DNSServiceProcessResult(obj->resolver4);
+		}
+		__except( EXCEPTION_EXECUTE_HANDLER )
+		{
+			err = kUnknownErr;
+		}
+
+		require_noerr( err, exit );
+	}
+
+	if ( data6 )
+	{
+		__try
+		{
+			err = DNSServiceProcessResult( obj->resolver6 );
+		}
+		__except( EXCEPTION_EXECUTE_HANDLER )
+		{
+			err = kUnknownErr;
+		}
+
+		require_noerr( err, exit );
+	}
+
+	require_action_quiet( obj->addr4Valid || obj->addr6Valid, exit, err = WSA_E_NO_MORE );
+
+	// Copy the externalized query results to the callers buffer (if it fits).
+	
+	size = QueryCopyQuerySetSize( obj, obj->querySet, obj->querySetFlags );
+	require_action( size <= (size_t) *ioSize, exit, err = WSAEFAULT );
+	
+	QueryCopyQuerySetTo( obj, obj->querySet, obj->querySetFlags, outResults );
+	outResults->dwOutputFlags = RESULT_IS_ADDED;
+	obj->addr4Valid = false;
+	obj->addr6Valid = false;
+
+exit:
+	if( obj )
+	{
+		QueryRelease( obj );
+	}
+	NSPUnlock();
+	dlog( kDebugLevelTrace, "%s end   (ticks=%d)\n", __ROUTINE__, GetTickCount() );
+	if( err != NO_ERROR )
+	{
+		SetLastError( (DWORD) err );
+		return( SOCKET_ERROR );
+	}
+	return( NO_ERROR );
+}
+
+//===========================================================================================================================
+//	NSPLookupServiceEnd
+//
+//	This function maps to the Winsock call WSALookupServiceEnd. Once the user process has finished is query (usually 
+//	indicated when WSALookupServiceNext returns the error WSA_E_NO_MORE) a call to this function is made to release any 
+//	allocated resources associated with the query.
+//===========================================================================================================================
+
+DEBUG_LOCAL int WSPAPI	NSPLookupServiceEnd( HANDLE inLookup )
+{
+	OSStatus		err;
+
+	dlog( kDebugLevelTrace, "%s begin (ticks=%d)\n", __ROUTINE__, GetTickCount() );
+	
+	dlog( kDebugLevelTrace, "%s (lookup=%#p)\n", __ROUTINE__, inLookup );
+	
+	NSPLock();
+	err = QueryRelease( (QueryRef) inLookup );
+	NSPUnlock();
+	require_noerr( err, exit );
+	
+exit:
+	dlog( kDebugLevelTrace, "%s end   (ticks=%d)\n", __ROUTINE__, GetTickCount() );
+	if( err != NO_ERROR )
+	{
+		SetLastError( (DWORD) err );
+		return( SOCKET_ERROR );
+	}
+	return( NO_ERROR );
+}
+
+//===========================================================================================================================
+//	NSPSetService
+//
+//	This function maps to the Winsock call WSASetService. This routine is called when the user wants to register or 
+//	deregister an instance of a server with our service. For registration, the user needs to associate the server with a 
+//	service class. For deregistration the service class is required along with the servicename. The inRegInfo parameter 
+//	contains a WSAQUERYSET structure defining the server (such as protocol and address where it is).
+//===========================================================================================================================
+
+DEBUG_LOCAL int WSPAPI
+	NSPSetService(
+		LPGUID					inProviderID,						
+		LPWSASERVICECLASSINFOW	inServiceClassInfo,   
+		LPWSAQUERYSETW			inRegInfo,				  
+		WSAESETSERVICEOP		inOperation,			   
+		DWORD					inFlags )
+{
+	DEBUG_UNUSED( inProviderID );
+	DEBUG_UNUSED( inServiceClassInfo );
+	DEBUG_UNUSED( inRegInfo );
+	DEBUG_UNUSED( inOperation );
+	DEBUG_UNUSED( inFlags );
+	
+	dlog( kDebugLevelTrace, "%s begin (ticks=%d)\n", __ROUTINE__, GetTickCount() );
+	dlog( kDebugLevelTrace, "%s\n", __ROUTINE__ );
+	
+	// We don't allow services to be registered so always return an error.
+	
+	dlog( kDebugLevelTrace, "%s end   (ticks=%d)\n", __ROUTINE__, GetTickCount() );
+	return( WSAEINVAL );
+}
+
+//===========================================================================================================================
+//	NSPInstallServiceClass
+//
+//	This function maps to the Winsock call WSAInstallServiceClass. This routine is used to install a service class which 
+//	is used to define certain characteristics for a group of services. After a service class is registered, an actual
+//	instance of a server may be registered.
+//===========================================================================================================================
+
+DEBUG_LOCAL int WSPAPI	NSPInstallServiceClass( LPGUID inProviderID, LPWSASERVICECLASSINFOW inServiceClassInfo )
+{
+	DEBUG_UNUSED( inProviderID );
+	DEBUG_UNUSED( inServiceClassInfo );
+	
+	dlog( kDebugLevelTrace, "%s begin (ticks=%d)\n", __ROUTINE__, GetTickCount() );
+	dlog( kDebugLevelTrace, "%s\n", __ROUTINE__ );
+	
+	// We don't allow service classes to be installed so always return an error.
+
+	dlog( kDebugLevelTrace, "%s end   (ticks=%d)\n", __ROUTINE__, GetTickCount() );
+	return( WSA_INVALID_PARAMETER );
+}
+
+//===========================================================================================================================
+//	NSPRemoveServiceClass
+//
+//	This function maps to the Winsock call WSARemoveServiceClass. This routine removes a previously registered service 
+//	class. This is accomplished by connecting to the namespace service and writing the GUID which defines the given 
+//	service class.
+//===========================================================================================================================
+
+DEBUG_LOCAL int WSPAPI	NSPRemoveServiceClass( LPGUID inProviderID, LPGUID inServiceClassID )
+{
+	DEBUG_UNUSED( inProviderID );
+	DEBUG_UNUSED( inServiceClassID );
+	
+	dlog( kDebugLevelTrace, "%s begin (ticks=%d)\n", __ROUTINE__, GetTickCount() );
+	dlog( kDebugLevelTrace, "%s\n", __ROUTINE__ );
+	
+	// We don't allow service classes to be installed so always return an error.
+	
+	dlog( kDebugLevelTrace, "%s end   (ticks=%d)\n", __ROUTINE__, GetTickCount() );
+	return( WSATYPE_NOT_FOUND );
+}
+
+//===========================================================================================================================
+//	NSPGetServiceClassInfo
+//
+//	This function maps to the Winsock call WSAGetServiceClassInfo. This routine returns the information associated with 
+//	a given service class.
+//===========================================================================================================================
+
+DEBUG_LOCAL int WSPAPI	NSPGetServiceClassInfo(	LPGUID inProviderID, LPDWORD ioSize, LPWSASERVICECLASSINFOW ioServiceClassInfo )
+{
+	DEBUG_UNUSED( inProviderID );
+	DEBUG_UNUSED( ioSize );
+	DEBUG_UNUSED( ioServiceClassInfo );
+	
+	dlog( kDebugLevelTrace, "%s begin (ticks=%d)\n", __ROUTINE__, GetTickCount() );
+	dlog( kDebugLevelTrace, "%s\n", __ROUTINE__ );
+	
+	// We don't allow service classes to be installed so always return an error.
+	
+	dlog( kDebugLevelTrace, "%s end   (ticks=%d)\n", __ROUTINE__, GetTickCount() );
+	return( WSATYPE_NOT_FOUND );
+}
+
+#if 0
+#pragma mark -
+#endif
+
+//===========================================================================================================================
+//	QueryCreate
+//
+//	Warning: Assumes the NSP lock is held.
+//===========================================================================================================================
+
+DEBUG_LOCAL OSStatus	QueryCreate( const WSAQUERYSETW *inQuerySet, DWORD inQuerySetFlags, QueryRef *outRef )
+{
+	OSStatus		err;
+	QueryRef		obj;
+	char			name[ kDNSServiceMaxDomainName ];
+	int				n;
+	QueryRef *		p;
+	SOCKET			s4;
+	SOCKET			s6;
+
+	obj = NULL;
+	check( inQuerySet );
+	check( inQuerySet->lpszServiceInstanceName );
+	check( outRef );
+	
+	// Convert the wchar_t name to UTF-8.
+	
+	n = WideCharToMultiByte( CP_UTF8, 0, inQuerySet->lpszServiceInstanceName, -1, name, sizeof( name ), NULL, NULL );
+	err = translate_errno( n > 0, (OSStatus) GetLastError(), WSAEINVAL );
+	require_noerr( err, exit );
+	
+	// Allocate the object and append it to the list. Append immediately so releases of partial objects work.
+	
+	obj = (QueryRef) calloc( 1, sizeof( *obj ) );
+	require_action( obj, exit, err = WSA_NOT_ENOUGH_MEMORY );
+	
+	obj->refCount = 1;
+	
+	for( p = &gQueryList; *p; p = &( *p )->next ) {}	// Find the end of the list.
+	*p = obj;
+	
+	// Set up cancel event
+
+	obj->cancelEvent = CreateEvent( NULL, TRUE, FALSE, NULL );
+	require_action( obj->cancelEvent, exit, err = WSA_NOT_ENOUGH_MEMORY );
+
+	// Set up events to signal when A record data is ready
+	
+	obj->data4Event = CreateEvent( NULL, TRUE, FALSE, NULL );
+	require_action( obj->data4Event, exit, err = WSA_NOT_ENOUGH_MEMORY );
+	
+	// Start the query.  Handle delay loaded DLL errors.
+
+	__try
+	{
+		err = DNSServiceQueryRecord( &obj->resolver4, 0, 0, name, kDNSServiceType_A, kDNSServiceClass_IN, QueryRecordCallback4, obj );
+	}
+	__except( EXCEPTION_EXECUTE_HANDLER )
+	{
+		err = kUnknownErr;
+	}
+
+	require_noerr( err, exit );
+
+	// Attach the socket to the event
+
+	__try
+	{
+		s4 = DNSServiceRefSockFD(obj->resolver4);
+	}
+	__except( EXCEPTION_EXECUTE_HANDLER )
+	{
+		s4 = INVALID_SOCKET;
+	}
+
+	err = translate_errno( s4 != INVALID_SOCKET, errno_compat(), kUnknownErr );
+	require_noerr( err, exit );
+
+	WSAEventSelect(s4, obj->data4Event, FD_READ|FD_CLOSE);
+	
+	// Set up events to signal when AAAA record data is ready
+	
+	obj->data6Event = CreateEvent( NULL, TRUE, FALSE, NULL );
+	require_action( obj->data6Event, exit, err = WSA_NOT_ENOUGH_MEMORY );
+	
+	// Start the query.  Handle delay loaded DLL errors.
+
+	__try
+	{
+		err = DNSServiceQueryRecord( &obj->resolver6, 0, 0, name, kDNSServiceType_AAAA, kDNSServiceClass_IN, QueryRecordCallback6, obj );
+	}
+	__except( EXCEPTION_EXECUTE_HANDLER )
+	{
+		err = kUnknownErr;
+	}
+
+	require_noerr( err, exit );
+
+	// Attach the socket to the event
+
+	__try
+	{
+		s6 = DNSServiceRefSockFD(obj->resolver6);
+	}
+	__except( EXCEPTION_EXECUTE_HANDLER )
+	{
+		s6 = INVALID_SOCKET;
+	}
+
+	err = translate_errno( s6 != INVALID_SOCKET, errno_compat(), kUnknownErr );
+	require_noerr( err, exit );
+
+	WSAEventSelect(s6, obj->data6Event, FD_READ|FD_CLOSE);
+
+	obj->waitCount = 0;
+	obj->waitHandles[ obj->waitCount++ ] = obj->cancelEvent;
+	obj->waitHandles[ obj->waitCount++ ] = obj->data4Event;
+	obj->waitHandles[ obj->waitCount++ ] = obj->data6Event;
+	
+	check( obj->waitCount == sizeof_array( obj->waitHandles ) );
+	
+	// Copy the QuerySet so it can be returned later.
+	
+	obj->querySetFlags = inQuerySetFlags;
+	inQuerySetFlags = ( inQuerySetFlags & ~( LUP_RETURN_ADDR | LUP_RETURN_BLOB ) ) | LUP_RETURN_NAME;
+	err = QueryCopyQuerySet( obj, inQuerySet, inQuerySetFlags, &obj->querySet, &obj->querySetSize );
+	require_noerr( err, exit );
+	
+	// Success!
+	
+	*outRef	= obj;
+	obj 	= NULL;
+	err 	= NO_ERROR;
+
+exit:
+	if( obj )
+	{
+		QueryRelease( obj );
+	}
+	return( err );
+}
+
+//===========================================================================================================================
+//	QueryRetain
+//
+//	Warning: Assumes the NSP lock is held.
+//===========================================================================================================================
+
+DEBUG_LOCAL OSStatus	QueryRetain( QueryRef inRef )
+{
+	OSStatus		err;
+	QueryRef		obj;
+	
+	for( obj = gQueryList; obj; obj = obj->next )
+	{
+		if( obj == inRef )
+		{
+			break;
+		}
+	}
+	require_action( obj, exit, err = WSA_INVALID_HANDLE );
+	
+	++inRef->refCount;
+	err = NO_ERROR;
+	
+exit:
+	return( err );
+}
+
+//===========================================================================================================================
+//	QueryRelease
+//
+//	Warning: Assumes the NSP lock is held.
+//===========================================================================================================================
+
+DEBUG_LOCAL OSStatus	QueryRelease( QueryRef inRef )
+{
+	OSStatus		err;
+	QueryRef *		p;
+	BOOL			ok;
+		
+	// Find the item in the list.
+	
+	for( p = &gQueryList; *p; p = &( *p )->next )
+	{
+		if( *p == inRef )
+		{
+			break;
+		}
+	}
+	require_action( *p, exit, err = WSA_INVALID_HANDLE );
+	
+	// Signal a cancel to unblock any threads waiting for results.
+	
+	if( inRef->cancelEvent )
+	{
+		ok = SetEvent( inRef->cancelEvent );
+		check_translated_errno( ok, GetLastError(), WSAEINVAL );
+	}
+	
+	// Stop the query.
+	
+	if( inRef->resolver4 )
+	{
+		__try
+		{
+			DNSServiceRefDeallocate( inRef->resolver4 );
+		}
+		__except( EXCEPTION_EXECUTE_HANDLER )
+		{
+		}
+		
+		inRef->resolver4 = NULL;
+	}
+
+	if ( inRef->resolver6 )
+	{
+		__try
+		{
+			DNSServiceRefDeallocate( inRef->resolver6 );
+		}
+		__except( EXCEPTION_EXECUTE_HANDLER )
+		{
+		}
+
+		inRef->resolver6 = NULL;
+	}
+	
+	// Decrement the refCount. Fully release if it drops to 0. If still referenced, just exit.
+	
+	if( --inRef->refCount != 0 )
+	{
+		err = NO_ERROR;
+		goto exit;
+	}
+	*p = inRef->next;
+	
+	// Release resources.
+	
+	if( inRef->cancelEvent )
+	{
+		ok = CloseHandle( inRef->cancelEvent );
+		check_translated_errno( ok, GetLastError(), WSAEINVAL );
+	}
+	if( inRef->data4Event )
+	{
+		ok = CloseHandle( inRef->data4Event );
+		check_translated_errno( ok, GetLastError(), WSAEINVAL );
+	}
+	if( inRef->data6Event )
+	{
+		ok = CloseHandle( inRef->data6Event );
+		check_translated_errno( ok, GetLastError(), WSAEINVAL );
+	}
+	if( inRef->querySet )
+	{
+		free( inRef->querySet );
+	}
+	free( inRef );
+	err = NO_ERROR;
+	
+exit:
+	return( err );
+}
+
+//===========================================================================================================================
+//	QueryRecordCallback4
+//===========================================================================================================================
+
+DEBUG_LOCAL void CALLBACK_COMPAT
+	QueryRecordCallback4(
+		DNSServiceRef		inRef,
+		DNSServiceFlags		inFlags,
+		uint32_t			inInterfaceIndex,
+		DNSServiceErrorType	inErrorCode,
+		const char *		inName,    
+		uint16_t			inRRType,
+		uint16_t			inRRClass,
+		uint16_t			inRDataSize,
+		const void *		inRData,
+		uint32_t			inTTL,
+		void *				inContext )
+{
+	QueryRef			obj;
+	const char *		src;
+	char *				dst;
+	BOOL				ok;
+	
+	DEBUG_UNUSED( inFlags );
+	DEBUG_UNUSED( inInterfaceIndex );
+	DEBUG_UNUSED( inTTL );
+
+	NSPLock();
+	obj = (QueryRef) inContext;
+	check( obj );
+	require_noerr( inErrorCode, exit );
+	require_quiet( inFlags & kDNSServiceFlagsAdd, exit );
+	require( inRRClass   == kDNSServiceClass_IN, exit );
+	require( inRRType    == kDNSServiceType_A, exit );
+	require( inRDataSize == 4, exit );
+	
+	dlog( kDebugLevelTrace, "%s (flags=0x%08X, name=%s, rrType=%d, rDataSize=%d)\n", 
+		__ROUTINE__, inFlags, inName, inRRType, inRDataSize );
+		
+	// Copy the name if needed.
+	
+	if( obj->name[ 0 ] == '\0' )
+	{
+		src = inName;
+		dst = obj->name;
+		while( *src != '\0' )
+		{
+			*dst++ = *src++;
+		}
+		*dst = '\0';
+		obj->nameSize = (size_t)( dst - obj->name );
+		check( obj->nameSize < sizeof( obj->name ) );
+	}
+	
+	// Copy the data.
+	
+	memcpy( &obj->addr4, inRData, inRDataSize );
+	obj->addr4Valid = true;
+	obj->numValidAddrs++;
+	
+	// Signal that a result is ready.
+	
+	check( obj->data4Event );
+	ok = SetEvent( obj->data4Event );
+	check_translated_errno( ok, GetLastError(), WSAEINVAL );
+	
+	// Stop the resolver after the first response.
+	
+	__try
+	{
+		DNSServiceRefDeallocate( inRef );
+	}
+	__except( EXCEPTION_EXECUTE_HANDLER )
+	{
+	}
+
+	obj->resolver4 = NULL;
+
+exit:
+	NSPUnlock();
+}
+
+#if 0
+#pragma mark -
+#endif
+
+
+//===========================================================================================================================
+//	QueryRecordCallback6
+//===========================================================================================================================
+
+DEBUG_LOCAL void CALLBACK_COMPAT
+	QueryRecordCallback6(
+		DNSServiceRef		inRef,
+		DNSServiceFlags		inFlags,
+		uint32_t			inInterfaceIndex,
+		DNSServiceErrorType	inErrorCode,
+		const char *		inName,    
+		uint16_t			inRRType,
+		uint16_t			inRRClass,
+		uint16_t			inRDataSize,
+		const void *		inRData,
+		uint32_t			inTTL,
+		void *				inContext )
+{
+	QueryRef			obj;
+	const char *		src;
+	char *				dst;
+	BOOL				ok;
+	
+	DEBUG_UNUSED( inFlags );
+	DEBUG_UNUSED( inInterfaceIndex );
+	DEBUG_UNUSED( inTTL );
+
+	NSPLock();
+	obj = (QueryRef) inContext;
+	check( obj );
+	require_noerr( inErrorCode, exit );
+	require_quiet( inFlags & kDNSServiceFlagsAdd, exit );
+	require( inRRClass   == kDNSServiceClass_IN, exit );
+	require( inRRType    == kDNSServiceType_AAAA, exit );
+	require( inRDataSize == 16, exit );
+	
+	dlog( kDebugLevelTrace, "%s (flags=0x%08X, name=%s, rrType=%d, rDataSize=%d)\n", 
+		__ROUTINE__, inFlags, inName, inRRType, inRDataSize );
+
+	// Copy the name if needed.
+	
+	if( obj->name[ 0 ] == '\0' )
+	{
+		src = inName;
+		dst = obj->name;
+		while( *src != '\0' )
+		{
+			*dst++ = *src++;
+		}
+		*dst = '\0';
+		obj->nameSize = (size_t)( dst - obj->name );
+		check( obj->nameSize < sizeof( obj->name ) );
+	}
+	
+	// Copy the data.
+	
+	memcpy( &obj->addr6, inRData, inRDataSize );
+
+	obj->addr6ScopeId = GetScopeId( inInterfaceIndex );
+	require( obj->addr6ScopeId, exit );
+	obj->addr6Valid	  = true;
+	obj->numValidAddrs++;
+
+	// Signal that we're done
+	
+	check( obj->data6Event );
+	ok = SetEvent( obj->data6Event );
+	check_translated_errno( ok, GetLastError(), WSAEINVAL );
+
+	// Stop the resolver after the first response.
+	
+	__try
+	{
+		DNSServiceRefDeallocate( inRef );
+	}
+	__except( EXCEPTION_EXECUTE_HANDLER )
+	{
+	}
+
+	obj->resolver6 = NULL;
+
+exit:
+
+	
+	
+	NSPUnlock();
+}
+
+
+//===========================================================================================================================
+//	QueryCopyQuerySet
+//
+//	Warning: Assumes the NSP lock is held.
+//===========================================================================================================================
+
+DEBUG_LOCAL OSStatus
+	QueryCopyQuerySet( 
+		QueryRef 				inRef, 
+		const WSAQUERYSETW *	inQuerySet, 
+		DWORD 					inQuerySetFlags, 
+		WSAQUERYSETW **			outQuerySet, 
+		size_t *				outSize )
+{
+	OSStatus			err;
+	size_t				size;
+	WSAQUERYSETW *		qs;
+	
+	check( inQuerySet );
+	check( outQuerySet );
+	
+	size  = QueryCopyQuerySetSize( inRef, inQuerySet, inQuerySetFlags );
+	qs = (WSAQUERYSETW *) calloc( 1, size );
+	require_action( qs, exit, err = WSA_NOT_ENOUGH_MEMORY  );
+	
+	QueryCopyQuerySetTo( inRef, inQuerySet, inQuerySetFlags, qs );
+	
+	*outQuerySet = qs;
+	if( outSize )
+	{
+		*outSize = size;
+	}
+	qs = NULL;
+	err = NO_ERROR;
+	
+exit:
+	if( qs )
+	{
+		free( qs );
+	}
+	return( err );	
+}
+
+
+
+//===========================================================================================================================
+//	QueryCopyQuerySetTo
+//
+//	Warning: Assumes the NSP lock is held.
+//===========================================================================================================================
+
+DEBUG_LOCAL void
+	QueryCopyQuerySetTo( 
+		QueryRef 				inRef, 
+		const WSAQUERYSETW *	inQuerySet, 
+		DWORD 					inQuerySetFlags, 
+		WSAQUERYSETW *			outQuerySet )
+{
+	uint8_t *		dst;
+	LPCWSTR			s;
+	LPWSTR			q;
+	DWORD			n;
+	DWORD			i;
+	
+#if( DEBUG )
+	size_t			debugSize;
+	
+	debugSize = QueryCopyQuerySetSize( inRef, inQuerySet, inQuerySetFlags );
+#endif
+
+	check( inQuerySet );
+	check( outQuerySet );
+
+	dst = (uint8_t *) outQuerySet;
+	
+	// Copy the static portion of the results.
+	
+	*outQuerySet = *inQuerySet;
+	dst += sizeof( *inQuerySet );
+	
+	if( inQuerySetFlags & LUP_RETURN_NAME )
+	{
+		s = inQuerySet->lpszServiceInstanceName;
+		if( s )
+		{
+			outQuerySet->lpszServiceInstanceName = (LPWSTR) dst;
+			q = (LPWSTR) dst;
+			while( ( *q++ = *s++ ) != 0 ) {}
+			dst = (uint8_t *) q;
+		}
+	}
+	else
+	{
+		outQuerySet->lpszServiceInstanceName = NULL;
+	}
+	
+	if( inQuerySet->lpServiceClassId )
+	{
+		outQuerySet->lpServiceClassId  = (LPGUID) dst;
+		*outQuerySet->lpServiceClassId = *inQuerySet->lpServiceClassId;
+		dst += sizeof( *inQuerySet->lpServiceClassId );
+	}
+	
+	if( inQuerySet->lpVersion )
+	{
+		outQuerySet->lpVersion  = (LPWSAVERSION) dst;
+		*outQuerySet->lpVersion = *inQuerySet->lpVersion;
+		dst += sizeof( *inQuerySet->lpVersion );
+	}
+	
+	s = inQuerySet->lpszComment;
+	if( s )
+	{
+		outQuerySet->lpszComment = (LPWSTR) dst;
+		q = (LPWSTR) dst;
+		while( ( *q++ = *s++ ) != 0 ) {}
+		dst = (uint8_t *) q;
+	}
+	
+	if( inQuerySet->lpNSProviderId )
+	{
+		outQuerySet->lpNSProviderId  = (LPGUID) dst;
+		*outQuerySet->lpNSProviderId = *inQuerySet->lpNSProviderId;
+		dst += sizeof( *inQuerySet->lpNSProviderId );
+	}
+	
+	s = inQuerySet->lpszContext;
+	if( s )
+	{
+		outQuerySet->lpszContext = (LPWSTR) dst;
+		q = (LPWSTR) dst;
+		while( ( *q++ = *s++ ) != 0 ) {}
+		dst = (uint8_t *) q;
+	}
+		
+	n = inQuerySet->dwNumberOfProtocols;
+
+	if( n > 0 )
+	{
+		check( inQuerySet->lpafpProtocols );
+		
+		outQuerySet->lpafpProtocols = (LPAFPROTOCOLS) dst;
+		for( i = 0; i < n; ++i )
+		{
+			outQuerySet->lpafpProtocols[ i ] = inQuerySet->lpafpProtocols[ i ];
+			dst += sizeof( *inQuerySet->lpafpProtocols );
+		}
+	}
+		
+	s = inQuerySet->lpszQueryString;
+	if( s )
+	{
+		outQuerySet->lpszQueryString = (LPWSTR) dst;
+		q = (LPWSTR) dst;
+		while( ( *q++ = *s++ ) != 0 ) {}
+		dst = (uint8_t *) q;
+	}
+	
+	// Copy the address(es).
+	
+	if( ( inQuerySetFlags & LUP_RETURN_ADDR ) && ( inRef->numValidAddrs > 0 ) )
+	{
+		struct sockaddr_in	*	addr4;
+		struct sockaddr_in6	*	addr6;
+		int						index;
+		
+		outQuerySet->dwNumberOfCsAddrs	= inRef->numValidAddrs;
+		outQuerySet->lpcsaBuffer 		= (LPCSADDR_INFO) dst;
+		dst 							+= ( sizeof( *outQuerySet->lpcsaBuffer ) ) * ( inRef->numValidAddrs ) ;
+		index							= 0;
+		
+		if ( inRef->addr4Valid )
+		{	
+			outQuerySet->lpcsaBuffer[ index ].LocalAddr.lpSockaddr 			= NULL;
+			outQuerySet->lpcsaBuffer[ index ].LocalAddr.iSockaddrLength		= 0;
+		
+			outQuerySet->lpcsaBuffer[ index ].RemoteAddr.lpSockaddr 		= (LPSOCKADDR) dst;
+			outQuerySet->lpcsaBuffer[ index ].RemoteAddr.iSockaddrLength	= sizeof( struct sockaddr_in );
+		
+			addr4 															= (struct sockaddr_in *) dst;
+			memset( addr4, 0, sizeof( *addr4 ) );
+			addr4->sin_family												= AF_INET;
+			memcpy( &addr4->sin_addr, &inRef->addr4, 4 );
+			dst 															+= sizeof( *addr4 );
+		
+			outQuerySet->lpcsaBuffer[ index ].iSocketType 					= AF_INET;		// Emulate Tcpip NSP
+			outQuerySet->lpcsaBuffer[ index ].iProtocol						= IPPROTO_UDP;	// Emulate Tcpip NSP
+
+			index++;
+		}
+
+		if ( inRef->addr6Valid )
+		{
+			outQuerySet->lpcsaBuffer[ index ].LocalAddr.lpSockaddr 			= NULL;
+			outQuerySet->lpcsaBuffer[ index ].LocalAddr.iSockaddrLength		= 0;
+		
+			outQuerySet->lpcsaBuffer[ index ].RemoteAddr.lpSockaddr 		= (LPSOCKADDR) dst;
+			outQuerySet->lpcsaBuffer[ index ].RemoteAddr.iSockaddrLength	= sizeof( struct sockaddr_in6 );
+		
+			addr6 															= (struct sockaddr_in6 *) dst;
+			memset( addr6, 0, sizeof( *addr6 ) );
+			addr6->sin6_family												= AF_INET6;
+			addr6->sin6_scope_id											= inRef->addr6ScopeId;
+			memcpy( &addr6->sin6_addr, &inRef->addr6, 16 );
+			dst 															+= sizeof( *addr6 );
+		
+			outQuerySet->lpcsaBuffer[ index ].iSocketType 					= AF_INET6;		// Emulate Tcpip NSP
+			outQuerySet->lpcsaBuffer[ index ].iProtocol						= IPPROTO_UDP;	// Emulate Tcpip NSP
+		}
+	}
+	else
+	{
+		outQuerySet->dwNumberOfCsAddrs	= 0;
+		outQuerySet->lpcsaBuffer 		= NULL;
+	}
+	
+	// Copy the hostent blob.
+	
+	if( ( inQuerySetFlags & LUP_RETURN_BLOB ) && inRef->addr4Valid )
+	{
+		uint8_t *				base;
+		struct hostent *		he;
+		uintptr_t *				p;
+
+		outQuerySet->lpBlob	 = (LPBLOB) dst;
+		dst 				+= sizeof( *outQuerySet->lpBlob );
+		
+		base = dst;
+		he	 = (struct hostent *) dst;
+		dst += sizeof( *he );
+		
+		he->h_name = (char *)( dst - base );
+		memcpy( dst, inRef->name, inRef->nameSize + 1 );
+		dst += ( inRef->nameSize + 1 );
+		
+		he->h_aliases 	= (char **)( dst - base );
+		p	  			= (uintptr_t *) dst;
+		*p++  			= 0;
+		dst 		 	= (uint8_t *) p;
+		
+		he->h_addrtype 	= AF_INET;
+		he->h_length	= 4;
+		
+		he->h_addr_list	= (char **)( dst - base );
+		p	  			= (uintptr_t *) dst;
+		dst 		   += ( 2 * sizeof( *p ) );
+		*p++			= (uintptr_t)( dst - base );
+		*p++			= 0;
+		p	  			= (uintptr_t *) dst;
+		*p++			= (uintptr_t) inRef->addr4;
+		dst 		 	= (uint8_t *) p;
+		
+		outQuerySet->lpBlob->cbSize 	= (ULONG)( dst - base );
+		outQuerySet->lpBlob->pBlobData	= (BYTE *) base;
+	}
+	dlog_query_set( kDebugLevelVerbose, outQuerySet );
+
+	check( (size_t)( dst - ( (uint8_t *) outQuerySet ) ) == debugSize );
+}
+
+//===========================================================================================================================
+//	QueryCopyQuerySetSize
+//
+//	Warning: Assumes the NSP lock is held.
+//===========================================================================================================================
+
+DEBUG_LOCAL size_t	QueryCopyQuerySetSize( QueryRef inRef, const WSAQUERYSETW *inQuerySet, DWORD inQuerySetFlags )
+{
+	size_t		size;
+	LPCWSTR		s;
+	LPCWSTR		p;
+	
+	check( inRef );
+	check( inQuerySet );
+	
+	// Calculate the size of the static portion of the results.
+	
+	size = sizeof( *inQuerySet );
+	
+	if( inQuerySetFlags & LUP_RETURN_NAME )
+	{
+		s = inQuerySet->lpszServiceInstanceName;
+		if( s )
+		{
+			for( p = s; *p; ++p ) {}
+			size += (size_t)( ( ( p - s ) + 1 ) * sizeof( *p ) );
+		}
+	}
+	
+	if( inQuerySet->lpServiceClassId )
+	{
+		size += sizeof( *inQuerySet->lpServiceClassId );
+	}
+	
+	if( inQuerySet->lpVersion )
+	{
+		size += sizeof( *inQuerySet->lpVersion );
+	}
+	
+	s = inQuerySet->lpszComment;
+	if( s )
+	{
+		for( p = s; *p; ++p ) {}
+		size += (size_t)( ( ( p - s ) + 1 ) * sizeof( *p ) );
+	}
+	
+	if( inQuerySet->lpNSProviderId )
+	{
+		size += sizeof( *inQuerySet->lpNSProviderId );
+	}
+	
+	s = inQuerySet->lpszContext;
+	if( s )
+	{
+		for( p = s; *p; ++p ) {}
+		size += (size_t)( ( ( p - s ) + 1 ) * sizeof( *p ) );
+	}
+	
+	size += ( inQuerySet->dwNumberOfProtocols * sizeof( *inQuerySet->lpafpProtocols ) );
+	
+	s = inQuerySet->lpszQueryString;
+	if( s )
+	{
+		for( p = s; *p; ++p ) {}
+		size += (size_t)( ( ( p - s ) + 1 ) * sizeof( *p ) );
+	}
+	
+	// Calculate the size of the address(es).
+	
+	if( ( inQuerySetFlags & LUP_RETURN_ADDR ) && inRef->addr4Valid )
+	{
+		size += sizeof( *inQuerySet->lpcsaBuffer );
+		size += sizeof( struct sockaddr_in );
+	}
+
+	if( ( inQuerySetFlags & LUP_RETURN_ADDR ) && inRef->addr6Valid )
+	{
+		size += sizeof( *inQuerySet->lpcsaBuffer );
+		size += sizeof( struct sockaddr_in6 );
+	}
+	
+	// Calculate the size of the hostent blob.
+	
+	if( ( inQuerySetFlags & LUP_RETURN_BLOB ) && inRef->addr4Valid )
+	{
+		size += sizeof( *inQuerySet->lpBlob );	// Blob ptr/size structure
+		size += sizeof( struct hostent );		// Old-style hostent structure
+		size += ( inRef->nameSize + 1 );		// Name and null terminator
+		size += 4;								// Alias list terminator (0 offset)
+		size += 4;								// Offset to address.
+		size += 4;								// Address list terminator (0 offset)
+		size += 4;								// IPv4 address
+	}
+	return( size );
+}
+
+#if 0
+#pragma mark -
+#endif
+
+#if( DEBUG )
+//===========================================================================================================================
+//	DebugDumpQuerySet
+//===========================================================================================================================
+
+#define	DebugSocketFamilyToString( FAM )	( ( FAM ) == AF_INET )  ? "AF_INET"  : \
+											( ( FAM ) == AF_INET6 ) ? "AF_INET6" : ""
+
+#define	DebugSocketProtocolToString( PROTO )	( ( PROTO ) == IPPROTO_UDP ) ? "IPPROTO_UDP" : \
+												( ( PROTO ) == IPPROTO_TCP ) ? "IPPROTO_TCP" : ""
+
+#define	DebugNameSpaceToString( NS )			( ( NS ) == NS_DNS ) ? "NS_DNS" : ( ( NS ) == NS_ALL ) ? "NS_ALL" : ""
+
+void	DebugDumpQuerySet( DebugLevel inLevel, const WSAQUERYSETW *inQuerySet )
+{
+	DWORD		i;
+	
+	check( inQuerySet );
+
+	// Fixed portion of the QuerySet.
+		
+	dlog( inLevel, "QuerySet:\n" );
+	dlog( inLevel, "    dwSize:                  %d (expected %d)\n", inQuerySet->dwSize, sizeof( *inQuerySet ) );
+	if( inQuerySet->lpszServiceInstanceName )
+	{
+		dlog( inLevel, "    lpszServiceInstanceName: %S\n", inQuerySet->lpszServiceInstanceName );
+	}
+	else
+	{
+		dlog( inLevel, "    lpszServiceInstanceName: <null>\n" );
+	}
+	if( inQuerySet->lpServiceClassId )
+	{
+		dlog( inLevel, "    lpServiceClassId:        %U\n", inQuerySet->lpServiceClassId );
+	}
+	else
+	{
+		dlog( inLevel, "    lpServiceClassId:        <null>\n" );
+	}
+	if( inQuerySet->lpVersion )
+	{
+		dlog( inLevel, "    lpVersion:\n" );
+		dlog( inLevel, "        dwVersion:               %d\n", inQuerySet->lpVersion->dwVersion );
+		dlog( inLevel, "        dwVersion:               %d\n", inQuerySet->lpVersion->ecHow );
+	}
+	else
+	{
+		dlog( inLevel, "    lpVersion:               <null>\n" );
+	}
+	if( inQuerySet->lpszComment )
+	{
+		dlog( inLevel, "    lpszComment:             %S\n", inQuerySet->lpszComment );
+	}
+	else
+	{
+		dlog( inLevel, "    lpszComment:             <null>\n" );
+	}
+	dlog( inLevel, "    dwNameSpace:             %d %s\n", inQuerySet->dwNameSpace, 
+		DebugNameSpaceToString( inQuerySet->dwNameSpace ) );
+	if( inQuerySet->lpNSProviderId )
+	{
+		dlog( inLevel, "    lpNSProviderId:          %U\n", inQuerySet->lpNSProviderId );
+	}
+	else
+	{
+		dlog( inLevel, "    lpNSProviderId:          <null>\n" );
+	}
+	if( inQuerySet->lpszContext )
+	{
+		dlog( inLevel, "    lpszContext:             %S\n", inQuerySet->lpszContext );
+	}
+	else
+	{
+		dlog( inLevel, "    lpszContext:             <null>\n" );
+	}
+	dlog( inLevel, "    dwNumberOfProtocols:     %d\n", inQuerySet->dwNumberOfProtocols );
+	dlog( inLevel, "    lpafpProtocols:          %s\n", inQuerySet->lpafpProtocols ? "" : "<null>" );
+	for( i = 0; i < inQuerySet->dwNumberOfProtocols; ++i )
+	{
+		if( i != 0 )
+		{
+			dlog( inLevel, "\n" );
+		}
+		dlog( inLevel, "        iAddressFamily:          %d %s\n", inQuerySet->lpafpProtocols[ i ].iAddressFamily, 
+			DebugSocketFamilyToString( inQuerySet->lpafpProtocols[ i ].iAddressFamily ) );
+		dlog( inLevel, "        iProtocol:               %d %s\n", inQuerySet->lpafpProtocols[ i ].iProtocol, 
+			DebugSocketProtocolToString( inQuerySet->lpafpProtocols[ i ].iProtocol ) );
+	}
+	if( inQuerySet->lpszQueryString )
+	{
+		dlog( inLevel, "    lpszQueryString:         %S\n", inQuerySet->lpszQueryString );
+	}
+	else
+	{
+		dlog( inLevel, "    lpszQueryString:         <null>\n" );
+	}
+	dlog( inLevel, "    dwNumberOfCsAddrs:       %d\n", inQuerySet->dwNumberOfCsAddrs );
+	dlog( inLevel, "    lpcsaBuffer:             %s\n", inQuerySet->lpcsaBuffer ? "" : "<null>" );
+	for( i = 0; i < inQuerySet->dwNumberOfCsAddrs; ++i )
+	{
+		if( i != 0 )
+		{
+			dlog( inLevel, "\n" );
+		}
+		if( inQuerySet->lpcsaBuffer[ i ].LocalAddr.lpSockaddr && 
+			( inQuerySet->lpcsaBuffer[ i ].LocalAddr.iSockaddrLength > 0 ) )
+		{
+			dlog( inLevel, "        LocalAddr:               %##a\n", 
+				inQuerySet->lpcsaBuffer[ i ].LocalAddr.lpSockaddr );
+		}
+		else
+		{
+			dlog( inLevel, "        LocalAddr:               <null/empty>\n" );
+		}
+		if( inQuerySet->lpcsaBuffer[ i ].RemoteAddr.lpSockaddr && 
+			( inQuerySet->lpcsaBuffer[ i ].RemoteAddr.iSockaddrLength > 0 ) )
+		{
+			dlog( inLevel, "        RemoteAddr:              %##a\n", 
+				inQuerySet->lpcsaBuffer[ i ].RemoteAddr.lpSockaddr );
+		}
+		else
+		{
+			dlog( inLevel, "        RemoteAddr:              <null/empty>\n" );
+		}
+		dlog( inLevel, "        iSocketType:             %d\n", inQuerySet->lpcsaBuffer[ i ].iSocketType );
+		dlog( inLevel, "        iProtocol:               %d\n", inQuerySet->lpcsaBuffer[ i ].iProtocol );
+	}
+	dlog( inLevel, "    dwOutputFlags:           %d\n", inQuerySet->dwOutputFlags );
+	
+	// Blob portion of the QuerySet.
+	
+	if( inQuerySet->lpBlob )
+	{
+		dlog( inLevel, "    lpBlob:\n" );
+		dlog( inLevel, "        cbSize:                  %ld\n", inQuerySet->lpBlob->cbSize );
+		dlog( inLevel, "        pBlobData:               %#p\n", inQuerySet->lpBlob->pBlobData );
+		dloghex( inLevel, 12, NULL, 0, 0, NULL, 0, 
+			inQuerySet->lpBlob->pBlobData, inQuerySet->lpBlob->pBlobData, inQuerySet->lpBlob->cbSize, 
+			kDebugFlagsNone, NULL, 0 );
+	}
+	else
+	{
+		dlog( inLevel, "    lpBlob:                  <null>\n" );
+	}
+}
+#endif
+
+
+//===========================================================================================================================
+//	InHostsTable
+//===========================================================================================================================
+
+DEBUG_LOCAL BOOL
+InHostsTable( const char * name )
+{
+	HostsFileInfo	*	node;
+	BOOL				ret = FALSE;
+	OSStatus			err;
+	
+	check( name );
+
+	if ( gHostsFileInfo == NULL )
+	{
+		TCHAR				systemDirectory[MAX_PATH];
+		TCHAR				hFileName[MAX_PATH];
+		HostsFile		*	hFile;
+
+		GetSystemDirectory( systemDirectory, sizeof( systemDirectory ) );
+		sprintf( hFileName, "%s\\drivers\\etc\\hosts", systemDirectory );
+		err = HostsFileOpen( &hFile, hFileName );
+		require_noerr( err, exit );
+
+		while ( HostsFileNext( hFile, &node ) == 0 )
+		{
+			if ( IsLocalName( node ) )
+			{
+				node->m_next = gHostsFileInfo;
+				gHostsFileInfo = node;
+			}
+			else
+			{
+				HostsFileInfoFree( node );
+			}
+		}
+
+		HostsFileClose( hFile );
+	}
+
+	for ( node = gHostsFileInfo; node; node = node->m_next )
+	{
+		if ( IsSameName( node, name ) )
+		{
+			ret = TRUE;
+			break;
+		}
+	}
+
+exit:
+
+	return ret;
+}
+
+
+//===========================================================================================================================
+//	IsLocalName
+//===========================================================================================================================
+
+DEBUG_LOCAL BOOL
+IsLocalName( HostsFileInfo * node )
+{
+	BOOL ret = TRUE;
+
+	check( node );
+
+	if ( strstr( node->m_host.h_name, ".local" ) == NULL )
+	{
+		int i;
+
+		for ( i = 0; node->m_host.h_aliases[i]; i++ )
+		{
+			if ( strstr( node->m_host.h_aliases[i], ".local" ) )
+			{
+				goto exit;
+			}
+		}
+
+		ret = FALSE;
+	}
+
+exit:
+
+	return ret;
+}
+
+
+//===========================================================================================================================
+//	IsSameName
+//===========================================================================================================================
+
+DEBUG_LOCAL BOOL
+IsSameName( HostsFileInfo * node, const char * name )
+{
+	BOOL ret = TRUE;
+
+	check( node );
+	check( name );
+
+	if ( strcmp( node->m_host.h_name, name ) != 0 )
+	{
+		int i;
+
+		for ( i = 0; node->m_host.h_aliases[i]; i++ )
+		{
+			if ( strcmp( node->m_host.h_aliases[i], name ) == 0 )
+			{
+				goto exit;
+			}
+		}
+
+		ret = FALSE;
+	}
+
+exit:
+
+	return ret;
+}
+
+
+//===========================================================================================================================
+//	HostsFileOpen
+//===========================================================================================================================
+
+DEBUG_LOCAL OSStatus
+HostsFileOpen( HostsFile ** self, const char * fname )
+{
+	OSStatus err = kNoErr;
+
+	*self = (HostsFile*) malloc( sizeof( HostsFile ) );
+	require_action( *self, exit, err = kNoMemoryErr );
+	memset( *self, 0, sizeof( HostsFile ) );
+
+	(*self)->m_bufferSize = BUFFER_INITIAL_SIZE;
+	(*self)->m_buffer = (char*) malloc( (*self)->m_bufferSize );
+	require_action( (*self)->m_buffer, exit, err = kNoMemoryErr );
+
+	// check malloc
+
+	(*self)->m_fp = fopen( fname, "r" );
+	require_action( (*self)->m_fp, exit, err = kUnknownErr );
+
+exit:
+
+	if ( err && *self )
+	{
+		HostsFileClose( *self );
+		*self = NULL;
+	}
+		
+	return err;
+}
+
+
+//===========================================================================================================================
+//	HostsFileClose
+//===========================================================================================================================
+
+DEBUG_LOCAL OSStatus
+HostsFileClose( HostsFile * self )
+{
+	check( self );
+
+	if ( self->m_buffer )
+	{
+		free( self->m_buffer );
+		self->m_buffer = NULL;
+	}
+
+	if ( self->m_fp )
+	{
+		fclose( self->m_fp );
+		self->m_fp = NULL;
+	}
+
+	free( self );
+
+	return kNoErr;
+} 
+
+
+//===========================================================================================================================
+//	HostsFileInfoFree
+//===========================================================================================================================
+
+DEBUG_LOCAL void
+HostsFileInfoFree( HostsFileInfo * info )
+{
+	while ( info )
+	{
+		HostsFileInfo * next = info->m_next;
+
+		if ( info->m_host.h_addr_list )
+		{
+			if ( info->m_host.h_addr_list[0] )
+			{
+				free( info->m_host.h_addr_list[0] );
+				info->m_host.h_addr_list[0] = NULL;
+			}
+
+			free( info->m_host.h_addr_list );
+			info->m_host.h_addr_list = NULL;
+		}
+
+		if ( info->m_host.h_aliases )
+		{
+			int i;
+
+			for ( i = 0; info->m_host.h_aliases[i]; i++ )
+			{
+				free( info->m_host.h_aliases[i] );
+			}
+
+			free( info->m_host.h_aliases );
+		}
+
+		if ( info->m_host.h_name )
+		{
+			free( info->m_host.h_name );
+			info->m_host.h_name = NULL;
+		}
+			
+		free( info );
+
+		info = next;
+	}
+}
+
+
+//===========================================================================================================================
+//	HostsFileNext
+//===========================================================================================================================
+
+DEBUG_LOCAL OSStatus
+HostsFileNext( HostsFile * self, HostsFileInfo ** hInfo )
+{
+	struct sockaddr_in6	addr_6;
+	struct sockaddr_in	addr_4;
+	int					numAliases = ALIASES_INITIAL_SIZE;
+	char			*	line;
+	char			*	tok;
+	int					dwSize;
+	int					idx;
+	int					i;
+	short				family;
+	OSStatus			err = kNoErr;
+
+	check( self );
+	check( self->m_fp );
+	check( hInfo );
+
+	idx	= 0;
+
+	*hInfo = (HostsFileInfo*) malloc( sizeof( HostsFileInfo ) );
+	require_action( *hInfo, exit, err = kNoMemoryErr );
+	memset( *hInfo, 0, sizeof( HostsFileInfo ) );
+
+	for ( ; ; )
+	{
+		line = fgets( self->m_buffer + idx, self->m_bufferSize - idx, self->m_fp );
+		
+		if ( line == NULL )
+		{
+			err = 1;
+			goto exit;
+		}
+
+		// If there's no eol and no eof, then we didn't get the whole line
+
+		if ( !strchr( line, '\n' ) && !feof( self->m_fp ) )
+		{
+			int			bufferSize;
+			char	*	buffer;
+
+			/* Try and allocate space for longer line */
+
+			bufferSize	= self->m_bufferSize * 2;
+			buffer		= (char*) realloc( self->m_buffer, bufferSize );
+			require_action( buffer, exit, err = kNoMemoryErr );
+			self->m_bufferSize	= bufferSize;
+			self->m_buffer		= buffer;
+			idx					= (int) strlen( self->m_buffer );
+
+			continue;
+		}
+
+		line	= self->m_buffer;
+		idx		= 0;
+
+		if (*line == '#')
+		{
+			continue;
+		}
+
+		// Get rid of either comments or eol characters
+
+		if (( tok = strpbrk(line, "#\n")) != NULL )
+		{
+			*tok = '\0';
+		}
+
+		// Make sure there is some whitespace on this line
+
+		if (( tok = strpbrk(line, " \t")) == NULL )
+		{
+			continue;
+		}
+
+		// Create two strings, where p == the IP Address and tok is the name list
+
+		*tok++ = '\0';
+
+		while ( *tok == ' ' || *tok == '\t')
+		{
+			tok++;
+		}
+
+		// Now we have the name
+
+		(*hInfo)->m_host.h_name = (char*) malloc( strlen( tok ) + 1 );
+		require_action( (*hInfo)->m_host.h_name, exit, err = kNoMemoryErr );
+		strcpy( (*hInfo)->m_host.h_name, tok );
+
+		// Now create the address (IPv6/IPv4)
+
+		addr_6.sin6_family	= family = AF_INET6;
+		dwSize				= sizeof( addr_6 );
+
+		if ( WSAStringToAddress( line, AF_INET6, NULL, ( struct sockaddr*) &addr_6, &dwSize ) != 0 )
+		{
+			addr_4.sin_family = family = AF_INET;
+			dwSize = sizeof( addr_4 );
+
+			if (WSAStringToAddress( line, AF_INET, NULL, ( struct sockaddr*) &addr_4, &dwSize ) != 0 )
+			{
+				continue;
+			}
+		}
+
+		(*hInfo)->m_host.h_addr_list = (char**) malloc( sizeof( char**) * 2 );
+		require_action( (*hInfo)->m_host.h_addr_list, exit, err = kNoMemoryErr );
+
+		if ( family == AF_INET6 )
+		{
+			(*hInfo)->m_host.h_length		= (short) sizeof( addr_6.sin6_addr );
+			(*hInfo)->m_host.h_addr_list[0] = (char*) malloc( (*hInfo)->m_host.h_length );
+			require_action( (*hInfo)->m_host.h_addr_list[0], exit, err = kNoMemoryErr );
+			memmove( (*hInfo)->m_host.h_addr_list[0], &addr_6.sin6_addr, sizeof( addr_6.sin6_addr ) );
+			
+		}
+		else
+		{
+			(*hInfo)->m_host.h_length		= (short) sizeof( addr_4.sin_addr );
+			(*hInfo)->m_host.h_addr_list[0] = (char*) malloc( (*hInfo)->m_host.h_length );
+			require_action( (*hInfo)->m_host.h_addr_list[0], exit, err = kNoMemoryErr );
+			memmove( (*hInfo)->m_host.h_addr_list[0], &addr_4.sin_addr, sizeof( addr_4.sin_addr ) );
+		}
+
+		(*hInfo)->m_host.h_addr_list[1] = NULL;
+		(*hInfo)->m_host.h_addrtype		= family;
+
+		// Now get the aliases
+
+		if ((tok = strpbrk(tok, " \t")) != NULL)
+		{
+			*tok++ = '\0';
+		}
+
+		i = 0;
+
+		(*hInfo)->m_host.h_aliases		= (char**) malloc( sizeof(char**) * numAliases );
+		require_action( (*hInfo)->m_host.h_aliases, exit, err = kNoMemoryErr );
+		(*hInfo)->m_host.h_aliases[0]	= NULL;
+
+		while ( tok && *tok )
+		{
+			// Skip over the whitespace, waiting for the start of the next alias name
+
+			if (*tok == ' ' || *tok == '\t')
+			{
+				tok++;
+				continue;
+			}
+
+			// Check to make sure we don't exhaust the alias buffer
+
+			if ( i >= ( numAliases - 1 ) )
+			{
+				numAliases = numAliases * 2;
+				(*hInfo)->m_host.h_aliases = (char**) realloc( (*hInfo)->m_host.h_aliases, numAliases * sizeof( char** ) );
+				require_action( (*hInfo)->m_host.h_aliases, exit, err = kNoMemoryErr );
+			}
+
+			(*hInfo)->m_host.h_aliases[i] = (char*) malloc( strlen( tok ) + 1 );
+			require_action( (*hInfo)->m_host.h_aliases[i], exit, err = kNoMemoryErr );
+
+			strcpy( (*hInfo)->m_host.h_aliases[i], tok );
+
+			if (( tok = strpbrk( tok, " \t")) != NULL )
+			{
+				*tok++ = '\0';
+			}
+
+			(*hInfo)->m_host.h_aliases[++i] = NULL;
+		}
+
+		break;
+	}
+
+exit:
+
+	if ( err && ( *hInfo ) )
+	{
+		HostsFileInfoFree( *hInfo );
+		*hInfo = NULL;
+	}
+
+	return err;
+}
+
+
+#ifdef ENABLE_REVERSE_LOOKUP
+//===========================================================================================================================
+//	IsReverseLookup
+//===========================================================================================================================
+
+DEBUG_LOCAL OSStatus
+IsReverseLookup( LPCWSTR name, size_t size )
+{
+	LPCWSTR		p;
+	OSStatus	err = kNoErr;
+
+	// IPv6LL Reverse-mapping domains are {8,9,A,B}.E.F.ip6.arpa
+	require_action_quiet( size > sizeof_string( ".0.8.e.f.ip6.arpa" ), exit, err = WSASERVICE_NOT_FOUND );
+ 
+	p = name + ( size - 1 );
+	p = ( *p == '.' ) ? ( p - sizeof_string( ".0.8.e.f.ip6.arpa" ) ) : ( ( p - sizeof_string( ".0.8.e.f.ip6.arpa" ) ) + 1 );
+	
+	if	( ( ( p[ 0 ] != '.' )							||
+		( ( p[ 1 ] != '0' ) )							||
+		( ( p[ 2 ] != '.' ) )							||
+		( ( p[ 3 ] != '8' ) )							||
+		( ( p[ 4 ] != '.' ) )							||
+		( ( p[ 5 ] != 'E' ) && ( p[ 5 ] != 'e' ) )		||
+		( ( p[ 6 ] != '.' ) )							||
+		( ( p[ 7 ] != 'F' ) && ( p[ 7 ] != 'f' ) )		||
+		( ( p[ 8 ] != '.' ) )							||
+		( ( p[ 9 ] != 'I' ) && ( p[ 9 ] != 'i' ) )		||
+		( ( p[ 10 ] != 'P' ) && ( p[ 10 ] != 'p' ) )	||	
+		( ( p[ 11 ] != '6' ) )							||
+		( ( p[ 12 ] != '.' ) )							||
+		( ( p[ 13 ] != 'A' ) && ( p[ 13 ] != 'a' ) )	||
+		( ( p[ 14 ] != 'R' ) && ( p[ 14 ] != 'r' ) )	||
+		( ( p[ 15 ] != 'P' ) && ( p[ 15 ] != 'p' ) )	||
+		( ( p[ 16 ] != 'A' ) && ( p[ 16 ] != 'a' ) ) ) )
+	{
+		require_action_quiet( size > sizeof_string( ".254.169.in-addr.arpa" ), exit, err = WSASERVICE_NOT_FOUND );
+ 
+		p = name + ( size - 1 );
+		p = ( *p == '.' ) ? ( p - sizeof_string( ".254.169.in-addr.arpa" ) ) : ( ( p - sizeof_string( ".254.169.in-addr.arpa" ) ) + 1 );
+	
+		require_action_quiet( ( ( p[ 0 ] == '.' )						 &&
+								( ( p[ 1 ] == '2' ) )							&&
+								( ( p[ 2 ] == '5' ) )							&&
+								( ( p[ 3 ] == '4' ) )							&&
+								( ( p[ 4 ] == '.' ) )							&&
+								( ( p[ 5 ] == '1' ) )							&&
+								( ( p[ 6 ] == '6' ) )							&&
+								( ( p[ 7 ] == '9' ) )							&&
+								( ( p[ 8 ] == '.' ) )							&&
+								( ( p[ 9 ] == 'I' ) || ( p[ 9 ] == 'i' ) )		&&
+								( ( p[ 10 ] == 'N' ) || ( p[ 10 ] == 'n' ) )	&&	
+								( ( p[ 11 ] == '-' ) )							&&
+								( ( p[ 12 ] == 'A' ) || ( p[ 12 ] == 'a' ) )	&&
+								( ( p[ 13 ] == 'D' ) || ( p[ 13 ] == 'd' ) )	&&
+								( ( p[ 14 ] == 'D' ) || ( p[ 14 ] == 'd' ) )	&&
+								( ( p[ 15 ] == 'R' ) || ( p[ 15 ] == 'r' ) )	&&
+								( ( p[ 16 ] == '.' ) )							&&
+								( ( p[ 17 ] == 'A' ) || ( p[ 17 ] == 'a' ) )	&&
+								( ( p[ 18 ] == 'R' ) || ( p[ 18 ] == 'r' ) )	&&
+								( ( p[ 19 ] == 'P' ) || ( p[ 19 ] == 'p' ) )	&&
+								( ( p[ 20 ] == 'A' ) || ( p[ 20 ] == 'a' ) ) ),
+								exit, err = WSASERVICE_NOT_FOUND );
+	}
+
+	// It's a reverse lookup
+
+	check( err == kNoErr );
+
+exit:
+
+	return err;
+}
+#endif
+
+//===========================================================================================================================
+//	GetScopeId
+//===========================================================================================================================
+
+DEBUG_LOCAL DWORD
+GetScopeId( DWORD ifIndex )
+{
+	DWORD						err;
+	int							i;
+	DWORD						flags;
+	struct ifaddrs *			head;
+	struct ifaddrs **			next;
+	IP_ADAPTER_ADDRESSES *		iaaList;
+	ULONG						iaaListSize;
+	IP_ADAPTER_ADDRESSES *		iaa;
+	DWORD						scopeId = 0;
+	
+	head	= NULL;
+	next	= &head;
+	iaaList	= NULL;
+	
+	require( gGetAdaptersAddressesFunctionPtr, exit );
+
+	// Get the list of interfaces. The first call gets the size and the second call gets the actual data.
+	// This loops to handle the case where the interface changes in the window after getting the size, but before the
+	// second call completes. A limit of 100 retries is enforced to prevent infinite loops if something else is wrong.
+	
+	flags = GAA_FLAG_SKIP_ANYCAST | GAA_FLAG_SKIP_MULTICAST | GAA_FLAG_SKIP_DNS_SERVER | GAA_FLAG_SKIP_FRIENDLY_NAME;
+	i = 0;
+	for( ;; )
+	{
+		iaaListSize = 0;
+		err = gGetAdaptersAddressesFunctionPtr( AF_UNSPEC, flags, NULL, NULL, &iaaListSize );
+		check( err == ERROR_BUFFER_OVERFLOW );
+		check( iaaListSize >= sizeof( IP_ADAPTER_ADDRESSES ) );
+		
+		iaaList = (IP_ADAPTER_ADDRESSES *) malloc( iaaListSize );
+		require_action( iaaList, exit, err = ERROR_NOT_ENOUGH_MEMORY );
+		
+		err = gGetAdaptersAddressesFunctionPtr( AF_UNSPEC, flags, NULL, iaaList, &iaaListSize );
+		if( err == ERROR_SUCCESS ) break;
+		
+		free( iaaList );
+		iaaList = NULL;
+		++i;
+		require( i < 100, exit );
+		dlog( kDebugLevelWarning, "%s: retrying GetAdaptersAddresses after %d failure(s) (%d %m)\n", __ROUTINE__, i, err, err );
+	}
+	
+	for( iaa = iaaList; iaa; iaa = iaa->Next )
+	{
+		DWORD ipv6IfIndex;
+
+		if ( iaa->IfIndex > 0xFFFFFF )
+		{
+			continue;
+		}
+		if ( iaa->Ipv6IfIndex > 0xFF )
+		{
+			continue;
+		}
+
+		// For IPv4 interfaces, there seems to be a bug in iphlpapi.dll that causes the 
+		// following code to crash when iterating through the prefix list.  This seems
+		// to occur when iaa->Ipv6IfIndex != 0 when IPv6 is not installed on the host.
+		// This shouldn't happen according to Microsoft docs which states:
+		//
+		//     "Ipv6IfIndex contains 0 if IPv6 is not available on the interface."
+		//
+		// So the data structure seems to be corrupted when we return from
+		// GetAdaptersAddresses(). The bug seems to occur when iaa->Length <
+		// sizeof(IP_ADAPTER_ADDRESSES), so when that happens, we'll manually
+		// modify iaa to have the correct values.
+
+		if ( iaa->Length >= sizeof( IP_ADAPTER_ADDRESSES ) )
+		{
+			ipv6IfIndex = iaa->Ipv6IfIndex;
+		}
+		else
+		{
+			ipv6IfIndex	= 0;
+		}
+
+		// Skip psuedo and tunnel interfaces.
+		
+		if( ( ipv6IfIndex == 1 ) || ( iaa->IfType == IF_TYPE_TUNNEL ) )
+		{
+			continue;
+		}
+
+		if ( iaa->IfIndex == ifIndex )
+		{
+			scopeId = iaa->Ipv6IfIndex;
+			break;
+		}
+	} 
+
+exit:
+
+	if( iaaList )
+	{
+		free( iaaList );
+	}
+
+	return scopeId;
+}
diff --git a/mdnsresponder/mDNSWindows/mdnsNSP/mdnsNSP.def b/mdnsresponder/mDNSWindows/mdnsNSP/mdnsNSP.def
new file mode 100644
index 0000000..822213d
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/mdnsNSP/mdnsNSP.def
@@ -0,0 +1,24 @@
+; -*- tab-width: 4 -*-
+;
+; Copyright (c) 2003-2004 Apple Computer, Inc. All rights reserved.
+;
+; Licensed 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.
+;
+
+LIBRARY		mdnsNSP
+
+EXPORTS
+	NSPStartup
+	NSPCleanup
+	DllRegisterServer	PRIVATE
+	DllUnregisterServer	PRIVATE
diff --git a/mdnsresponder/mDNSWindows/mdnsNSP/mdnsNSP.rc b/mdnsresponder/mDNSWindows/mdnsNSP/mdnsNSP.rc
new file mode 100644
index 0000000..c090f0c
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/mdnsNSP/mdnsNSP.rc
@@ -0,0 +1,104 @@
+// Microsoft Visual C++ generated resource script.
+//
+#include "resource.h"
+
+#define APSTUDIO_READONLY_SYMBOLS
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 2 resource.
+//
+#include "afxres.h"
+#include "WinVersRes.h"
+
+/////////////////////////////////////////////////////////////////////////////
+#undef APSTUDIO_READONLY_SYMBOLS
+
+/////////////////////////////////////////////////////////////////////////////
+// English (U.S.) resources
+
+#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
+#ifdef _WIN32
+LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
+#pragma code_page(1252)
+#endif //_WIN32
+
+#ifdef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// TEXTINCLUDE
+//
+
+1 TEXTINCLUDE 
+BEGIN
+    "resource.h\0"
+END
+
+2 TEXTINCLUDE 
+BEGIN
+    "#include ""afxres.h""\r\n"
+    "#include ""WinVersRes.h""\r\n"
+    "\0"
+END
+
+3 TEXTINCLUDE 
+BEGIN
+    "\r\n"
+    "\0"
+END
+
+#endif    // APSTUDIO_INVOKED
+
+
+/////////////////////////////////////////////////////////////////////////////
+//
+// Version
+//
+
+VS_VERSION_INFO VERSIONINFO
+ FILEVERSION MASTER_PROD_VERS
+ PRODUCTVERSION MASTER_PROD_VERS
+ FILEFLAGSMASK 0x17L
+#ifdef _DEBUG
+ FILEFLAGS 0x1L
+#else
+ FILEFLAGS 0x0L
+#endif
+ FILEOS 0x4L
+ FILETYPE 0x2L
+ FILESUBTYPE 0x0L
+BEGIN
+    BLOCK "StringFileInfo"
+    BEGIN
+        BLOCK "040904b0"
+        BEGIN
+            VALUE "CompanyName", MASTER_COMPANY_NAME
+            VALUE "FileDescription", "Bonjour Namespace Provider"
+            VALUE "FileVersion", MASTER_PROD_VERS_STR
+            VALUE "InternalName", "mdnsNSP.dll"
+            VALUE "LegalCopyright", MASTER_LEGAL_COPYRIGHT
+            VALUE "OriginalFilename", "mdnsNSP.dll"
+            VALUE "ProductName", MASTER_PROD_NAME
+            VALUE "ProductVersion", MASTER_PROD_VERS_STR
+        END
+    END
+    BLOCK "VarFileInfo"
+    BEGIN
+        VALUE "Translation", 0x409, 1200
+    END
+END
+
+#endif    // English (U.S.) resources
+/////////////////////////////////////////////////////////////////////////////
+
+
+
+#ifndef APSTUDIO_INVOKED
+/////////////////////////////////////////////////////////////////////////////
+//
+// Generated from the TEXTINCLUDE 3 resource.
+//
+
+
+/////////////////////////////////////////////////////////////////////////////
+#endif    // not APSTUDIO_INVOKED
+
diff --git a/mdnsresponder/mDNSWindows/mdnsNSP/mdnsNSP.vcproj b/mdnsresponder/mDNSWindows/mdnsNSP/mdnsNSP.vcproj
new file mode 100644
index 0000000..1d6f563
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/mdnsNSP/mdnsNSP.vcproj
@@ -0,0 +1,457 @@
+<?xml version="1.0" encoding="Windows-1252"?>

+<VisualStudioProject

+	ProjectType="Visual C++"

+	Version="8.00"

+	Name="mdnsNSP"

+	ProjectGUID="{F4F15529-F0EB-402F-8662-73C5797EE557}"

+	Keyword="Win32Proj"

+	>

+	<Platforms>

+		<Platform

+			Name="Win32"

+		/>

+		<Platform

+			Name="x64"

+		/>

+	</Platforms>

+	<ToolFiles>

+	</ToolFiles>

+	<Configurations>

+		<Configuration

+			Name="Debug|Win32"

+			OutputDirectory="$(PlatformName)\$(ConfigurationName)"

+			IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"

+			ConfigurationType="2"

+			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"

+			CharacterSet="2"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				Optimization="0"

+				AdditionalIncludeDirectories=".;../;../../mDNSShared;../../Clients"

+				PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;NSP_EXPORTS;DEBUG;WIN32_LEAN_AND_MEAN;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1"

+				StringPooling="true"

+				MinimalRebuild="true"

+				ExceptionHandling="0"

+				BasicRuntimeChecks="3"

+				SmallerTypeCheck="true"

+				RuntimeLibrary="1"

+				BufferSecurityCheck="true"

+				UsePrecompiledHeader="0"

+				AssemblerListingLocation="$(IntDir)\"

+				WarningLevel="4"

+				Detect64BitPortabilityProblems="true"

+				DebugInformationFormat="3"

+				CallingConvention="2"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+				AdditionalIncludeDirectories="../"

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				AdditionalOptions="/NXCOMPAT /DYNAMICBASE /SAFESEH"

+				AdditionalDependencies="../DLL/$(PlatformName)/$(ConfigurationName)/dnssd.lib ws2_32.lib iphlpapi.lib shlwapi.lib"

+				OutputFile="$(OutDir)/mdnsNSP.dll"

+				LinkIncremental="2"

+				ModuleDefinitionFile="mdnsNSP.def"

+				DelayLoadDLLs="dnssd.dll"

+				GenerateDebugInformation="true"

+				ProgramDatabaseFile="$(OutDir)/mdnsNSP.pdb"

+				SubSystem="2"

+				BaseAddress="0x64000000"

+				ImportLibrary="$(OutDir)/mdnsNSP.lib"

+				TargetMachine="1"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+			/>

+		</Configuration>

+		<Configuration

+			Name="Debug|x64"

+			OutputDirectory="$(PlatformName)\$(ConfigurationName)"

+			IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"

+			ConfigurationType="2"

+			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"

+			CharacterSet="2"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+				TargetEnvironment="3"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				Optimization="0"

+				AdditionalIncludeDirectories=".;../;../../mDNSShared;../../Clients"

+				PreprocessorDefinitions="WIN32;_DEBUG;_WINDOWS;_USRDLL;NSP_EXPORTS;DEBUG;WIN32_LEAN_AND_MEAN;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1"

+				StringPooling="true"

+				MinimalRebuild="true"

+				ExceptionHandling="0"

+				BasicRuntimeChecks="3"

+				SmallerTypeCheck="true"

+				RuntimeLibrary="1"

+				BufferSecurityCheck="true"

+				UsePrecompiledHeader="0"

+				AssemblerListingLocation="$(IntDir)\"

+				WarningLevel="4"

+				Detect64BitPortabilityProblems="true"

+				DebugInformationFormat="3"

+				CallingConvention="2"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+				AdditionalIncludeDirectories="../"

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				AdditionalOptions="/NXCOMPAT /DYNAMICBASE"

+				AdditionalDependencies="../DLL/$(PlatformName)/$(ConfigurationName)/dnssd.lib ws2_32.lib iphlpapi.lib shlwapi.lib"

+				OutputFile="$(OutDir)/mdnsNSP.dll"

+				LinkIncremental="2"

+				ModuleDefinitionFile="mdnsNSP.def"

+				DelayLoadDLLs="dnssd.dll"

+				GenerateDebugInformation="true"

+				ProgramDatabaseFile="$(OutDir)/mdnsNSP.pdb"

+				SubSystem="2"

+				BaseAddress="0x64000000"

+				ImportLibrary="$(OutDir)/mdnsNSP.lib"

+				TargetMachine="17"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+			/>

+		</Configuration>

+		<Configuration

+			Name="Release|Win32"

+			OutputDirectory="$(PlatformName)\$(ConfigurationName)"

+			IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"

+			ConfigurationType="2"

+			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"

+			CharacterSet="2"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				AdditionalIncludeDirectories=".;../;../../mDNSShared;../../Clients"

+				PreprocessorDefinitions="WIN32;_WINDOWS;_USRDLL;NSP_EXPORTS;WIN32_LEAN_AND_MEAN;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1"

+				StringPooling="true"

+				MinimalRebuild="true"

+				ExceptionHandling="0"

+				BasicRuntimeChecks="0"

+				SmallerTypeCheck="false"

+				RuntimeLibrary="0"

+				UsePrecompiledHeader="0"

+				AssemblerListingLocation="$(IntDir)\"

+				WarningLevel="4"

+				Detect64BitPortabilityProblems="true"

+				DebugInformationFormat="3"

+				CallingConvention="2"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+				AdditionalIncludeDirectories="../"

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				AdditionalOptions="/NXCOMPAT /DYNAMICBASE /SAFESEH"

+				AdditionalDependencies="../DLL/$(PlatformName)/$(ConfigurationName)/dnssd.lib ws2_32.lib iphlpapi.lib shlwapi.lib"

+				OutputFile="$(OutDir)/mdnsNSP.dll"

+				LinkIncremental="1"

+				ModuleDefinitionFile="mdnsNSP.def"

+				DelayLoadDLLs="dnssd.dll"

+				GenerateDebugInformation="true"

+				ProgramDatabaseFile="$(IntDir)/$(ProjectName).pdb"

+				SubSystem="2"

+				OptimizeReferences="0"

+				EnableCOMDATFolding="0"

+				BaseAddress="0x64000000"

+				ImportLibrary="$(IntDir)/mdnsNSP.lib"

+				TargetMachine="1"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+				CommandLine="if not &quot;%RC_XBS%&quot; == &quot;YES&quot; goto END&#x0D;&#x0A;if not exist &quot;$(DSTROOT)\Program Files\Bonjour\$(PlatformName)&quot;   mkdir &quot;$(DSTROOT)\Program Files\Bonjour\$(PlatformName)&quot;&#x0D;&#x0A;xcopy /I/Y &quot;$(TargetPath)&quot;                                                                  &quot;$(DSTROOT)\Program Files\Bonjour\$(PlatformName)&quot;&#x0D;&#x0A;:END&#x0D;&#x0A;"

+			/>

+		</Configuration>

+		<Configuration

+			Name="Release|x64"

+			OutputDirectory="$(PlatformName)\$(ConfigurationName)"

+			IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"

+			ConfigurationType="2"

+			InheritedPropertySheets="$(VCInstallDir)VCProjectDefaults\UpgradeFromVC71.vsprops"

+			CharacterSet="2"

+			>

+			<Tool

+				Name="VCPreBuildEventTool"

+			/>

+			<Tool

+				Name="VCCustomBuildTool"

+			/>

+			<Tool

+				Name="VCXMLDataGeneratorTool"

+			/>

+			<Tool

+				Name="VCWebServiceProxyGeneratorTool"

+			/>

+			<Tool

+				Name="VCMIDLTool"

+				TargetEnvironment="3"

+			/>

+			<Tool

+				Name="VCCLCompilerTool"

+				AdditionalIncludeDirectories=".;../;../../mDNSShared;../../Clients"

+				PreprocessorDefinitions="WIN32;_WINDOWS;_USRDLL;NSP_EXPORTS;WIN32_LEAN_AND_MEAN;_CRT_SECURE_NO_DEPRECATE;_CRT_SECURE_CPP_OVERLOAD_STANDARD_NAMES=1"

+				StringPooling="true"

+				MinimalRebuild="true"

+				ExceptionHandling="0"

+				BasicRuntimeChecks="0"

+				SmallerTypeCheck="false"

+				RuntimeLibrary="0"

+				UsePrecompiledHeader="0"

+				AssemblerListingLocation="$(IntDir)\"

+				WarningLevel="4"

+				Detect64BitPortabilityProblems="true"

+				DebugInformationFormat="3"

+				CallingConvention="2"

+			/>

+			<Tool

+				Name="VCManagedResourceCompilerTool"

+			/>

+			<Tool

+				Name="VCResourceCompilerTool"

+				AdditionalIncludeDirectories="../"

+			/>

+			<Tool

+				Name="VCPreLinkEventTool"

+			/>

+			<Tool

+				Name="VCLinkerTool"

+				AdditionalOptions="/NXCOMPAT /DYNAMICBASE"

+				AdditionalDependencies="../DLL/$(PlatformName)/$(ConfigurationName)/dnssd.lib ws2_32.lib iphlpapi.lib shlwapi.lib"

+				OutputFile="$(OutDir)/mdnsNSP.dll"

+				LinkIncremental="1"

+				ModuleDefinitionFile="mdnsNSP.def"

+				DelayLoadDLLs="dnssd.dll"

+				GenerateDebugInformation="true"

+				ProgramDatabaseFile="$(IntDir)/$(ProjectName).pdb"

+				SubSystem="2"

+				OptimizeReferences="0"

+				EnableCOMDATFolding="0"

+				BaseAddress="0x64000000"

+				ImportLibrary="$(IntDir)/mdnsNSP.lib"

+				TargetMachine="17"

+			/>

+			<Tool

+				Name="VCALinkTool"

+			/>

+			<Tool

+				Name="VCManifestTool"

+			/>

+			<Tool

+				Name="VCXDCMakeTool"

+			/>

+			<Tool

+				Name="VCBscMakeTool"

+			/>

+			<Tool

+				Name="VCFxCopTool"

+			/>

+			<Tool

+				Name="VCAppVerifierTool"

+			/>

+			<Tool

+				Name="VCWebDeploymentTool"

+			/>

+			<Tool

+				Name="VCPostBuildEventTool"

+				CommandLine="if not &quot;%RC_XBS%&quot; == &quot;YES&quot; goto END&#x0D;&#x0A;if not exist &quot;$(DSTROOT)\Program Files\Bonjour\$(PlatformName)&quot;   mkdir &quot;$(DSTROOT)\Program Files\Bonjour\$(PlatformName)&quot;&#x0D;&#x0A;xcopy /I/Y &quot;$(TargetPath)&quot;                                                                  &quot;$(DSTROOT)\Program Files\Bonjour\$(PlatformName)&quot;&#x0D;&#x0A;:END&#x0D;&#x0A;"

+			/>

+		</Configuration>

+	</Configurations>

+	<References>

+	</References>

+	<Files>

+		<Filter

+			Name="Source Files"

+			Filter="cpp;c;cxx;def;odl;idl;hpj;bat;asm;asmx"

+			UniqueIdentifier="{4FC737F1-C7A5-4376-A066-2A32D752A2FF}"

+			>

+			<File

+				RelativePath="..\..\Clients\ClientCommon.c"

+				>

+			</File>

+			<File

+				RelativePath="..\..\mDNSShared\DebugServices.c"

+				>

+			</File>

+			<File

+				RelativePath=".\mdnsNSP.c"

+				>

+			</File>

+			<File

+				RelativePath=".\mdnsNSP.def"

+				>

+			</File>

+		</Filter>

+		<Filter

+			Name="Header Files"

+			Filter="h;hpp;hxx;hm;inl;inc;xsd"

+			UniqueIdentifier="{93995380-89BD-4b04-88EB-625FBE52EBFB}"

+			>

+			<File

+				RelativePath="..\..\Clients\ClientCommon.h"

+				>

+			</File>

+			<File

+				RelativePath="..\..\mDNSShared\CommonServices.h"

+				>

+			</File>

+			<File

+				RelativePath="..\..\mDNSShared\DebugServices.h"

+				>

+			</File>

+			<File

+				RelativePath="..\..\..\mDNSShared\dns_sd.h"

+				>

+			</File>

+			<File

+				RelativePath=".\resource.h"

+				>

+			</File>

+		</Filter>

+		<Filter

+			Name="Resource Files"

+			Filter="rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx"

+			UniqueIdentifier="{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}"

+			>

+			<File

+				RelativePath=".\mdnsNSP.rc"

+				>

+			</File>

+		</Filter>

+	</Files>

+	<Globals>

+	</Globals>

+</VisualStudioProject>

diff --git a/mdnsresponder/mDNSWindows/mdnsNSP/resource.h b/mdnsresponder/mDNSWindows/mdnsNSP/resource.h
new file mode 100644
index 0000000..818f083
--- /dev/null
+++ b/mdnsresponder/mDNSWindows/mdnsNSP/resource.h
@@ -0,0 +1,27 @@
+//{{NO_DEPENDENCIES}}
+// Microsoft Visual C++ generated include file.
+// Used by mdnsNSP.rc
+//
+#define IDS_PROJNAME                    100
+#define IDR_WMDMLOGGER                  101
+#define IDS_LOG_SEV_INFO                201
+#define IDS_LOG_SEV_WARN                202
+#define IDS_LOG_SEV_ERROR               203
+#define IDS_LOG_DATETIME                204
+#define IDS_LOG_SRCNAME                 205
+#define IDS_DEF_LOGFILE                 301
+#define IDS_DEF_MAXSIZE                 302
+#define IDS_DEF_SHRINKTOSIZE            303
+#define IDS_DEF_LOGENABLED              304
+#define IDS_MUTEX_TIMEOUT               401
+
+// Next default values for new objects
+// 
+#ifdef APSTUDIO_INVOKED
+#ifndef APSTUDIO_READONLY_SYMBOLS
+#define _APS_NEXT_RESOURCE_VALUE        201
+#define _APS_NEXT_COMMAND_VALUE         32768
+#define _APS_NEXT_CONTROL_VALUE         201
+#define _APS_NEXT_SYMED_VALUE           101
+#endif
+#endif
diff --git a/mdnsresponder/mdnsd.rc b/mdnsresponder/mdnsd.rc
new file mode 100644
index 0000000..0696ac5
--- /dev/null
+++ b/mdnsresponder/mdnsd.rc
@@ -0,0 +1,7 @@
+service mdnsd /system/bin/mdnsd
+    class main
+    user mdnsr
+    group inet
+    socket mdnsd stream 0660 mdnsr inet
+    disabled
+    oneshot