summaryrefslogtreecommitdiff
path: root/usr
diff options
context:
space:
mode:
authorrs200217 <none@none>2007-08-20 11:44:22 -0700
committerrs200217 <none@none>2007-08-20 11:44:22 -0700
commit4b22b9337f359bfd063322244f5336cc7c6ffcfa (patch)
treed935e6adb14cc353974e0fcf2dc89a47f18b9971 /usr
parentd96018ff0218b4466f62ea165b3dfd64ef271965 (diff)
downloadillumos-gate-4b22b9337f359bfd063322244f5336cc7c6ffcfa.tar.gz
PSARC 2005/562 Multicast DNS and Service Discovery
6550311 Multicast DNS and service discovery support
Diffstat (limited to 'usr')
-rw-r--r--usr/src/cmd/Makefile1
-rw-r--r--usr/src/cmd/Makefile.cmd1
-rw-r--r--usr/src/cmd/cmd-inet/etc/services2
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/Makefile6
-rw-r--r--usr/src/cmd/cmd-inet/usr.bin/dns-sd.c805
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/Makefile3
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mdnsd/DNSCommon.c2181
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mdnsd/DNSCommon.h368
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mdnsd/DNSDigest.c1465
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mdnsd/GenLinkedList.c334
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mdnsd/GenLinkedList.h109
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mdnsd/Makefile83
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mdnsd/PlatformCommon.c138
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mdnsd/PlatformCommon.h37
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mdnsd/PosixDaemon.c369
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNS.c7362
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSDebug.c152
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSDebug.h192
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSEmbeddedAPI.h2850
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSPosix.c1771
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSPosix.h147
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSUNP.c1102
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSUNP.h190
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mdnsd/multicast.xml144
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mdnsd/uDNS.c5253
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mdnsd/uDNS.h192
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mdnsd/uds_daemon.c3695
-rw-r--r--usr/src/cmd/cmd-inet/usr.lib/mdnsd/uds_daemon.h118
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.h2
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_dns.c50
-rw-r--r--usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rport.c20
-rw-r--r--usr/src/cmd/netfiles/nsswitch.dns11
-rw-r--r--usr/src/cmd/nscd/nscd_cfgdef.h12
-rw-r--r--usr/src/cmd/nscd/nscd_switch.c99
-rw-r--r--usr/src/cmd/nscd/nscd_switch.h1
-rw-r--r--usr/src/lib/Makefile2
-rw-r--r--usr/src/lib/libdns_sd/Makefile59
-rw-r--r--usr/src/lib/libdns_sd/Makefile.com51
-rw-r--r--usr/src/lib/libdns_sd/README65
-rw-r--r--usr/src/lib/libdns_sd/amd64/Makefile30
-rw-r--r--usr/src/lib/libdns_sd/common/dns_sd.h1725
-rw-r--r--usr/src/lib/libdns_sd/common/dnssd_clientlib.c373
-rw-r--r--usr/src/lib/libdns_sd/common/dnssd_clientstub.c1249
-rw-r--r--usr/src/lib/libdns_sd/common/dnssd_ipc.c135
-rw-r--r--usr/src/lib/libdns_sd/common/dnssd_ipc.h253
-rw-r--r--usr/src/lib/libdns_sd/common/llib-ldns_sd32
-rw-r--r--usr/src/lib/libdns_sd/common/mapfile-vers56
-rw-r--r--usr/src/lib/libdns_sd/i386/Makefile28
-rw-r--r--usr/src/lib/libdns_sd/java/Makefile47
-rw-r--r--usr/src/lib/libdns_sd/java/Makefile.com57
-rw-r--r--usr/src/lib/libdns_sd/java/amd64/Makefile32
-rw-r--r--usr/src/lib/libdns_sd/java/com/apple/Makefile47
-rw-r--r--usr/src/lib/libdns_sd/java/com/apple/Makefile.com31
-rw-r--r--usr/src/lib/libdns_sd/java/com/apple/dnssd/BaseListener.java51
-rw-r--r--usr/src/lib/libdns_sd/java/com/apple/dnssd/BrowseListener.java88
-rw-r--r--usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSRecord.java68
-rw-r--r--usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSD.java897
-rw-r--r--usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDException.java76
-rw-r--r--usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDRecordRegistrar.java79
-rw-r--r--usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDRegistration.java77
-rw-r--r--usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDService.java52
-rw-r--r--usr/src/lib/libdns_sd/java/com/apple/dnssd/DomainListener.java75
-rw-r--r--usr/src/lib/libdns_sd/java/com/apple/dnssd/Makefile150
-rw-r--r--usr/src/lib/libdns_sd/java/com/apple/dnssd/QueryListener.java71
-rw-r--r--usr/src/lib/libdns_sd/java/com/apple/dnssd/RegisterListener.java64
-rw-r--r--usr/src/lib/libdns_sd/java/com/apple/dnssd/RegisterRecordListener.java49
-rw-r--r--usr/src/lib/libdns_sd/java/com/apple/dnssd/ResolveListener.java71
-rw-r--r--usr/src/lib/libdns_sd/java/com/apple/dnssd/TXTRecord.java313
-rw-r--r--usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/BrowserApp.java400
-rw-r--r--usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/BrowserApp.manifest3
-rw-r--r--usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SimpleChat.java336
-rw-r--r--usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SimpleChat.manifest3
-rw-r--r--usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SwingBrowseListener.java126
-rw-r--r--usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SwingDomainListener.java119
-rw-r--r--usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SwingQueryListener.java111
-rw-r--r--usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SwingResolveListener.java109
-rw-r--r--usr/src/lib/libdns_sd/java/common/JNISupport.c1107
-rw-r--r--usr/src/lib/libdns_sd/java/common/mapfile-vers48
-rw-r--r--usr/src/lib/libdns_sd/java/i386/Makefile35
-rw-r--r--usr/src/lib/libdns_sd/java/sparc/Makefile35
-rw-r--r--usr/src/lib/libdns_sd/java/sparcv9/Makefile32
-rw-r--r--usr/src/lib/libdns_sd/sparc/Makefile28
-rw-r--r--usr/src/lib/libdns_sd/sparcv9/Makefile35
-rw-r--r--usr/src/lib/libsecdb/auth_attr.txt2
-rw-r--r--usr/src/lib/libsecdb/help/auths/Makefile2
-rw-r--r--usr/src/lib/libsecdb/help/auths/SmfMDNSStates.html40
-rw-r--r--usr/src/lib/libsecdb/help/auths/SmfValueMDNS.html40
-rw-r--r--usr/src/lib/libsecdb/prof_attr.txt2
-rw-r--r--usr/src/lib/nsswitch/Makefile9
-rw-r--r--usr/src/lib/nsswitch/mdns/Makefile45
-rw-r--r--usr/src/lib/nsswitch/mdns/Makefile.com42
-rw-r--r--usr/src/lib/nsswitch/mdns/amd64/Makefile34
-rw-r--r--usr/src/lib/nsswitch/mdns/common/gethostent.c153
-rw-r--r--usr/src/lib/nsswitch/mdns/common/gethostent6.c174
-rw-r--r--usr/src/lib/nsswitch/mdns/common/mapfile-vers36
-rw-r--r--usr/src/lib/nsswitch/mdns/common/mdns_common.c763
-rw-r--r--usr/src/lib/nsswitch/mdns/common/mdns_common.h107
-rw-r--r--usr/src/lib/nsswitch/mdns/i386/Makefile34
-rw-r--r--usr/src/lib/nsswitch/mdns/sparc/Makefile34
-rw-r--r--usr/src/lib/nsswitch/mdns/sparcv9/Makefile35
-rw-r--r--usr/src/pkgdefs/Makefile2
-rw-r--r--usr/src/pkgdefs/SUNW0on/prototype_com2
-rw-r--r--usr/src/pkgdefs/SUNWcsu/prototype_com2
-rw-r--r--usr/src/pkgdefs/SUNWdsdr/Makefile37
-rw-r--r--usr/src/pkgdefs/SUNWdsdr/depend54
-rw-r--r--usr/src/pkgdefs/SUNWdsdr/pkginfo.tmpl50
-rw-r--r--usr/src/pkgdefs/SUNWdsdr/prototype_com41
-rw-r--r--usr/src/pkgdefs/SUNWdsdr/prototype_i38627
-rw-r--r--usr/src/pkgdefs/SUNWdsdr/prototype_sparc27
-rw-r--r--usr/src/pkgdefs/SUNWdsdu/Makefile37
-rw-r--r--usr/src/pkgdefs/SUNWdsdu/copyright43
-rw-r--r--usr/src/pkgdefs/SUNWdsdu/depend53
-rw-r--r--usr/src/pkgdefs/SUNWdsdu/pkginfo.tmpl50
-rw-r--r--usr/src/pkgdefs/SUNWdsdu/prototype_com55
-rw-r--r--usr/src/pkgdefs/SUNWdsdu/prototype_doc66
-rw-r--r--usr/src/pkgdefs/SUNWdsdu/prototype_i38636
-rw-r--r--usr/src/pkgdefs/SUNWdsdu/prototype_sparc36
-rw-r--r--usr/src/uts/common/netinet/in.h1
118 files changed, 40345 insertions, 71 deletions
diff --git a/usr/src/cmd/Makefile b/usr/src/cmd/Makefile
index 14950a9cb5..036049287c 100644
--- a/usr/src/cmd/Makefile
+++ b/usr/src/cmd/Makefile
@@ -818,6 +818,7 @@ MANIFEST_SUBDIRS= \
cmd-inet/usr.lib/in.ripngd \
cmd-inet/usr.lib/in.timed \
cmd-inet/usr.lib/inetd \
+ cmd-inet/usr.lib/mdnsd \
cmd-inet/usr.lib/slpd \
cmd-inet/usr.lib/wpad \
cmd-inet/usr.sbin \
diff --git a/usr/src/cmd/Makefile.cmd b/usr/src/cmd/Makefile.cmd
index 6a3cccfdb6..89d085da4d 100644
--- a/usr/src/cmd/Makefile.cmd
+++ b/usr/src/cmd/Makefile.cmd
@@ -236,6 +236,7 @@ ROOTSVCSYSTEMDEVICE= $(ROOTSVCSYSTEM)/device
ROOTSVCSYSTEMFILESYSTEM= $(ROOTSVCSYSTEM)/filesystem
ROOTSVCSYSTEMSECURITY= $(ROOTSVCSYSTEM)/security
ROOTSVCNETWORK= $(ROOTVARSVCMANIFEST)/network
+ROOTSVCNETWORKDNS= $(ROOTSVCNETWORK)/dns
ROOTSVCNETWORKLDAP= $(ROOTSVCNETWORK)/ldap
ROOTSVCNETWORKNFS= $(ROOTSVCNETWORK)/nfs
ROOTSVCNETWORKNIS= $(ROOTSVCNETWORK)/nis
diff --git a/usr/src/cmd/cmd-inet/etc/services b/usr/src/cmd/cmd-inet/etc/services
index c5a9e591ba..a39fbf5427 100644
--- a/usr/src/cmd/cmd-inet/etc/services
+++ b/usr/src/cmd/cmd-inet/etc/services
@@ -136,5 +136,7 @@ nfsd 2049/tcp nfs # NFS server daemon (cots)
eklogin 2105/tcp # Kerberos encrypted rlogin
lockd 4045/udp # NFS lock daemon/manager
lockd 4045/tcp
+mdns 5353/udp # Multicast DNS
+mdns 5353/tcp
dtspc 6112/tcp # CDE subprocess control
fs 7100/tcp # Font server
diff --git a/usr/src/cmd/cmd-inet/usr.bin/Makefile b/usr/src/cmd/cmd-inet/usr.bin/Makefile
index 1f418ae70a..4a120cc7f3 100644
--- a/usr/src/cmd/cmd-inet/usr.bin/Makefile
+++ b/usr/src/cmd/cmd-inet/usr.bin/Makefile
@@ -19,13 +19,13 @@
# CDDL HEADER END
#
#
-# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# ident "%Z%%M% %I% %E% SMI"
#
-PROG= finger rdate ruptime rwho whois
+PROG= dns-sd finger rdate ruptime rwho whois
SUIDPROG= rcp rlogin rsh
ALL= $(PROG) $(SUIDPROG)
SRCS= $(ALL:%=%.c)
@@ -72,10 +72,12 @@ CPPFLAGS += -DSYSV -DSTRNET -DBSD_COMP -I$(CMDINETCOMMONDIR)
# PROGS are lint clean.
$(LINTCLEAN) := CFLAGS += $(CCVERBOSE)
+dns-sd := CFLAGS += $(C99_ENABLE)
finger := CFLAGS += $(CCVERBOSE)
# Enable large file support for reading the lastlog file.
finger := CPPFLAGS += -D_FILE_OFFSET_BITS=64
+dns-sd := LDLIBS += -lsocket -ldns_sd
finger := LDLIBS += -lnsl -lcurses -lsocket
rcp lint-rcp := LDLIBS += -lsocket -lsec -lsendfile
rdate lint-rdate:= LDLIBS += -lsocket
diff --git a/usr/src/cmd/cmd-inet/usr.bin/dns-sd.c b/usr/src/cmd/cmd-inet/usr.bin/dns-sd.c
new file mode 100644
index 0000000000..aa123c921c
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.bin/dns-sd.c
@@ -0,0 +1,805 @@
+/* -*- Mode: C; tab-width: 4 -*-
+ *
+ * Copyright (c) 2002-2006 Apple Computer, 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)
+*/
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+// For testing changes to dnssd_clientstub.c, uncomment this line and the #include below
+// #define __APPLE_API_PRIVATE 1
+
+#include "dns_sd.h"
+#include <ctype.h>
+#include <stdio.h> // For stdout, stderr
+#include <stdlib.h> // For exit()
+#include <string.h> // For strlen(), strcpy(), bzero()
+#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 <process.h>
+typedef int pid_t;
+#define getpid _getpid
+#define strcasecmp _stricmp
+#define snprintf _snprintf
+static const char kFilePathSep = '\\';
+#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()
+static const char kFilePathSep = '/';
+#endif
+
+//#include "../mDNSShared/dnssd_clientstub.c"
+
+//*************************************************************************************************************
+// 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 client2 = NULL;
+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[8200];
+
+// 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;
+
+//*************************************************************************************************************
+// Supporting Utility Function
+
+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));
+ }
+
+//*************************************************************************************************************
+// 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")
+
+static 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 == '\\')
+ {
+ 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 (val <= 255) { c = (char)val; cstr += 2; } // If valid three-digit decimal value, use it
+ }
+ }
+ *ptr++ = c;
+ if (ptr >= label+64) return(NULL);
+ }
+ if (*cstr) cstr++; // Skip over the trailing dot (if present)
+ *ptr++ = 0;
+ return(cstr);
+ }
+
+static void DNSSD_API enum_reply(DNSServiceRef client, 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[128];
+
+ (void)client; // Unused
+ (void)ifIndex; // Unused
+ (void)context; // Unused
+
+ // 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)
+ {
+ 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 void DNSSD_API browse_reply(DNSServiceRef client, 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)client; // Unused
+ (void)context; // Unused
+ 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);
+ }
+
+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 client, 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)client; // Unused
+ (void)ifIndex; // Unused
+ (void)context; // Unused
+
+ printtimestamp();
+ if (errorCode) printf("Error code %d\n", errorCode);
+ else
+ {
+ printf("%s can be reached at %s:%u", fullname, hosttarget, PortAsNumber);
+ 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];
+ 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);
+ timeOut = LONG_TIME;
+ }
+ break;
+ }
+
+ if (err != kDNSServiceErr_NoError)
+ {
+ fprintf(stderr, "DNSService call failed %ld\n", (long int)err);
+ stopNow = 1;
+ }
+ }
+
+static void DNSSD_API reg_reply(DNSServiceRef client, const DNSServiceFlags flags, DNSServiceErrorType errorCode,
+ const char *name, const char *regtype, const char *domain, void *context)
+ {
+ (void)client; // Unused
+ (void)flags; // Unused
+ (void)context; // Unused
+
+ printf("Got a reply for %s.%s%s: ", name, regtype, domain);
+
+ if (errorCode == kDNSServiceErr_NoError)
+ {
+ printf("Name now registered and active\n");
+ if (operation == 'A' || operation == 'U' || operation == 'N') timeOut = 5;
+ }
+ 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);
+ }
+
+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];
+ int unknowntype = 0;
+
+ (void)sdRef; // Unused
+ (void)flags; // Unused
+ (void)ifIndex; // Unused
+ (void)ttl; // Unused
+ (void)context; // Unused
+
+ if (num_printed++ == 0) printf("Timestamp A/R Flags if %-30s%4s%4s Rdata\n", "Name", "T", "C");
+ printtimestamp();
+ if (errorCode)
+ printf("Error code %d\n", errorCode);
+ else
+ {
+ switch (rrtype)
+ {
+ case kDNSServiceType_A: sprintf(rdb, "%d.%d.%d.%d", rd[0], rd[1], rd[2], rd[3]); break;
+ case kDNSServiceType_AAAA: sprintf(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;
+ break;
+ default : sprintf(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++);
+ printf("\n");
+
+ if (operation == 'C')
+ if (flags & kDNSServiceFlagsAdd)
+ DNSServiceReconfirmRecord(flags, ifIndex, fullname, rrtype, rrclass, rdlen, rdata);
+ }
+
+ if (!(flags & kDNSServiceFlagsMoreComing)) fflush(stdout);
+ }
+
+//*************************************************************************************************************
+// The main test function
+
+static void HandleEvents(void)
+ {
+ int dns_sd_fd = client ? DNSServiceRefSockFD(client ) : -1;
+ int dns_sd_fd2 = client2 ? DNSServiceRefSockFD(client2) : -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 (client2) 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 (client2 && FD_ISSET(dns_sd_fd2, &readfds)) err = DNSServiceProcessResult(client2);
+ 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;
+ }
+ }
+ }
+
+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 operation = getopt(argc, (char * const *)argv, optstr);
+ *pOptInd = optind;
+ return operation;
+ }
+#endif
+
+static void DNSSD_API MyRegisterRecordCallback(DNSServiceRef service, DNSRecordRef record, const DNSServiceFlags flags,
+ DNSServiceErrorType errorCode, void * context)
+ {
+ char *name = (char *)context;
+
+ (void)service; // Unused
+ (void)record; // Unused
+ (void)flags; // Unused
+
+ printf("Got a reply for %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);
+ }
+
+static unsigned long getip(const char *const name)
+ {
+ unsigned long ip = 0;
+ struct addrinfo hints;
+ struct addrinfo * addrs = NULL;
+
+ memset(&hints, 0, sizeof(hints));
+ hints.ai_family = AF_INET;
+
+ if (getaddrinfo(name, NULL, &hints, &addrs) == 0)
+ {
+ ip = ((struct sockaddr_in*) addrs->ai_addr)->sin_addr.s_addr;
+ }
+
+ if (addrs)
+ {
+ freeaddrinfo(addrs);
+ }
+
+ return(ip);
+ }
+
+static DNSServiceErrorType RegisterProxyAddressRecord(DNSServiceRef *sdRef, const char *host, const char *ip)
+ {
+ // 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.
+ unsigned long addr = 0;
+ DNSServiceErrorType err = DNSServiceCreateConnection(sdRef);
+ if (err) { fprintf(stderr, "DNSServiceCreateConnection returned %d\n", err); return(err); }
+ addr = getip(ip);
+ return(DNSServiceRegisterRecord(*sdRef, &record, kDNSServiceFlagsUnique, kDNSServiceInterfaceIndexAny, host,
+ kDNSServiceType_A, kDNSServiceClass_IN, sizeof(addr), &addr, 240, MyRegisterRecordCallback, (void*)host));
+ // Note, should probably add support for creating proxy AAAA records too, one day
+ }
+
+#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)
+ {
+ 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\n", 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;
+ }
+ ShowTXTRecord(ptr-txt, txt);
+ printf("\n");
+ }
+
+ return(DNSServiceRegister(sdRef, /* kDNSServiceFlagsAllowRemoteQuery */ 0, opinterface, nam, typ, dom, host, registerPort.NotAnInteger, (uint16_t) (ptr-txt), txt, reg_reply, NULL));
+ }
+
+int main(int argc, char **argv)
+ {
+ DNSServiceErrorType err;
+ char *dom;
+ int optind;
+
+ // 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 (argc > 1 && !strcmp(argv[1], "-lo"))
+ {
+ argc--;
+ argv++;
+ opinterface = kDNSServiceInterfaceIndexLocalOnly;
+ printf("Using LocalOnly\n");
+ }
+
+ if (argc > 2 && !strcmp(argv[1], "-i") && atoi(argv[2]))
+ {
+ opinterface = atoi(argv[2]);
+ argc -= 2;
+ argv += 2;
+ printf("Using interface %d\n", opinterface);
+ }
+
+ if (argc < 2) goto Fail; // Minimum command line is the command name and one argument
+ operation = getfirstoption( argc, argv, "EFBLRPQCAUNTMI", &optind);
+ if (operation == -1) goto Fail;
+
+ 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': if (argc < optind+1) goto Fail;
+ dom = (argc < optind+2) ? "" : argv[optind+1];
+ 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", argv[optind+0], dom[0] ? "." : "", dom);
+ err = DNSServiceBrowse(&client, 0, opinterface, argv[optind+0], dom, browse_reply, NULL);
+ break;
+
+ case 'L': if (argc < optind+2) goto Fail;
+ dom = (argc < optind+3) ? "local" : argv[optind+2];
+ 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[optind+0], argv[optind+1], dom);
+ err = DNSServiceResolve(&client, 0, opinterface, argv[optind+0], argv[optind+1], dom, (DNSServiceResolveReply)resolve_reply, NULL);
+ break;
+
+ case 'R': if (argc < optind+4) goto Fail;
+ err = RegisterService(&client, argv[optind+0], argv[optind+1], argv[optind+2], NULL, argv[optind+3], argc-(optind+4), argv+(optind+4));
+ break;
+
+ case 'P': if (argc < optind+6) goto Fail;
+ err = RegisterProxyAddressRecord(&client2, argv[optind+4], argv[optind+5]);
+ if (err) break;
+ err = RegisterService(&client, argv[optind+0], argv[optind+1], argv[optind+2], argv[optind+4], argv[optind+3], argc-(optind+6), argv+(optind+6));
+ break;
+
+ case 'Q':
+ case 'C': {
+ uint16_t rrtype, rrclass;
+ DNSServiceFlags flags = kDNSServiceFlagsReturnCNAME;
+ if (argc < optind+1) goto Fail;
+ rrtype = (argc <= optind+1) ? kDNSServiceType_A : GetRRType(argv[optind+1]);
+ rrclass = (argc <= optind+2) ? kDNSServiceClass_IN : atoi(argv[optind+2]);
+ if (rrtype == kDNSServiceType_TXT || rrtype == kDNSServiceType_PTR) flags |= kDNSServiceFlagsLongLivedQuery;
+ err = DNSServiceQueryRecord(&client, flags, opinterface, argv[optind+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, 0, opinterface, "Test", "_testdualtxt._tcp.", "", NULL, registerPort.NotAnInteger, sizeof(TXT1)-1, TXT1, reg_reply, NULL);
+ if (!err) err = DNSServiceAddRecord(client, &record, 0, 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;
+ }
+
+ 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 (client2) DNSServiceRefDeallocate(client2);
+ 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 -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);
+ 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);
+ return 0;
+ }
diff --git a/usr/src/cmd/cmd-inet/usr.lib/Makefile b/usr/src/cmd/cmd-inet/usr.lib/Makefile
index b6fb9957a0..4bc772a574 100644
--- a/usr/src/cmd/cmd-inet/usr.lib/Makefile
+++ b/usr/src/cmd/cmd-inet/usr.lib/Makefile
@@ -27,7 +27,8 @@
SUBDIRS= dhcp dsvclockd in.chargend in.daytimed \
in.discardd in.echod in.dhcpd in.mpathd in.ndpd \
- in.ripngd in.timed inetd ncaconfd pppoe slpd wanboot wpad
+ in.ripngd in.timed inetd mdnsd ncaconfd pppoe \
+ slpd wanboot wpad
MSGSUBDIRS= dsvclockd in.dhcpd inetd ncaconfd wanboot
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/DNSCommon.c b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/DNSCommon.c
new file mode 100644
index 0000000000..73801fc781
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/DNSCommon.c
@@ -0,0 +1,2181 @@
+/* -*- 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.
+
+ Change History (most recent first):
+
+$Log: DNSCommon.c,v $
+Revision 1.100.2.1 2006/08/29 06:24:22 cheshire
+Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
+
+Revision 1.100 2006/06/08 22:58:46 cheshire
+<rdar://problem/4335605> IPv6 link-local address prefix is FE80::/10, not FE80::/16
+
+Revision 1.99 2006/05/18 01:32:33 cheshire
+<rdar://problem/4472706> iChat: Lost connection with Bonjour
+(mDNSResponder insufficiently defensive against malformed browsing PTR responses)
+
+Revision 1.98 2006/03/19 17:00:58 cheshire
+Define symbol MaxMsg instead of using hard-coded constant value '80'
+
+Revision 1.97 2006/03/18 21:47:56 cheshire
+<rdar://problem/4073825> Improve logic for delaying packets after repeated interface transitions
+
+Revision 1.96 2006/03/10 21:51:42 cheshire
+<rdar://problem/4111464> After record update, old record sometimes remains in cache
+Split out SameRDataBody() into a separate routine so it can be called from other code
+
+Revision 1.95 2006/03/08 22:43:11 cheshire
+Use "localdomain" symbol instead of literal string
+
+Revision 1.94 2006/03/02 21:59:55 cheshire
+<rdar://problem/4395331> Spurious warning "GetLargeResourceRecord: m->rec appears to be already in use"
+Improve sanity checks & debugging support in GetLargeResourceRecord()
+
+Revision 1.93 2006/03/02 20:30:47 cheshire
+Improved GetRRDisplayString to also show priority, weight, and port for SRV records
+
+Revision 1.92 2005/09/16 21:06:49 cheshire
+Use mDNS_TimeNow_NoLock macro, instead of writing "mDNSPlatformRawTime() + m->timenow_adjust" all over the place
+
+Revision 1.91 2005/07/10 22:10:37 cheshire
+The getOptRdata routine implicitly assumes the destination ResourceRecord is large enough to
+hold MaximumRDSize bytes, but its parameter was a generic ResourceRecord, which need not be that
+large. Changing the parameter to a LargeCacheRecord makes it clearer what the routine requires.
+
+Revision 1.90 2005/03/21 00:33:51 shersche
+<rdar://problem/4021486> Fix build warnings on Win32 platform
+
+Revision 1.89 2005/03/17 18:59:38 ksekar
+<rdar://problem/4012279> Properly parse multiple LLQ Options per packet on Windows
+
+Revision 1.88 2005/03/16 00:42:32 ksekar
+<rdar://problem/4012279> Long-lived queries not working on Windows
+
+Revision 1.87 2005/02/25 04:21:00 cheshire
+<rdar://problem/4015377> mDNS -F returns the same domain multiple times with different casing
+
+Revision 1.86 2005/02/18 00:43:12 cheshire
+<rdar://problem/4010245> mDNSResponder should auto-truncate service names that are too long
+
+Revision 1.85 2005/02/10 22:35:17 cheshire
+<rdar://problem/3727944> Update name
+
+Revision 1.84 2005/02/03 00:44:38 cheshire
+<rdar://problem/3986663> DNSServiceUpdateRecord returns kDNSServiceErr_Invalid when rdlen=0, rdata=NULL
+
+Revision 1.83 2005/01/27 22:57:55 cheshire
+Fix compile errors on gcc4
+
+Revision 1.82 2005/01/19 03:27:03 cheshire
+<rdar://problem/3961051> CPU Spin in mDNSResponder
+GetNextScheduledEvent() needs to check LocalRecordReady()
+
+Revision 1.81 2004/12/18 03:13:45 cheshire
+<rdar://problem/3751638> kDNSServiceInterfaceIndexLocalOnly should return all local records
+
+Revision 1.80 2004/12/16 21:46:43 cheshire
+Add DNSTypeName case for kDNSType_SOA
+
+Revision 1.79 2004/12/16 21:38:37 cheshire
+Add DNSTypeName case for kDNSType_NS
+
+Revision 1.78 2004/12/16 21:27:37 ksekar
+Fixed build failures when compiled with verbose debugging messages
+
+Revision 1.77 2004/12/16 20:12:59 cheshire
+<rdar://problem/3324626> Cache memory management improvements
+
+Revision 1.76 2004/12/16 08:05:29 shersche
+Remove extranenous semicolons that cause compilation errors on Windows
+
+Revision 1.75 2004/12/15 02:11:22 ksekar
+<rdar://problem/3917317> Don't check for Dynamic DNS hostname uniqueness
+
+Revision 1.74 2004/12/09 22:49:15 ksekar
+<rdar://problem/3913653> Wide-Area Goodbyes broken
+
+Revision 1.73 2004/12/07 22:49:06 cheshire
+<rdar://problem/3908850> BIND doesn't allow zero-length TXT records
+
+Revision 1.72 2004/12/06 21:15:20 ksekar
+<rdar://problem/3884386> mDNSResponder crashed in CheckServiceRegistrations
+
+Revision 1.71 2004/12/04 02:12:45 cheshire
+<rdar://problem/3517236> mDNSResponder puts LargeCacheRecord on the stack
+
+Revision 1.70 2004/12/03 19:52:44 ksekar
+Use PutResourceRecordTTLJumbo for putDeletionRecord()
+
+Revision 1.69 2004/12/03 07:20:50 ksekar
+<rdar://problem/3674208> Wide-Area: Registration of large TXT record fails
+
+Revision 1.68 2004/11/24 00:10:43 cheshire
+<rdar://problem/3869241> For unicast operations, verify that service types are legal
+
+Revision 1.67 2004/10/26 03:52:02 cheshire
+Update checkin comments
+
+Revision 1.66 2004/10/23 01:16:00 cheshire
+<rdar://problem/3851677> uDNS operations not always reliable on multi-homed hosts
+
+Revision 1.65 2004/10/20 02:15:09 cheshire
+Add case in GetRRDisplayString() to display NS rdata
+
+Revision 1.64 2004/10/13 00:24:02 cheshire
+Disable "array is too small to include a terminating null character" warning on Windows
+
+Revision 1.63 2004/10/10 06:57:14 cheshire
+Change definition of "localdomain" to make code compile a little smaller
+
+Revision 1.62 2004/10/06 01:44:19 cheshire
+<rdar://problem/3813936> Resolving too quickly sometimes returns stale TXT record
+
+Revision 1.61 2004/09/30 00:24:56 ksekar
+<rdar://problem/3695802> Dynamically update default registration domains on config change
+
+Revision 1.60 2004/09/27 23:25:30 cheshire
+Fix compiler warning: soa.serial is signed, not unsigned
+
+Revision 1.59 2004/09/27 22:53:45 ksekar
+Fixed getLargeResourceRecord for SOA rdata.
+
+Revision 1.58 2004/09/25 02:41:39 cheshire
+<rdar://problem/3637266> Deliver near-pending "remove" events before new "add" events
+
+Revision 1.57 2004/09/25 02:24:27 cheshire
+Removed unused rr->UseCount
+
+Revision 1.56 2004/09/24 20:57:39 cheshire
+<rdar://problem/3680902> Eliminate inappropriate casts that cause misaligned-address errors
+
+Revision 1.55 2004/09/17 01:08:48 cheshire
+Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h
+ The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces
+ declared in that file are ONLY appropriate to single-address-space embedded applications.
+ For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used.
+
+Revision 1.54 2004/09/17 00:49:51 cheshire
+Get rid of now-unused GetResourceRecord -- the correct (safe) routine to use
+is GetLargeResourceRecord
+
+Revision 1.53 2004/09/17 00:31:51 cheshire
+For consistency with ipv6, renamed rdata field 'ip' to 'ipv4'
+
+Revision 1.52 2004/09/17 00:19:10 cheshire
+For consistency with AllDNSLinkGroupv6, rename AllDNSLinkGroup to AllDNSLinkGroupv4
+
+Revision 1.51 2004/09/16 02:29:39 cheshire
+Moved mDNS_Lock/mDNS_Unlock to DNSCommon.c; Added necessary locking around
+uDNS_ReceiveMsg, uDNS_StartQuery, uDNS_UpdateRecord, uDNS_RegisterService
+
+Revision 1.50 2004/09/16 01:58:14 cheshire
+Fix compiler warnings
+
+Revision 1.49 2004/09/14 23:42:35 cheshire
+<rdar://problem/3801296> Need to seed random number generator from platform-layer data
+
+Revision 1.48 2004/09/14 23:27:46 cheshire
+Fix compile errors
+
+Revision 1.47 2004/08/25 02:50:04 cheshire
+<rdar://problem/3561220> Browses are no longer piggybacking on other browses
+Make mDNSSameAddress() recognise that two mDNSAddrType_None addresses are necessarily equal
+
+Revision 1.46 2004/08/18 17:35:40 ksekar
+<rdar://problem/3651443>: Feature #9586: Need support for Legacy NAT gateways
+
+Revision 1.45 2004/08/15 18:26:00 cheshire
+Don't use strcpy() on "struct domainname" objects; use AssignDomainName() instead
+(A "struct domainname" is a collection of packed pascal strings, not a C string.)
+
+Revision 1.44 2004/08/13 23:46:58 cheshire
+"asyncronous" -> "asynchronous"
+
+Revision 1.43 2004/08/12 02:55:46 ksekar
+Fix param order error moving putPrereqNameNotInUse from uDNS.c using
+ustrcpy macro to DNSCommon.c using mDNSPlatformStrCopy().
+
+Revision 1.42 2004/08/10 23:19:14 ksekar
+<rdar://problem/3722542>: DNS Extension daemon for Wide Area Service Discovery
+Moved routines/constants to allow extern access for garbage collection daemon
+
+Revision 1.41 2004/08/10 01:10:01 cheshire
+<rdar://problem/3588761> Current method of doing subtypes causes name collisions
+Minor revision from Roger Pantos
+
+Revision 1.40 2004/08/04 22:10:46 cheshire
+<rdar://problem/3588761> Current method of doing subtypes causes name collisions
+Change to use "._sub." instead of ".s." to mark subtypes.
+
+Revision 1.39 2004/07/13 21:24:24 rpantos
+Fix for <rdar://problem/3701120>.
+
+Revision 1.38 2004/06/18 21:08:58 cheshire
+<rdar://problem/3540040> Applications are registering invalid records
+Attempts to create domain names like "www..apple.com." now logged to aid debugging
+
+Revision 1.37 2004/06/18 20:25:42 cheshire
+<rdar://problem/3488547> Add a syslog message if someone tries to use "local.arpa".
+
+Revision 1.36 2004/06/18 19:09:59 cheshire
+<rdar://problem/3588761> Current method of doing subtypes causes name collisions
+
+Revision 1.35 2004/06/05 00:14:44 cheshire
+Fix signed/unsigned and other compiler warnings
+
+Revision 1.34 2004/06/04 00:25:25 cheshire
+Fix misaligned write exception that occurs on some platforms
+
+Revision 1.33 2004/06/04 00:16:18 cheshire
+Remove non-portable use of 'inline'
+
+Revision 1.32 2004/06/03 03:09:58 ksekar
+<rdar://problem/3668626>: Garbage Collection for Dynamic Updates
+
+Revision 1.31 2004/05/28 23:42:36 ksekar
+<rdar://problem/3258021>: Feature: DNS server->client notification on record changes (#7805)
+
+Revision 1.30 2004/05/26 09:08:04 bradley
+Added cast to correct structure pointer when allocating domain name list element to fix C++ builds.
+
+Revision 1.29 2004/05/18 23:51:25 cheshire
+Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers
+
+Revision 1.28 2004/05/13 04:54:20 ksekar
+Unified list copy/free code. Added symetric list for
+
+Revision 1.27 2004/04/22 20:29:07 cheshire
+Log error message if no count field passed to PutResourceRecordTTL()
+
+Revision 1.26 2004/04/22 04:07:01 cheshire
+Fix from Bob Bradley: Don't try to do inline functions on compilers that don't support it
+
+Revision 1.25 2004/04/22 03:05:28 cheshire
+kDNSClass_ANY should be kDNSQClass_ANY
+
+Revision 1.24 2004/04/22 02:51:20 cheshire
+Use common code for HINFO/TXT and TSIG cases in putRData
+
+Revision 1.23 2004/04/15 00:51:28 bradley
+Minor tweaks for Windows and C++ builds. Added casts for signed/unsigned integers and 64-bit pointers.
+Prefix some functions with mDNS to avoid conflicts. Disable benign warnings on Microsoft compilers.
+
+Revision 1.22 2004/04/14 23:09:28 ksekar
+Support for TSIG signed dynamic updates.
+
+Revision 1.21 2004/04/09 16:47:28 cheshire
+<rdar://problem/3617655>: mDNSResponder escape handling inconsistent with BIND
+
+Revision 1.20 2004/04/09 16:37:15 cheshire
+Suggestion from Bob Bradley:
+Move NumCacheRecordsForInterfaceID() to DNSCommon.c so it's available to all platform layers
+
+Revision 1.19 2004/04/02 19:34:38 cheshire
+Fix broken comment
+
+Revision 1.18 2004/03/30 06:45:00 cheshire
+Compiler warning fixes from Don Woodward at Roku Labs
+
+Revision 1.17 2004/03/19 22:25:20 cheshire
+<rdar://problem/3579561>: Need to limit service types to fourteen characters
+Won't actually do this for now, but keep the code around just in case
+
+Revision 1.16 2004/03/08 02:45:35 cheshire
+Minor change to make a couple of the log messages a bit shorter
+
+Revision 1.15 2004/03/08 02:44:09 cheshire
+<rdar://problem/3579561>: Need to limit service types to fourteen characters
+
+Revision 1.14 2004/02/21 02:06:24 cheshire
+Can't use anonymous unions -- they're non-standard and don't work on all compilers
+
+Revision 1.13 2004/02/06 23:04:18 ksekar
+Basic Dynamic Update support via mDNS_Register (dissabled via
+UNICAST_REGISTRATION #define)
+
+Revision 1.12 2004/02/03 22:37:10 cheshire
+Delete unused (commented-out) code
+
+Revision 1.11 2004/02/03 22:35:34 cheshire
+<rdar://problem/3548256>: Should not allow empty string for resolve domain
+
+Revision 1.10 2004/02/03 19:47:36 ksekar
+Added an asynchronous state machine mechanism to uDNS.c, including
+calls to find the parent zone for a domain name. Changes include code
+in repository previously dissabled via "#if 0 incomplete". Codepath
+is currently unused, and will be called to create update records, etc.
+
+Revision 1.9 2004/01/27 20:15:22 cheshire
+<rdar://problem/3541288>: Time to prune obsolete code for listening on port 53
+
+Revision 1.8 2004/01/24 23:24:36 cheshire
+Expanded out the list of local domains to reduce risk of mistakes in future
+
+Revision 1.7 2004/01/24 08:32:30 bradley
+Mask values with 0xFF before casting to avoid runtime truncation errors on Windows debug builds.
+Separated octal-escaped sequences preceding decimal digits to avoid errors with some compilers wanting
+to signal potentially hidden errors about the subsequent digit not being part of the octal sequence.
+
+Revision 1.6 2004/01/24 04:59:15 cheshire
+Fixes so that Posix/Linux, OS9, Windows, and VxWorks targets build again
+
+Revision 1.5 2004/01/23 23:23:14 ksekar
+Added TCP support for truncated unicast messages.
+
+Revision 1.4 2004/01/22 02:15:33 cheshire
+<rdar://problem/3536597>: Link-local reverse-mapping domains need to be resolved using link-local multicast
+
+Revision 1.3 2004/01/21 21:16:29 cheshire
+Minor tidy-up: Deleted a bunch of blank lines, trailing spaces, tabs, etc.
+
+Revision 1.2 2003/12/13 05:47:48 bradley
+Made local ptr const to fix error when assigning from const structure. Disable benign conditional
+expression is constant warning when building with Microsoft compilers.
+
+Revision 1.1 2003/12/13 03:05:27 ksekar
+<rdar://problem/3192548>: DynDNS: Unicast query of service records
+
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+// 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 -
+#pragma mark - DNameList copy/deallocation routines
+#endif
+
+mDNSexport DNameListElem *mDNS_CopyDNameList(const DNameListElem *orig)
+ {
+ DNameListElem *copy = mDNSNULL, *newelem;
+ const DNameListElem *ptr;
+
+ for (ptr = orig; ptr; ptr = ptr->next)
+ {
+ newelem = (DNameListElem*)mDNSPlatformMemAllocate(sizeof(DNameListElem));
+ if (!newelem) { LogMsg("ERROR: malloc"); return mDNSNULL; }
+ AssignDomainName(&newelem->name, &ptr->name);
+ newelem->next = copy;
+ copy = newelem;
+ }
+ return copy;
+ }
+
+mDNSexport void mDNS_FreeDNameList(DNameListElem *list)
+ {
+ DNameListElem *fptr;
+
+ while (list)
+ {
+ fptr = list;
+ list = list->next;
+ mDNSPlatformMemFree(fptr);
+ }
+ }
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - General Utility Functions
+#endif
+
+// return true for RFC1918 private addresses
+mDNSexport mDNSBool IsPrivateV4Addr(mDNSAddr *addr)
+ {
+ mDNSu8 *b;
+
+ if (addr->type != mDNSAddrType_IPv4) return mDNSfalse;
+ b = addr->ip.v4.b;
+
+ return ((b[0] == 10) || // 10/8 prefix
+ (b[0] == 172 && b[1] > 15 && b[1] < 32) || // 172.16/12
+ (b[0] == 192 && b[1] == 168)); // 192.168/16
+ }
+
+mDNSexport const NetworkInterfaceInfo *GetFirstActiveInterface(const 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;
+ 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 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 *rr, RDataBody *rd, char *buffer)
+ {
+ #define Max (MaxMsg-1)
+ char *ptr = buffer;
+ mDNSu32 length = mDNS_snprintf(buffer, Max, "%4d %##s %s ", rr->rdlength, rr->name->c, DNSTypeName(rr->rrtype));
+ switch (rr->rrtype)
+ {
+ case kDNSType_A: mDNS_snprintf(buffer+length, Max-length, "%.4a", &rd->ipv4); break;
+
+ case kDNSType_NS: // Same as PTR
+ case kDNSType_CNAME:// Same as PTR
+ case kDNSType_PTR: mDNS_snprintf(buffer+length, Max-length, "%##s", rd->name.c); break;
+
+ case kDNSType_HINFO:// Display this the same as TXT (just show first string)
+ case kDNSType_TXT: mDNS_snprintf(buffer+length, Max-length, "%#s", rd->txt.c); break;
+
+ case kDNSType_AAAA: mDNS_snprintf(buffer+length, Max-length, "%.16a", &rd->ipv6); break;
+ case kDNSType_SRV: mDNS_snprintf(buffer+length, Max-length, "%u %u %u %##s",
+ rd->srv.priority, rd->srv.weight, mDNSVal16(rd->srv.port), rd->srv.target.c); break;
+ default: mDNS_snprintf(buffer+length, Max-length, "RDLen %d: %s", rr->rdlength, rd->data); break;
+ }
+ for (ptr = buffer; *ptr; ptr++) if (*ptr < ' ') *ptr='.';
+ return(buffer);
+ }
+
+mDNSexport mDNSu32 mDNSRandom(mDNSu32 max)
+ {
+ static mDNSu32 seed = 0;
+ mDNSu32 mask = 1;
+
+ if (!seed)
+ {
+ int i;
+ seed = mDNSPlatformRandomSeed(); // Pick an initial seed
+ for (i=0; i<100; i++) seed = seed * 21 + 1; // And mix it up a bit
+ }
+ while (mask < max) mask = (mask << 1) | 1;
+ do seed = seed * 21 + 1; while ((seed & mask) > max);
+ return (seed & mask);
+ }
+
+mDNSexport mDNSu32 mDNSRandomFromFixedSeed(mDNSu32 seed, mDNSu32 max)
+ {
+ mDNSu32 mask = 1;
+ while (mask < max) mask = (mask << 1) | 1;
+ do seed = seed * 21 + 1; while ((seed & mask) > max);
+ return (seed & mask);
+ }
+
+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)(ip->ip.v4.NotAnInteger == AllDNSLinkGroupv4.NotAnInteger);
+ case mDNSAddrType_IPv6: return(mDNSBool)(ip->ip.v6.l[0] == AllDNSLinkGroupv6.l[0] &&
+ ip->ip.v6.l[1] == AllDNSLinkGroupv6.l[1] &&
+ ip->ip.v6.l[2] == AllDNSLinkGroupv6.l[2] &&
+ ip->ip.v6.l[3] == AllDNSLinkGroupv6.l[3] );
+ 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 255 characters)"); return(mDNSfalse); }
+ if (!SameDomainLabel(a, b)) return(mDNSfalse);
+ a += 1 + *a;
+ b += 1 + *b;
+ }
+
+ return(mDNStrue);
+ }
+
+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 = (domainname*)"\x5" "local";
+ static const domainname *nR = (domainname*)"\x3" "254" "\x3" "169" "\x7" "in-addr" "\x4" "arpa";
+ static const domainname *n8 = (domainname*)"\x1" "8" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
+ static const domainname *n9 = (domainname*)"\x1" "9" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
+ static const domainname *nA = (domainname*)"\x1" "a" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
+ static const domainname *nB = (domainname*)"\x1" "b" "\x1" "e" "\x1" "f" "\x3" "ip6" "\x4" "arpa";
+
+ const domainname *d1, *d2, *d3, *d4, *d5, *d6; // Top-level domain, second-level domain, etc.
+ d1 = d2 = d3 = d4 = d5 = d6 = mDNSNULL;
+ while (d->c[0])
+ {
+ d6 = d5; d5 = d4; d4 = d3; d3 = d2; d2 = d1; d1 = d;
+ d = (domainname*)(d->c + 1 + d->c[0]);
+ }
+
+ if (d1 && SameDomainName(d1, nL)) return(mDNStrue);
+ if (d4 && SameDomainName(d4, nR)) return(mDNStrue);
+ if (d6 && SameDomainName(d6, n8)) return(mDNStrue);
+ if (d6 && SameDomainName(d6, n9)) return(mDNStrue);
+ if (d6 && SameDomainName(d6, nA)) return(mDNStrue);
+ if (d6 && SameDomainName(d6, nB)) return(mDNStrue);
+ return(mDNSfalse);
+ }
+
+// Returns length of a domain name INCLUDING the byte for the final null label
+// i.e. 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 255 (MAX_DOMAIN_NAME)
+// If the given domainname is invalid, result is 256
+mDNSexport mDNSu16 DomainNameLength(const domainname *const name)
+ {
+ const mDNSu8 *src = name->c;
+ while (*src)
+ {
+ if (*src > MAX_DOMAIN_LABEL) return(MAX_DOMAIN_NAME+1);
+ src += 1 + *src;
+ if (src - name->c >= MAX_DOMAIN_NAME) return(MAX_DOMAIN_NAME+1);
+ }
+ return((mDNSu16)(src - name->c + 1));
+ }
+
+// CompressedDomainNameLength returns the length of a domain name INCLUDING the byte
+// for the final null label i.e. 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((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));
+ }
+
+// 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 255 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 255 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 255 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 255 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 (1005 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]);
+ }
+
+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;
+
+ // 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 = (domainname *)s1;
+
+ // Special support for queries done by some third-party network monitoring software
+ // For these queries, we retract the "._sub" we just added between the subtype and the main type
+ if (SameDomainName((domainname*)s0, (domainname*)"\x09_services\x07_dns-sd\x04_udp") ||
+ SameDomainName((domainname*)s0, (domainname*)"\x09_services\x05_mdns\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 >= 0x40 || (len > 15 && !SameDomainName(domain, &localdomain)))
+ {
+ errormsg="Application protocol name must be underscore plus 1-14 characters. See <http://www.dns-sd.org/ServiceTypes.html>";
+ goto fail;
+ }
+ if (src[1] != '_') { errormsg="Application protocol name must begin with underscore"; goto fail; }
+ for (i=2; i<=len; i++)
+ if (!mdnsIsLetter(src[i]) && !mdnsIsDigit(src[i]) && src[i] != '-' && src[i] != '_')
+ { errormsg="Application protocol name must contain only letters, digits, and hyphens"; goto fail; }
+ for (i=0; i<=len; i++) *dst++ = *src++;
+
+ len = *src;
+ if (!(len == 4 && src[1] == '_' &&
+ (((src[2] | 0x20) == 'u' && (src[3] | 0x20) == 'd') || ((src[2] | 0x20) == 't' && (src[3] | 0x20) == 'c')) &&
+ (src[4] | 0x20) == 'p'))
+ { 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, (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); }
+ for (i=0; i<=len; i++) *dst++ = *src++;
+
+ len = *src;
+ if (!len) { debugf("DeconstructServiceName: FQDN contains only two labels!"); return(mDNSfalse); }
+ if (len >= 0x40) { debugf("DeconstructServiceName: Transport protocol name too long"); 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] : 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 *name, mDNSu32 val, 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 (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
+
+mDNSexport mDNSu32 RDataHashValue(mDNSu16 const rdlength, const RDataBody *const rdb)
+ {
+ mDNSu32 sum = 0;
+ int i;
+ for (i=0; i+1 < rdlength; i+=2)
+ {
+ sum += (((mDNSu32)(rdb->data[i])) << 8) | rdb->data[i+1];
+ sum = (sum<<3) | (sum>>29);
+ }
+ if (i < rdlength)
+ {
+ 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)
+ {
+ switch(r1->rrtype)
+ {
+ case kDNSType_CNAME:// Same as PTR
+ case kDNSType_PTR: return(SameDomainName(&r1->rdata->u.name, &r2->name));
+
+ case kDNSType_SRV: return(mDNSBool)( r1->rdata->u.srv.priority == r2->srv.priority &&
+ r1->rdata->u.srv.weight == r2->srv.weight &&
+ r1->rdata->u.srv.port.NotAnInteger == r2->srv.port.NotAnInteger &&
+ SameDomainName(&r1->rdata->u.srv.target, &r2->srv.target) );
+
+ default: return(mDNSPlatformMemSame(r1->rdata->u.data, r2->data, r1->rdlength));
+ }
+ }
+
+mDNSexport mDNSBool SameRData(const ResourceRecord *const r1, const ResourceRecord *const r2)
+ {
+ if (r1->rrtype != r2->rrtype) return(mDNSfalse);
+ if (r1->rdlength != r2->rdlength) return(mDNSfalse);
+ if (r1->rdatahash != r2->rdatahash) return(mDNSfalse);
+ return(SameRDataBody(r1, &r2->rdata->u));
+ }
+
+mDNSexport mDNSBool SameResourceRecord(ResourceRecord *r1, ResourceRecord *r2)
+ {
+ return (r1->namehash == r2->namehash &&
+ r1->rrtype == r2->rrtype &&
+ SameDomainName(r1->name, r2->name) &&
+ SameRData(r1, r2));
+ }
+
+mDNSexport mDNSBool ResourceRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q)
+ {
+ if (rr->InterfaceID &&
+ q ->InterfaceID && q->InterfaceID != mDNSInterface_LocalOnly &&
+ rr->InterfaceID != q->InterfaceID) return(mDNSfalse);
+
+ // RR type CNAME matches any query type. QTYPE ANY matches any RR type. QCLASS ANY matches any RR class.
+ if (rr->rrtype != kDNSType_CNAME && rr->rrtype != q->qtype && q->qtype != kDNSQType_ANY ) 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 RDataBody *rd = &rr->rdata->u;
+ const domainname *const name = estimate ? rr->name : mDNSNULL;
+ switch (rr->rrtype)
+ {
+ case kDNSType_A: return(sizeof(rd->ipv4));
+ case kDNSType_CNAME:// Same as PTR
+ case kDNSType_NS: // Same as PTR
+ case kDNSType_PTR: return(CompressedDomainNameLength(&rd->name, name));
+ case kDNSType_HINFO:return(mDNSu16)(2 + (int)rd->data[0] + (int)rd->data[1 + (int)rd->data[0]]);
+ case kDNSType_NULL: // Same as TXT -- not self-describing, so have to just trust rdlength
+ case kDNSType_TXT: return(rr->rdlength); // TXT is not self-describing, so have to just trust rdlength
+ case kDNSType_AAAA: return(sizeof(rd->ipv6));
+ case kDNSType_SRV: return(mDNSu16)(6 + CompressedDomainNameLength(&rd->srv.target, name));
+ case kDNSType_SOA: return (mDNSu16)(CompressedDomainNameLength(&rd->soa.mname, name) +
+ CompressedDomainNameLength(&rd->soa.rname, name) +
+ 5 * sizeof(mDNSOpaque32));
+ case kDNSType_OPT: return(rr->rdlength);
+ default: debugf("Warning! Don't know how to get length of resource type %d", rr->rrtype);
+ return(rr->rdlength);
+ }
+ }
+
+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: if (!rdlength) return(mDNSfalse);
+ len = DomainNameLength(&rd->u.name);
+ 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: if (!rdlength) return(mDNSfalse);
+ len = DomainNameLength(&rd->u.mx.exchange);
+ return(len <= MAX_DOMAIN_NAME && rdlength == 2+len);
+
+ case kDNSType_SRV: if (!rdlength) return(mDNSfalse);
+ len = DomainNameLength(&rd->u.srv.target);
+ return(len <= MAX_DOMAIN_NAME && rdlength == 6+len);
+
+ default: return(mDNStrue); // Allow all other types without checking
+ }
+ }
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#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;
+
+ while (*np && ptr < limit-1) // While we've got characters in the name, and space to write them in the message...
+ {
+ 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 255 bytes long, including the final trailing root label.
+ // Suppose np is now at name->c[248], and we're about to write our last non-null label ("local").
+ // We know that max will be at name->c[255]
+ // 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 255 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 255 bytes)", name->c); return(mDNSNULL); }
+
+ if (base) pointer = FindCompressionPointer(base, searchlimit, np);
+ if (pointer) // Use a compression pointer if we can
+ {
+ mDNSu16 offset = (mDNSu16)(pointer - base);
+ *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 (ptr + 1 + len >= limit) return(mDNSNULL);
+ *ptr++ = len;
+ for (i=0; i<len; i++) *ptr++ = *np++;
+ }
+ }
+
+ if (ptr < limit) // If we didn't run out of space
+ {
+ *ptr++ = 0; // Put the final root label
+ return(ptr); // and return
+ }
+
+ return(mDNSNULL);
+ }
+
+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);
+ }
+
+mDNSlocal mDNSu8 *putOptRData(mDNSu8 *ptr, const mDNSu8 *limit, ResourceRecord *rr)
+ {
+ int nput = 0;
+ rdataOpt *opt;
+
+ while (nput < rr->rdlength)
+ {
+ // check if space for opt/optlen
+ if (ptr + (2 * sizeof(mDNSu16)) > limit) goto space_err;
+ opt = (rdataOpt *)(rr->rdata->u.data + nput);
+ ptr = putVal16(ptr, opt->opt);
+ ptr = putVal16(ptr, opt->optlen);
+ nput += 2 * sizeof(mDNSu16);
+ if (opt->opt == kDNSOpt_LLQ)
+ {
+ if (ptr + LLQ_OPTLEN > limit) goto space_err;
+ ptr = putVal16(ptr, opt->OptData.llq.vers);
+ ptr = putVal16(ptr, opt->OptData.llq.llqOp);
+ ptr = putVal16(ptr, opt->OptData.llq.err);
+ mDNSPlatformMemCopy(opt->OptData.llq.id, ptr, 8); // 8-byte id
+ ptr += 8;
+ ptr = putVal32(ptr, opt->OptData.llq.lease);
+ nput += LLQ_OPTLEN;
+ }
+ else if (opt->opt == kDNSOpt_Lease)
+ {
+ if (ptr + sizeof(mDNSs32) > limit) goto space_err;
+ ptr = putVal32(ptr, opt->OptData.lease);
+ nput += sizeof(mDNSs32);
+ }
+ else { LogMsg("putOptRData - unknown option %d", opt->opt); return mDNSNULL; }
+ }
+
+ return ptr;
+
+ space_err:
+ LogMsg("ERROR: putOptRData - out of space");
+ return mDNSNULL;
+ }
+
+mDNSlocal mDNSu16 getVal16(const mDNSu8 **ptr)
+ {
+ mDNSu16 val = (mDNSu16)(((mDNSu16)(*ptr)[0]) << 8 | (*ptr)[1]);
+ *ptr += sizeof(mDNSOpaque16);
+ return val;
+ }
+
+mDNSlocal const mDNSu8 *getOptRdata(const mDNSu8 *ptr, const mDNSu8 *const limit, LargeCacheRecord *const cr, mDNSu16 pktRDLen)
+ {
+ int nread = 0;
+ ResourceRecord *const rr = &cr->r.resrec;
+ rdataOpt *opt = (rdataOpt *)rr->rdata->u.data;
+
+ while (nread < pktRDLen && (mDNSu8 *)opt < rr->rdata->u.data + MaximumRDSize - sizeof(rdataOpt))
+ {
+ // space for opt + optlen
+ if (nread + (2 * sizeof(mDNSu16)) > rr->rdata->MaxRDLength) goto space_err;
+ opt->opt = getVal16(&ptr);
+ opt->optlen = getVal16(&ptr);
+ nread += 2 * sizeof(mDNSu16);
+ if (opt->opt == kDNSOpt_LLQ)
+ {
+ if ((unsigned)(limit - ptr) < LLQ_OPTLEN) goto space_err;
+ opt->OptData.llq.vers = getVal16(&ptr);
+ opt->OptData.llq.llqOp = getVal16(&ptr);
+ opt->OptData.llq.err = getVal16(&ptr);
+ mDNSPlatformMemCopy(ptr, opt->OptData.llq.id, 8);
+ ptr += 8;
+ opt->OptData.llq.lease = (mDNSu32) ((mDNSu32)ptr[0] << 24 | (mDNSu32)ptr[1] << 16 | (mDNSu32)ptr[2] << 8 | ptr[3]);
+ if (opt->OptData.llq.lease > 0x70000000UL / mDNSPlatformOneSecond)
+ opt->OptData.llq.lease = 0x70000000UL / mDNSPlatformOneSecond;
+ ptr += sizeof(mDNSOpaque32);
+ nread += LLQ_OPTLEN;
+ }
+ else if (opt->opt == kDNSOpt_Lease)
+ {
+ if ((unsigned)(limit - ptr) < sizeof(mDNSs32)) goto space_err;
+
+ opt->OptData.lease = (mDNSu32) ((mDNSu32)ptr[0] << 24 | (mDNSu32)ptr[1] << 16 | (mDNSu32)ptr[2] << 8 | ptr[3]);
+ if (opt->OptData.lease > 0x70000000UL / mDNSPlatformOneSecond)
+ opt->OptData.lease = 0x70000000UL / mDNSPlatformOneSecond;
+ ptr += sizeof(mDNSs32);
+ nread += sizeof(mDNSs32);
+ }
+ else { LogMsg("ERROR: getOptRdata - unknown opt %d", opt->opt); return mDNSNULL; }
+ opt++; // increment pointer into rdatabody
+ }
+
+ rr->rdlength = pktRDLen;
+ return ptr;
+
+ space_err:
+ LogMsg("ERROR: getLLQRdata - out of space");
+ return mDNSNULL;
+ }
+
+mDNSexport mDNSu8 *putRData(const DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, ResourceRecord *rr)
+ {
+ 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++ = rr->rdata->u.ipv4.b[0];
+ *ptr++ = rr->rdata->u.ipv4.b[1];
+ *ptr++ = rr->rdata->u.ipv4.b[2];
+ *ptr++ = rr->rdata->u.ipv4.b[3];
+ return(ptr);
+
+ case kDNSType_CNAME:// Same as PTR
+ case kDNSType_PTR: return(putDomainNameAsLabels(msg, ptr, limit, &rr->rdata->u.name));
+
+ case kDNSType_AAAA: if (rr->rdlength != sizeof(rr->rdata->u.ipv6))
+ {
+ debugf("putRData: Illegal length %d for kDNSType_AAAA", rr->rdlength);
+ return(mDNSNULL);
+ }
+ if (ptr + sizeof(rr->rdata->u.ipv6) > limit) return(mDNSNULL);
+ mDNSPlatformMemCopy(&rr->rdata->u.ipv6, ptr, sizeof(rr->rdata->u.ipv6));
+ return(ptr + sizeof(rr->rdata->u.ipv6));
+
+ case kDNSType_SRV: if (ptr + 6 > limit) return(mDNSNULL);
+ *ptr++ = (mDNSu8)(rr->rdata->u.srv.priority >> 8);
+ *ptr++ = (mDNSu8)(rr->rdata->u.srv.priority & 0xFF);
+ *ptr++ = (mDNSu8)(rr->rdata->u.srv.weight >> 8);
+ *ptr++ = (mDNSu8)(rr->rdata->u.srv.weight & 0xFF);
+ *ptr++ = rr->rdata->u.srv.port.b[0];
+ *ptr++ = rr->rdata->u.srv.port.b[1];
+ return(putDomainNameAsLabels(msg, ptr, limit, &rr->rdata->u.srv.target));
+ case kDNSType_OPT: return putOptRData(ptr, limit, rr);
+
+ default: debugf("putRData: Warning! Writing unknown resource type %d as raw data", rr->rrtype);
+ // Fall through to common code below
+ case kDNSType_HINFO:
+ case kDNSType_TXT:
+ case kDNSType_TSIG: if (ptr + rr->rdlength > limit) return(mDNSNULL);
+ mDNSPlatformMemCopy(rr->rdata->u.data, ptr, rr->rdlength);
+ return(ptr + rr->rdlength);
+ }
+ }
+
+mDNSexport mDNSu8 *PutResourceRecordTTLWithLimit(DNSMessage *const msg, mDNSu8 *ptr, mDNSu16 *count, ResourceRecord *rr, mDNSu32 ttl, const mDNSu8 *limit)
+ {
+ mDNSu8 *endofrdata;
+ mDNSu16 actualLength;
+
+ if (rr->RecordType == kDNSRecordTypeUnregistered)
+ {
+ LogMsg("PutResourceRecord ERROR! Attempt to put kDNSRecordTypeUnregistered %##s (%s)", rr->name->c, DNSTypeName(rr->rrtype));
+ return(ptr);
+ }
+
+ 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);
+ endofrdata = putRData(msg, 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);
+ }
+
+mDNSexport mDNSu8 *PutResourceRecordCappedTTL(DNSMessage *const msg, mDNSu8 *ptr, mDNSu16 *count, ResourceRecord *rr, mDNSu32
+ maxttl)
+ {
+ if (maxttl > rr->rroriginalttl) maxttl = rr->rroriginalttl;
+ return(PutResourceRecordTTL(msg, ptr, count, rr, maxttl));
+ }
+
+mDNSexport 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(domainname *name, DNSMessage *msg, mDNSu8 *ptr, mDNSu8 *end)
+ {
+ AuthRecord prereq;
+
+ mDNSPlatformMemZero(&prereq, sizeof(AuthRecord));
+ mDNS_SetupResourceRecord(&prereq, mDNSNULL, mDNSInterface_Any, kDNSQType_ANY, kStandardTTL, 0, mDNSNULL, mDNSNULL);
+ AssignDomainName(prereq.resrec.name, name);
+ prereq.resrec.rrtype = kDNSQType_ANY;
+ prereq.resrec.rrclass = kDNSClass_NONE;
+ ptr = putEmptyResourceRecord(msg, ptr, end, &msg->h.mDNS_numPrereqs, &prereq);
+ return ptr;
+ }
+
+// for dynamic updates
+mDNSexport mDNSu8 *putDeletionRecord(DNSMessage *msg, mDNSu8 *ptr, ResourceRecord *rr)
+ {
+ mDNSu16 origclass;
+ // deletion: specify record w/ TTL 0, class NONE
+
+ origclass = rr->rrclass;
+ rr->rrclass = kDNSClass_NONE;
+ ptr = PutResourceRecordTTLJumbo(msg, ptr, &msg->h.mDNS_numUpdates, rr, 0);
+ rr->rrclass = origclass;
+ return ptr;
+ }
+
+mDNSexport mDNSu8 *putDeleteRRSet(DNSMessage *msg, mDNSu8 *ptr, const domainname *name, mDNSu16 rrtype)
+ {
+ const mDNSu8 *limit = msg->data + AbsoluteMaxDNSMessageData;
+ 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;
+ ResourceRecord *opt = &rr.resrec;
+ rdataOpt *optRD;
+
+ mDNSPlatformMemZero(&rr, sizeof(AuthRecord));
+ mDNS_SetupResourceRecord(&rr, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, 0, mDNSNULL, mDNSNULL);
+
+ opt->RecordType = kDNSRecordTypeKnownUnique; // to avoid warnings in other layers
+ opt->rrtype = kDNSType_OPT;
+ opt->rdlength = LEASE_OPT_RDLEN;
+ opt->rdestimate = LEASE_OPT_RDLEN;
+
+ optRD = &rr.resrec.rdata->u.opt;
+ optRD->opt = kDNSOpt_Lease;
+ optRD->optlen = sizeof(mDNSs32);
+ optRD->OptData.lease = lease;
+ end = PutResourceRecordTTLJumbo(msg, end, &msg->h.numAdditionals, opt, 0);
+ if (!end) { LogMsg("ERROR: putUpdateLease - PutResourceRecordTTL"); return mDNSNULL; }
+
+ 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->rdlength, &rr->rdata->u);
+ }
+
+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 255 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 255 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 *largecr)
+ {
+ CacheRecord *rr = &largecr->r;
+ mDNSu16 pktrdlength;
+
+ if (largecr == &m->rec && largecr->r.resrec.RecordType)
+ LogMsg("GetLargeResourceRecord: m->rec appears to be already in use for %s", CRDisplayString(m, &largecr->r));
+
+ 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 SetNextCacheCheckTime()
+ rr->LastUsed = m ? m->timenow : 0;
+ rr->CRActiveQuestion = mDNSNULL;
+ rr->UnansweredQueries = 0;
+ rr->LastUnansweredTime= 0;
+ rr->MPUnansweredQ = 0;
+ rr->MPLastUnansweredQT= 0;
+ rr->MPUnansweredKA = 0;
+ rr->MPExpectingKA = mDNSfalse;
+ rr->NextInCFList = mDNSNULL;
+
+ rr->resrec.InterfaceID = InterfaceID;
+ ptr = getDomainName(msg, ptr, end, rr->resrec.name);
+ if (!ptr) { debugf("GetResourceRecord: Malformed RR name"); return(mDNSNULL); }
+
+ if (ptr + 10 > end) { debugf("GetResourceRecord: 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 (ptr[2] & (kDNSClass_UniqueRRSet >> 8))
+ RecordType |= kDNSRecordTypePacketUniqueMask;
+ ptr += 10;
+ if (ptr + pktrdlength > end) { debugf("GetResourceRecord: 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->rdatastorage;
+ rr->resrec.rdata->MaxRDLength = MaximumRDSize;
+
+ if (!RecordType) LogMsg("GetLargeResourceRecord: No RecordType for %##s", rr->resrec.name->c);
+
+ switch (rr->resrec.rrtype)
+ {
+ case kDNSType_A: rr->resrec.rdata->u.ipv4.b[0] = ptr[0];
+ rr->resrec.rdata->u.ipv4.b[1] = ptr[1];
+ rr->resrec.rdata->u.ipv4.b[2] = ptr[2];
+ rr->resrec.rdata->u.ipv4.b[3] = ptr[3];
+ break;
+
+ case kDNSType_CNAME:// Same as PTR
+ case kDNSType_NS:
+ case kDNSType_PTR: if (!getDomainName(msg, ptr, end, &rr->resrec.rdata->u.name))
+ { debugf("GetResourceRecord: Malformed CNAME/PTR RDATA name"); return(mDNSNULL); }
+ //debugf("%##s PTR %##s rdlen %d", rr->resrec.name.c, rr->resrec.rdata->u.name.c, pktrdlength);
+ break;
+
+ case kDNSType_NULL: //Same as TXT
+ case kDNSType_HINFO://Same as TXT
+ case kDNSType_TXT: if (pktrdlength > rr->resrec.rdata->MaxRDLength)
+ {
+ debugf("GetResourceRecord: %s rdata size (%d) exceeds storage (%d)",
+ DNSTypeName(rr->resrec.rrtype), pktrdlength, rr->resrec.rdata->MaxRDLength);
+ return(mDNSNULL);
+ }
+ rr->resrec.rdlength = pktrdlength;
+ mDNSPlatformMemCopy(ptr, rr->resrec.rdata->u.data, pktrdlength);
+ break;
+
+ case kDNSType_AAAA: mDNSPlatformMemCopy(ptr, &rr->resrec.rdata->u.ipv6, sizeof(rr->resrec.rdata->u.ipv6));
+ break;
+
+ case kDNSType_SRV: rr->resrec.rdata->u.srv.priority = (mDNSu16)((mDNSu16)ptr[0] << 8 | ptr[1]);
+ rr->resrec.rdata->u.srv.weight = (mDNSu16)((mDNSu16)ptr[2] << 8 | ptr[3]);
+ rr->resrec.rdata->u.srv.port.b[0] = ptr[4];
+ rr->resrec.rdata->u.srv.port.b[1] = ptr[5];
+ if (!getDomainName(msg, ptr+6, end, &rr->resrec.rdata->u.srv.target))
+ { debugf("GetResourceRecord: Malformed SRV RDATA name"); return(mDNSNULL); }
+ //debugf("%##s SRV %##s rdlen %d", rr->resrec.name.c, rr->resrec.rdata->u.srv.target.c, pktrdlength);
+ break;
+
+ case kDNSType_SOA: ptr = getDomainName(msg, ptr, end, &rr->resrec.rdata->u.soa.mname);
+ if (!ptr) { debugf("GetResourceRecord: Malformed SOA RDATA mname"); return mDNSNULL; }
+ ptr = getDomainName(msg, ptr, end, &rr->resrec.rdata->u.soa.rname);
+ if (!ptr) { debugf("GetResourceRecord: Malformed SOA RDATA rname"); return mDNSNULL; }
+ if (ptr + 0x14 != end) { debugf("GetResourceRecord: Malformed SOA RDATA"); return mDNSNULL; }
+ rr->resrec.rdata->u.soa.serial = (mDNSs32) ((mDNSs32)ptr[0x00] << 24 | (mDNSs32)ptr[0x01] << 16 | (mDNSs32)ptr[0x02] << 8 | ptr[0x03]);
+ rr->resrec.rdata->u.soa.refresh = (mDNSu32) ((mDNSu32)ptr[0x04] << 24 | (mDNSu32)ptr[0x05] << 16 | (mDNSu32)ptr[0x06] << 8 | ptr[0x07]);
+ rr->resrec.rdata->u.soa.retry = (mDNSu32) ((mDNSu32)ptr[0x08] << 24 | (mDNSu32)ptr[0x09] << 16 | (mDNSu32)ptr[0x0A] << 8 | ptr[0x0B]);
+ rr->resrec.rdata->u.soa.expire = (mDNSu32) ((mDNSu32)ptr[0x0C] << 24 | (mDNSu32)ptr[0x0D] << 16 | (mDNSu32)ptr[0x0E] << 8 | ptr[0x0F]);
+ rr->resrec.rdata->u.soa.min = (mDNSu32) ((mDNSu32)ptr[0x10] << 24 | (mDNSu32)ptr[0x11] << 16 | (mDNSu32)ptr[0x12] << 8 | ptr[0x13]);
+ break;
+
+ case kDNSType_OPT: getOptRdata(ptr, end, largecr, pktrdlength); break;
+
+ default: if (pktrdlength > rr->resrec.rdata->MaxRDLength)
+ {
+ debugf("GetResourceRecord: rdata %d (%s) size (%d) exceeds storage (%d)",
+ rr->resrec.rrtype, DNSTypeName(rr->resrec.rrtype), pktrdlength, rr->resrec.rdata->MaxRDLength);
+ return(mDNSNULL);
+ }
+ debugf("GetResourceRecord: 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(ptr, rr->resrec.rdata->u.data, pktrdlength);
+ break;
+ }
+
+ rr->resrec.namehash = DomainNameHashValue(rr->resrec.name);
+ SetNewRData(&rr->resrec, mDNSNULL, 0);
+
+ // Success! Now fill in RecordType to show this record contains valid data
+ rr->resrec.RecordType = RecordType;
+ return(ptr + pktrdlength);
+ }
+
+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)
+ {
+ question->InterfaceID = InterfaceID;
+ 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);
+ }
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark -
+#pragma mark - Packet Sending Functions
+#endif
+
+mDNSexport mStatus mDNSSendDNSMessage(const mDNS *const m, DNSMessage *const msg, mDNSu8 *end,
+ mDNSInterfaceID InterfaceID, const mDNSAddr *dst, mDNSIPPort dstport, int sd, uDNS_AuthInfo *authInfo)
+ {
+ mStatus status;
+ int nsent;
+ mDNSs32 msglen;
+ mDNSu8 lenbuf[2];
+ mDNSu16 numQuestions = msg->h.numQuestions;
+ mDNSu16 numAnswers = msg->h.numAnswers;
+ mDNSu16 numAuthorities = msg->h.numAuthorities;
+ mDNSu16 numAdditionals = msg->h.numAdditionals;
+ mDNSu8 *ptr = (mDNSu8 *)&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);
+
+ if (authInfo)
+ {
+ end = DNSDigest_SignMessage(msg, &end, &numAdditionals, authInfo);
+ if (!end) return mStatus_UnknownErr;
+ }
+
+ // Send the packet on the wire
+
+ if (sd >= 0)
+ {
+ msglen = (mDNSu16)(end - (mDNSu8 *)msg);
+ lenbuf[0] = (mDNSu8)(msglen >> 8); // host->network byte conversion
+ lenbuf[1] = (mDNSu8)(msglen & 0xFF);
+ nsent = mDNSPlatformWriteTCP(sd, (char*)lenbuf, 2);
+ //!!!KRS make sure kernel is sending these as 1 packet!
+ if (nsent != 2) goto tcp_error;
+ nsent = mDNSPlatformWriteTCP(sd, (char *)msg, msglen);
+ if (nsent != msglen) goto tcp_error;
+ status = mStatus_NoError;
+ }
+ else
+ {
+ status = mDNSPlatformSendUDP(m, msg, end, InterfaceID, dst, dstport);
+ }
+
+ // Put all the integer values back the way they were before we return
+ msg->h.numQuestions = numQuestions;
+ msg->h.numAnswers = numAnswers;
+ msg->h.numAuthorities = numAuthorities;
+ msg->h.numAdditionals = (mDNSu16)(authInfo ? numAdditionals - 1 : numAdditionals);
+
+ return(status);
+
+ tcp_error:
+ LogMsg("mDNSSendDNSMessage: error sending message over tcp");
+ return mStatus_UnknownErr;
+ }
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - RR List Management & Task Management
+#endif
+
+mDNSexport void mDNS_Lock(mDNS *const m)
+ {
+ // 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("mDNS_Lock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
+
+ // 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("mDNS_Lock: m->timenow already set (%ld/%ld)", 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("mDNS_Lock: m->mDNS_busy is %ld but m->timenow not set", 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("mDNSPlatformRawTime went backwards by %ld ticks; setting correction factor to %ld", 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 mDNSs32 GetNextScheduledEvent(const mDNS *const m)
+ {
+ mDNSs32 e = m->timenow + 0x78000000;
+ if (m->mDNSPlatformStatus != mStatus_NoError || m->SleepState) 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 && LocalRecordReady(m->NewLocalRecords)) return(m->timenow);
+ if (m->SuppressSending) return(m->SuppressSending);
+#ifndef UNICAST_DISABLED
+ if (e - m->uDNS_info.nextevent > 0) e = m->uDNS_info.nextevent;
+#endif
+ if (e - m->NextCacheCheck > 0) e = m->NextCacheCheck;
+ if (e - m->NextScheduledQuery > 0) e = m->NextScheduledQuery;
+ if (e - m->NextScheduledProbe > 0) e = m->NextScheduledProbe;
+ if (e - m->NextScheduledResponse > 0) e = m->NextScheduledResponse;
+ return(e);
+ }
+
+mDNSexport void mDNS_Unlock(mDNS *const m)
+ {
+ // Decrement mDNS_busy
+ m->mDNS_busy--;
+
+ // Check for locking failures
+ if (m->mDNS_busy != m->mDNS_reentrancy)
+ LogMsg("mDNS_Unlock: Locking failure! mDNS_busy (%ld) != mDNS_reentrancy (%ld)", m->mDNS_busy, m->mDNS_reentrancy);
+
+ // 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("mDNS_Unlock: ERROR! m->timenow aready zero");
+ m->timenow = 0;
+ }
+
+ // MUST release the platform lock LAST!
+ mDNSPlatformUnlock(m);
+ }
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/DNSCommon.h b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/DNSCommon.h
new file mode 100644
index 0000000000..717d87702a
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/DNSCommon.h
@@ -0,0 +1,368 @@
+/* -*- 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.
+
+ Change History (most recent first):
+
+$Log: DNSCommon.h,v $
+Revision 1.34.2.1 2006/08/29 06:24:22 cheshire
+Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
+
+Revision 1.34 2006/03/18 21:47:56 cheshire
+<rdar://problem/4073825> Improve logic for delaying packets after repeated interface transitions
+
+Revision 1.33 2006/03/10 21:51:41 cheshire
+<rdar://problem/4111464> After record update, old record sometimes remains in cache
+Split out SameRDataBody() into a separate routine so it can be called from other code
+
+Revision 1.32 2005/03/21 00:33:51 shersche
+<rdar://problem/4021486> Fix build warnings on Win32 platform
+
+Revision 1.31 2005/02/18 00:43:11 cheshire
+<rdar://problem/4010245> mDNSResponder should auto-truncate service names that are too long
+
+Revision 1.30 2005/01/19 03:12:44 cheshire
+Move LocalRecordReady() macro from mDNS.c to DNSCommon.h
+
+Revision 1.29 2004/12/15 02:11:22 ksekar
+<rdar://problem/3917317> Don't check for Dynamic DNS hostname uniqueness
+
+Revision 1.28 2004/12/06 21:15:22 ksekar
+<rdar://problem/3884386> mDNSResponder crashed in CheckServiceRegistrations
+
+Revision 1.27 2004/12/03 07:20:50 ksekar
+<rdar://problem/3674208> Wide-Area: Registration of large TXT record fails
+
+Revision 1.26 2004/12/03 05:18:33 ksekar
+<rdar://problem/3810596> mDNSResponder needs to return more specific TSIG errors
+
+Revision 1.25 2004/10/26 03:52:02 cheshire
+Update checkin comments
+
+Revision 1.24 2004/10/23 01:16:00 cheshire
+<rdar://problem/3851677> uDNS operations not always reliable on multi-homed hosts
+
+Revision 1.23 2004/10/03 23:18:58 cheshire
+Move address comparison macros from DNSCommon.h to mDNSEmbeddedAPI.h
+
+Revision 1.22 2004/09/30 00:24:56 ksekar
+<rdar://problem/3695802> Dynamically update default registration domains on config change
+
+Revision 1.21 2004/09/17 01:08:48 cheshire
+Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h
+ The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces
+ declared in that file are ONLY appropriate to single-address-space embedded applications.
+ For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used.
+
+Revision 1.20 2004/09/17 00:49:51 cheshire
+Get rid of now-unused GetResourceRecord -- the correct (safe) routine to use
+is GetLargeResourceRecord
+
+Revision 1.19 2004/09/16 21:59:15 cheshire
+For consistency with zerov6Addr, rename zeroIPAddr to zerov4Addr
+
+Revision 1.18 2004/09/16 02:29:39 cheshire
+Moved mDNS_Lock/mDNS_Unlock to DNSCommon.c; Added necessary locking around
+uDNS_ReceiveMsg, uDNS_StartQuery, uDNS_UpdateRecord, uDNS_RegisterService
+
+Revision 1.17 2004/09/14 23:27:46 cheshire
+Fix compile errors
+
+Revision 1.16 2004/08/13 23:46:58 cheshire
+"asyncronous" -> "asynchronous"
+
+Revision 1.15 2004/08/10 23:19:14 ksekar
+<rdar://problem/3722542>: DNS Extension daemon for Wide Area Service Discovery
+Moved routines/constants to allow extern access for garbage collection daemon
+
+Revision 1.14 2004/05/28 23:42:36 ksekar
+<rdar://problem/3258021>: Feature: DNS server->client notification on record changes (#7805)
+
+Revision 1.13 2004/05/18 23:51:25 cheshire
+Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers
+
+Revision 1.12 2004/04/22 04:03:59 cheshire
+Headers should use "extern" declarations, not "mDNSexport"
+
+Revision 1.11 2004/04/14 23:09:28 ksekar
+Support for TSIG signed dynamic updates.
+
+Revision 1.10 2004/03/13 01:57:33 ksekar
+<rdar://problem/3192546>: DynDNS: Dynamic update of service records
+
+Revision 1.9 2004/02/21 08:56:58 bradley
+Wrap prototypes with extern "C" for C++ builds.
+
+Revision 1.8 2004/02/06 23:04:18 ksekar
+Basic Dynamic Update support via mDNS_Register (dissabled via
+UNICAST_REGISTRATION #define)
+
+Revision 1.7 2004/02/03 19:47:36 ksekar
+Added an asynchronous state machine mechanism to uDNS.c, including
+calls to find the parent zone for a domain name. Changes include code
+in repository previously dissabled via "#if 0 incomplete". Codepath
+is currently unused, and will be called to create update records, etc.
+
+Revision 1.6 2004/01/27 20:15:22 cheshire
+<rdar://problem/3541288>: Time to prune obsolete code for listening on port 53
+
+Revision 1.5 2004/01/24 03:40:56 cheshire
+Move mDNSAddrIsDNSMulticast() from DNSCommon.h to mDNSEmbeddedAPI.h so embedded clients can use it
+
+Revision 1.4 2004/01/24 03:38:27 cheshire
+Fix minor syntactic error: Headers should use "extern" declarations, not "mDNSexport"
+
+Revision 1.3 2004/01/23 23:23:14 ksekar
+Added TCP support for truncated unicast messages.
+
+Revision 1.2 2004/01/21 21:12:23 cheshire
+Add missing newline at end of file to make Unix tools happier
+
+Revision 1.1 2003/12/13 03:05:27 ksekar
+<rdar://problem/3192548>: DynDNS: Unicast query of service records
+
+
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifndef __DNSCOMMON_H_
+#define __DNSCOMMON_H_
+
+#include "mDNSEmbeddedAPI.h"
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+
+// ***************************************************************************
+#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 = 0x0F, // Response code
+ kDNSFlag1_RC_NoErr = 0x00,
+ kDNSFlag1_RC_FmtErr = 0x01,
+ kDNSFlag1_RC_SrvErr = 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 const NetworkInterfaceInfo *GetFirstActiveInterface(const NetworkInterfaceInfo *intf);
+extern mDNSInterfaceID GetNextActiveInterfaceID(const NetworkInterfaceInfo *intf);
+
+extern mDNSu32 mDNSRandom(mDNSu32 max);
+extern mDNSu32 mDNSRandomFromFixedSeed(mDNSu32 seed, mDNSu32 max);
+
+
+// ***************************************************************************
+#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 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 *name, mDNSu32 val, mDNSBool RichText);
+extern void mDNS_HostNameCallback(mDNS *const m, AuthRecord *const rr, mStatus result);
+#define ValidateDomainName(N) (DomainNameLength(N) <= MAX_DOMAIN_NAME)
+
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - Resource Record Utility Functions
+#endif
+
+extern mDNSu32 RDataHashValue(mDNSu16 const rdlength, const RDataBody *const rdb);
+
+extern mDNSBool SameRDataBody(const ResourceRecord *const r1, const RDataBody *const r2);
+extern mDNSBool SameRData(const ResourceRecord *const r1, const ResourceRecord *const r2);
+
+extern mDNSBool ResourceRecordAnswersQuestion(const ResourceRecord *const rr, const DNSQuestion *const q);
+
+extern mDNSBool SameResourceRecord(ResourceRecord *r1, ResourceRecord *r2);
+
+extern mDNSu16 GetRDLength(const ResourceRecord *const rr, mDNSBool estimate);
+
+#define GetRRDomainNameTarget(RR) ( \
+ ((RR)->rrtype == kDNSType_CNAME || (RR)->rrtype == kDNSType_PTR || (RR)->rrtype == kDNSType_NS) \
+ ? &(RR)->rdata->u.name : \
+ ((RR)->rrtype == kDNSType_SRV ) ? &(RR)->rdata->u.srv.target : mDNSNULL )
+
+extern mDNSBool ValidateRData(const mDNSu16 rrtype, const mDNSu16 rdlength, const RData *const rd);
+#define LocalRecordReady(X) ((X)->resrec.RecordType != kDNSRecordTypeUnique && (X)->resrec.RecordType != kDNSRecordTypeDeregistering)
+
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#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, ResourceRecord *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
+
+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)->h.numAnswers || (msg)->h.numAuthorities || (msg)->h.numAdditionals) ? (msg)->data + NormalMaxDNSMessageData : (msg)->data + AbsoluteMaxDNSMessageData)
+
+#define PutResourceRecordTTLJumbo(msg, ptr, count, rr, ttl) PutResourceRecordTTLWithLimit((msg), (ptr), (count), (rr), (ttl), \
+ (msg)->data + AbsoluteMaxDNSMessageData)
+
+extern mDNSu8 *PutResourceRecordCappedTTL(DNSMessage *const msg, mDNSu8 *ptr, mDNSu16 *count, ResourceRecord *rr, mDNSu32 maxttl);
+
+extern mDNSu8 *putEmptyResourceRecord(DNSMessage *const msg, mDNSu8 *ptr, const mDNSu8 *const limit, mDNSu16 *count, const AuthRecord *rr);
+
+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(domainname *name, DNSMessage *msg, mDNSu8 *ptr, mDNSu8 *end);
+
+extern mDNSu8 *putDeletionRecord(DNSMessage *msg, mDNSu8 *ptr, ResourceRecord *rr);
+
+extern mDNSu8 *putDeleteRRSet(DNSMessage *msg, mDNSu8 *ptr, const domainname *name, mDNSu16 rrtype);
+
+extern mDNSu8 *putDeleteAllRRSets(DNSMessage *msg, mDNSu8 *ptr, const domainname *name);
+
+extern mDNSu8 *putUpdateLease(DNSMessage *msg, mDNSu8 *end, mDNSu32 lease);
+
+#define PutResourceRecord(MSG, P, C, RR) PutResourceRecordTTL((MSG), (P), (C), (RR), (RR)->rroriginalttl)
+
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - DNS Message Parsing Functions
+#endif
+
+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 *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);
+
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark -
+#pragma mark - Packet Sending Functions
+#endif
+
+extern mStatus mDNSSendDNSMessage(const mDNS *const m, DNSMessage *const msg, mDNSu8 *end,
+ mDNSInterfaceID InterfaceID, const mDNSAddr *dst, mDNSIPPort dstport, int sd, uDNS_AuthInfo *authInfo);
+
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - RR List Management & Task Management
+#endif
+
+extern void mDNS_Lock(mDNS *const m);
+extern void mDNS_Unlock(mDNS *const m);
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif // __DNSCOMMON_H_
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/DNSDigest.c b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/DNSDigest.c
new file mode 100644
index 0000000000..6d0346c4f2
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/DNSDigest.c
@@ -0,0 +1,1465 @@
+/* -*- 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.
+
+ Change History (most recent first):
+
+$Log: DNSDigest.c,v $
+Revision 1.15.2.1 2006/08/29 06:24:22 cheshire
+Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
+
+Revision 1.15 2006/06/20 04:12:30 cheshire
+<rdar://problem/4490961> DNS Update broken
+
+Revision 1.14 2006/02/25 23:12:07 cheshire
+<rdar://problem/4427969> Fix to avoid code generation warning/error on FreeBSD 7
+
+Revision 1.13 2004/12/16 20:12:59 cheshire
+<rdar://problem/3324626> Cache memory management improvements
+
+Revision 1.12 2004/12/03 07:20:50 ksekar
+<rdar://problem/3674208> Wide-Area: Registration of large TXT record fails
+
+Revision 1.11 2004/12/02 01:10:27 cheshire
+Fix to compile cleanly on 64-bit x86
+
+Revision 1.10 2004/11/01 20:36:04 ksekar
+<rdar://problem/3802395> mDNSResponder should not receive Keychain Notifications
+
+Revision 1.9 2004/10/26 09:00:12 cheshire
+Save a few bytes by creating HMAC_MD5_AlgName as a C string instead of a 256-byte object
+
+Revision 1.8 2004/09/17 01:08:48 cheshire
+Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h
+ The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces
+ declared in that file are ONLY appropriate to single-address-space embedded applications.
+ For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used.
+
+Revision 1.7 2004/08/15 18:36:38 cheshire
+Don't use strcpy() and strlen() on "struct domainname" objects;
+use AssignDomainName() and DomainNameLength() instead
+(A "struct domainname" is a collection of packed pascal strings, not a C string.)
+
+Revision 1.6 2004/06/02 00:17:46 ksekar
+Referenced original OpenSSL license headers in source file description.
+
+Revision 1.5 2004/05/20 18:37:37 cheshire
+Fix compiler warnings
+
+Revision 1.4 2004/04/22 20:28:20 cheshire
+Use existing facility of PutResourceRecordTTL() to update count field for us
+
+Revision 1.3 2004/04/22 03:05:28 cheshire
+kDNSClass_ANY should be kDNSQClass_ANY
+
+Revision 1.2 2004/04/15 00:51:28 bradley
+Minor tweaks for Windows and C++ builds. Added casts for signed/unsigned integers and 64-bit pointers.
+Prefix some functions with mDNS to avoid conflicts. Disable benign warnings on Microsoft compilers.
+
+Revision 1.1 2004/04/14 23:09:28 ksekar
+Support for TSIG signed dynamic updates.
+
+
+
+*/
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#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 - 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.c - 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;
+
+
+// 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>
+ */
+# 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"(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--)
+ {
+ memcpy (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)
+ {
+ memcpy (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
+
+
+
+ // ***************************************************************************
+#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 == ' ')
+
+static 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
+
+mDNSexport 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
+mDNSexport void DNSDigest_ConstructHMACKey(uDNS_AuthInfo *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->key.ipad, HMAC_LEN);
+ mDNSPlatformMemZero(info->key.opad, HMAC_LEN);
+ mDNSPlatformMemCopy(key, info->key.ipad, len);
+ mDNSPlatformMemCopy(key, info->key.opad, len);
+
+ // XOR key with ipad and opad values
+ for (i = 0; i < HMAC_LEN; i++)
+ {
+ info->key.ipad[i] ^= HMAC_IPAD;
+ info->key.opad[i] ^= HMAC_OPAD;
+ }
+
+ }
+
+mDNSexport mDNSu8 *DNSDigest_SignMessage(DNSMessage *msg, mDNSu8 **end, mDNSu16 *numAdditionals, uDNS_AuthInfo *info)
+ {
+ AuthRecord tsig;
+ mDNSu8 *countPtr, *rdata;
+ mDNSu32 utc32;
+ mDNSu8 utc48[6];
+ mDNSu8 digest[MD5_LEN];
+ mDNSu8 *ptr = *end;
+ mDNSu32 len;
+ mDNSOpaque16 buf;
+ MD5_CTX c;
+
+ // Init MD5 context, digest inner key pad and message
+ MD5_Init(&c);
+ MD5_Update(&c, info->key.ipad, HMAC_LEN);
+ MD5_Update(&c, (mDNSu8 *)msg, (unsigned long)(*end - (mDNSu8 *)msg));
+
+ // Construct TSIG RR, digesting variables as apporpriate
+ mDNSPlatformMemZero(&tsig, sizeof(AuthRecord));
+ mDNS_SetupResourceRecord(&tsig, mDNSNULL, 0, kDNSType_TSIG, 0, kDNSRecordTypeKnownUnique, mDNSNULL, mDNSNULL);
+
+ // key name
+ AssignDomainName(tsig.resrec.name, &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"); return 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(utc48, rdata, 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 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(digest, &c);
+
+ // perform outer MD5 (outer key pad, inner digest)
+ MD5_Init(&c);
+ MD5_Update(&c, info->key.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(digest, rdata, MD5_LEN); // MAC
+ rdata += MD5_LEN;
+ rdata[0] = msg->h.id.b[0]; // original ID
+ rdata[1] = msg->h.id.b[1];
+ rdata[2] = 0; // no error
+ rdata[3] = 0;
+ 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"); return mDNSNULL; }
+
+ // update num additionals
+ countPtr = (mDNSu8 *)&msg->h.numAdditionals; // increment (network-byte ordered) header value
+ *countPtr++ = (mDNSu8)(*numAdditionals >> 8);
+ *countPtr++ = (mDNSu8)(*numAdditionals & 0xFF);
+
+ return *end;
+ }
+
+#ifdef __cplusplus
+}
+#endif
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/GenLinkedList.c b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/GenLinkedList.c
new file mode 100644
index 0000000000..141c3bc9ca
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/GenLinkedList.c
@@ -0,0 +1,334 @@
+/* -*- 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
+
+ Change History (most recent first):
+
+$Log: GenLinkedList.c,v $
+Revision 1.4 2006/08/14 23:24:56 cheshire
+Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
+
+Revision 1.3 2004/04/22 21:14:42 cheshire
+Fix comment spelling mistake
+
+Revision 1.2 2004/02/05 07:41:08 cheshire
+Add Log header
+
+*/
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#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/usr/src/cmd/cmd-inet/usr.lib/mdnsd/GenLinkedList.h b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/GenLinkedList.h
new file mode 100644
index 0000000000..71863346b0
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/GenLinkedList.h
@@ -0,0 +1,109 @@
+/* -*- 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: interface to generic linked lists.
+
+ Version: 1.0
+ Tabs: 4 spaces
+
+ Change History (most recent first):
+
+$Log: GenLinkedList.h,v $
+Revision 1.3 2006/08/14 23:24:56 cheshire
+Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
+
+Revision 1.2 2004/02/05 07:41:08 cheshire
+Add Log header
+
+*/
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#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/usr/src/cmd/cmd-inet/usr.lib/mdnsd/Makefile b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/Makefile
new file mode 100644
index 0000000000..294f9a4622
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/Makefile
@@ -0,0 +1,83 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+PROG= mdnsd
+MANIFEST= multicast.xml
+
+CMN_DIR= $(SRC)/lib/libdns_sd/common
+CMN_OBJS= dnssd_ipc.o
+CMN_SRCS= $(CMN_OBJS:%.o=$(CMN_DIR)/%.c)
+
+LOCAL_OBJS= DNSCommon.o DNSDigest.o GenLinkedList.o \
+ PlatformCommon.o PosixDaemon.o \
+ mDNS.o mDNSDebug.o mDNSPosix.o mDNSUNP.o \
+ uDNS.o uds_daemon.o
+LOCAL_SRCS= $(LOCAL_OBJS:%.o=%.c)
+
+SRCS= $(LOCAL_SRCS) $(CMN_SRCS)
+OBJS= $(LOCAL_OBJS) $(CMN_OBJS)
+
+
+MDNSFLAGS= -DNOT_HAVE_DAEMON -DNOT_HAVE_SA_LEN \
+ -DLOG_PERROR=0 -DHAVE_SOLARIS -DHAVE_SOLARIS_ZONES \
+ -D_XPG4_2 -D__EXTENSIONS__ -DHAVE_BROKEN_RECVIF_NAME \
+ -DHAVE_IPV6=1 -Dasm=__asm -DMDNSD_NOROOT \
+ -DPID_FILE=\"\" -DMDNSD_USER=\"noaccess\"
+
+include ../../../Makefile.cmd
+
+ROOTMANIFESTDIR= $(ROOTSVCNETWORKDNS)
+$(ROOTMANIFEST) := FILEMODE= 444
+
+.PARALLEL: $(LOCAL_OBJS)
+.WAIT: $(PROG)
+.KEEP_STATE:
+
+all: $(PROG)
+
+$(PROG): $(OBJS)
+ $(LINK.c) $(OBJS) -o $@ $(LDLIBS)
+ $(POST_PROCESS)
+
+include ../Makefile.lib
+
+C99MODE = $(C99_ENABLE)
+CPPFLAGS += -D_REENTRANT $(MDNSFLAGS) -I$(CMN_DIR)
+LDLIBS += -lsocket -lnsl
+
+install: all $(ROOTLIBINETPROG) $(ROOTMANIFEST)
+
+%.o: $(CMN_DIR)/%.c
+ $(COMPILE.c) $(OUTPUT_OPTION) $<
+ $(POST_PROCESS_O)
+
+check: $(CHKMANIFEST)
+
+clean:
+ $(RM) $(OBJS)
+
+lint: lint_SRCS
+
+include ../../../Makefile.targ
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/PlatformCommon.c b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/PlatformCommon.c
new file mode 100644
index 0000000000..00a428f934
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/PlatformCommon.c
@@ -0,0 +1,138 @@
+/* -*- 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.
+
+ Change History (most recent first):
+
+$Log: PlatformCommon.c,v $
+Revision 1.7 2006/08/14 23:24:56 cheshire
+Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
+
+Revision 1.6 2005/04/08 21:30:16 ksekar
+<rdar://problem/4007457> Compiling problems with mDNSResponder-98 on Solaris/Sparc v9
+Patch submitted by Bernd Kuhls
+
+Revision 1.5 2005/02/01 19:33:30 ksekar
+<rdar://problem/3985239> Keychain format too restrictive
+
+Revision 1.4 2005/01/19 19:19:21 ksekar
+<rdar://problem/3960191> Need a way to turn off domain discovery
+
+Revision 1.3 2004/12/13 17:46:52 cheshire
+Use sizeof(buf) instead of fixed constant 1024
+
+Revision 1.2 2004/12/01 03:30:29 cheshire
+<rdar://problem/3889346> Add Unicast DNS support to mDNSPosix
+
+Revision 1.1 2004/12/01 01:51:35 cheshire
+Move ReadDDNSSettingsFromConfFile() from mDNSMacOSX.c to PlatformCommon.c
+
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#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 "mDNSEmbeddedAPI.h" // Defines the interface provided to the client layer above
+#include "PlatformCommon.h"
+
+#ifdef NOT_HAVE_SOCKLEN_T
+ typedef unsigned int socklen_t;
+#endif
+
+// Bind a UDP socket to a global destination to find the default route's interface address
+mDNSexport void FindDefaultRouteIP(mDNSAddr *a)
+ {
+ struct sockaddr_in addr;
+ socklen_t len = sizeof(addr);
+ int sock = socket(AF_INET,SOCK_DGRAM,0);
+ a->type = mDNSAddrType_None;
+ if (sock == -1) return;
+ addr.sin_family = AF_INET;
+ addr.sin_port = 1; // Not important, any port and public address will do
+ addr.sin_addr.s_addr = 0x11111111;
+ if ((connect(sock,(const struct sockaddr*)&addr,sizeof(addr))) == -1) { close(sock); return; }
+ if ((getsockname(sock,(struct sockaddr*)&addr, &len)) == -1) { close(sock); return; }
+ close(sock);
+ a->type = mDNSAddrType_IPv4;
+ a->ip.v4.NotAnInteger = addr.sin_addr.s_addr;
+ }
+
+// 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];
+ char secret[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;
+ GetConfigOption(secret, "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] && secret[0])
+ {
+ // for now we assume keyname = service reg domain and we use same key for service and hostname registration
+ err = mDNS_SetSecretForZone(m, domain, domain, secret);
+ if (err) LogMsg("ERROR: mDNS_SetSecretForZone returned %d for domain %##s", err, domain->c);
+ }
+
+ return;
+
+ badf:
+ LogMsg("ERROR: malformatted config file");
+ if (f) fclose(f);
+ }
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/PlatformCommon.h b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/PlatformCommon.h
new file mode 100644
index 0000000000..98f0e85e09
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/PlatformCommon.h
@@ -0,0 +1,37 @@
+/* -*- 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.
+
+ Change History (most recent first):
+
+$Log: PlatformCommon.h,v $
+Revision 1.4 2006/08/14 23:24:56 cheshire
+Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
+
+Revision 1.3 2005/01/19 19:19:21 ksekar
+<rdar://problem/3960191> Need a way to turn off domain discovery
+
+Revision 1.2 2004/12/01 03:30:29 cheshire
+<rdar://problem/3889346> Add Unicast DNS support to mDNSPosix
+
+Revision 1.1 2004/12/01 01:51:35 cheshire
+Move ReadDDNSSettingsFromConfFile() from mDNSMacOSX.c to PlatformCommon.c
+
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+extern void FindDefaultRouteIP(mDNSAddr *a);
+extern void ReadDDNSSettingsFromConfFile(mDNS *const m, const char *const filename, domainname *const hostname, domainname *const domain, mDNSBool *DomainDiscoveryDisabled);
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/PosixDaemon.c b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/PosixDaemon.c
new file mode 100644
index 0000000000..b6a92caa5b
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/PosixDaemon.c
@@ -0,0 +1,369 @@
+/* -*- 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.
+
+ Change History (most recent first):
+
+$Log: PosixDaemon.c,v $
+Revision 1.29.2.1 2006/08/29 06:24:34 cheshire
+Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
+
+Revision 1.29 2005/08/04 03:37:45 mkrochma
+Temporary workaround to fix posix after mDNS_SetPrimaryInterfaceInfo changed
+
+Revision 1.28 2005/07/19 11:21:09 cheshire
+<rdar://problem/4170449> Unix Domain Socket leak in mdnsd
+
+Revision 1.27 2005/02/04 00:39:59 cheshire
+Move ParseDNSServers() from PosixDaemon.c to mDNSPosix.c so all Posix client layers can use it
+
+Revision 1.26 2005/02/02 02:21:30 cheshire
+Update references to "mDNSResponder" to say "mdnsd" instead
+
+Revision 1.25 2005/01/27 20:01:50 cheshire
+udsSupportRemoveFDFromEventLoop() needs to close the file descriptor as well
+
+Revision 1.24 2005/01/19 19:20:49 ksekar
+<rdar://problem/3960191> Need a way to turn off domain discovery
+
+Revision 1.23 2004/12/16 20:17:11 cheshire
+<rdar://problem/3324626> Cache memory management improvements
+
+Revision 1.22 2004/12/10 13:12:08 cheshire
+Create no-op function RecordUpdatedNiceLabel(), required by uds_daemon.c
+
+Revision 1.21 2004/12/01 20:57:20 ksekar
+<rdar://problem/3873921> Wide Area Service Discovery must be split-DNS aware
+
+Revision 1.20 2004/12/01 04:28:43 cheshire
+<rdar://problem/3872803> Darwin patches for Solaris and Suse
+Use version of daemon() provided in mDNSUNP.c instead of local copy
+
+Revision 1.19 2004/12/01 03:30:29 cheshire
+<rdar://problem/3889346> Add Unicast DNS support to mDNSPosix
+
+Revision 1.18 2004/11/30 22:45:59 cheshire
+Minor code tidying
+
+Revision 1.17 2004/11/30 22:18:59 cheshire
+<rdar://problem/3889351> Posix needs to read the list of unicast DNS servers and set server list
+
+Revision 1.16 2004/09/21 21:05:12 cheshire
+Move duplicate code out of mDNSMacOSX/daemon.c and mDNSPosix/PosixDaemon.c,
+into mDNSShared/uds_daemon.c
+
+Revision 1.15 2004/09/17 01:08:53 cheshire
+Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h
+ The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces
+ declared in that file are ONLY appropriate to single-address-space embedded applications.
+ For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used.
+
+Revision 1.14 2004/09/16 00:24:49 cheshire
+<rdar://problem/3803162> Fix unsafe use of mDNSPlatformTimeNow()
+
+Revision 1.13 2004/08/11 01:59:41 cheshire
+Remove "mDNS *globalInstance" parameter from udsserver_init()
+
+Revision 1.12 2004/06/28 23:19:19 cheshire
+Fix "Daemon_Init declared but never defined" warning on Linux
+
+Revision 1.11 2004/06/25 00:26:27 rpantos
+Changes to fix the Posix build on Solaris.
+
+Revision 1.10 2004/06/08 04:59:40 cheshire
+Tidy up wording -- log messages are already prefixed with "mDNSResponder", so don't need to repeat it
+
+Revision 1.9 2004/05/29 00:14:20 rpantos
+<rdar://problem/3508093> Runtime check to disable prod mdnsd on OS X.
+
+Revision 1.8 2004/04/07 01:19:04 cheshire
+Hash slot value should be unsigned
+
+Revision 1.7 2004/02/14 06:34:57 cheshire
+Use LogMsg instead of fprintf( stderr
+
+Revision 1.6 2004/02/14 01:10:42 rpantos
+Allow daemon to run if 'nobody' is not defined, with a warning. (For Roku HD1000.)
+
+Revision 1.5 2004/02/05 07:45:43 cheshire
+Add Log header
+
+Revision 1.4 2004/01/28 21:14:23 cheshire
+Reconcile debug_mode and gDebugLogging into a single flag (mDNS_DebugMode)
+
+Revision 1.3 2004/01/19 19:51:46 cheshire
+Fix compiler error (mixed declarations and code) on some versions of Linux
+
+Revision 1.2 2003/12/11 03:03:51 rpantos
+Clean up mDNSPosix so that it builds on OS X again.
+
+Revision 1.1 2003/12/08 20:47:02 rpantos
+Add support for mDNSResponder on Linux.
+*/
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#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>
+
+#include "mDNSEmbeddedAPI.h"
+#include "mDNSDebug.h"
+#include "mDNSPosix.h"
+#include "uds_daemon.h"
+#include "PlatformCommon.h"
+#include "mDNSUNP.h"
+
+#ifndef MDNSD_USER
+#define MDNSD_USER "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];
+
+extern const char mDNSResponderVersionString[];
+
+static void Reconfigure(mDNS *m)
+ {
+ mDNSAddr DynDNSIP;
+ mDNS_SetPrimaryInterfaceInfo(m, NULL, NULL, NULL);
+ mDNS_DeleteDNSServers(m);
+ 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);
+ FindDefaultRouteIP(&DynDNSIP);
+ if (DynDNSHostname.c[0]) mDNS_AddDynDNSHostName(m, &DynDNSHostname, NULL, NULL);
+ if (DynDNSIP.type) mDNS_SetPrimaryInterfaceInfo(m, &DynDNSIP, NULL, NULL);
+ }
+
+// Do appropriate things at startup with command line arguments. Calls exit() if unhappy.
+static 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]);
+ }
+
+ 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
+ }
+ }
+
+static void DumpStateLog(mDNS *const m)
+// Dump a little log of what we've been up to.
+ {
+ LogMsgIdent(mDNSResponderVersionString, "---- BEGIN STATE LOG ----");
+ udsserver_info(m);
+ LogMsgIdent(mDNSResponderVersionString, "---- END STATE LOG ----");
+ }
+
+static 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)
+ {
+ #define mDNSRecord mDNSStorage
+ mDNS_PlatformSupport platformStorage;
+ mStatus err;
+
+ bzero(&mDNSRecord, sizeof mDNSRecord);
+ bzero(&platformStorage, sizeof platformStorage);
+
+ ParseCmdLinArgs(argc, argv);
+
+ LogMsgIdent(mDNSResponderVersionString, "starting");
+
+ err = mDNS_Init(&mDNSRecord, &platformStorage, gRRCache, RR_CACHE_SIZE, mDNS_Init_AdvertiseLocalAddresses,
+ mDNS_Init_NoInitCallback, mDNS_Init_NoInitCallbackContext);
+
+ if (mStatus_NoError == err)
+ err = udsserver_init();
+
+ Reconfigure(&mDNSRecord);
+
+ // Now that we're finished with anything privileged, switch over to running as "nobody"
+ if (mStatus_NoError == err)
+ {
+ const struct passwd *pw = getpwnam(MDNSD_USER);
+ if (pw != NULL)
+ setuid(pw->pw_uid);
+ else
+#ifdef MDNSD_NOROOT
+ {
+ LogMsg("WARNING: mdnsd exiting because user \""MDNSD_USER"\" does not exist");
+ err = mStatus_Invalid;
+ }
+#else
+ LogMsg("WARNING: mdnsd continuing as root because user \""MDNSD_USER"\" does not exist");
+#endif
+ }
+
+ if (mStatus_NoError == err)
+ err = MainLoop(&mDNSRecord);
+
+ LogMsgIdent(mDNSResponderVersionString, "stopping");
+
+ mDNS_Close(&mDNSRecord);
+
+ 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 ////////////////////////////////////////////////////////////
+
+#undef LogMalloc
+
+#if MDNS_MALLOC_DEBUGGING >= 2
+#define LogMalloc LogMsg
+#else
+#define LogMalloc(ARGS...) ((void)0)
+#endif
+
+mStatus udsSupportAddFDToEventLoop(int fd, udsEventCallback callback, void *context)
+/* Support routine for uds_daemon.c */
+ {
+ // Depends on the fact that udsEventCallback == mDNSPosixEventCallback
+ return mDNSPosixAddFDToEventLoop(fd, callback, context);
+ }
+
+mStatus udsSupportRemoveFDFromEventLoop(int fd) // Note: This also CLOSES the file descriptor
+ {
+ mStatus err = mDNSPosixRemoveFDFromEventLoop(fd);
+ close(fd);
+ return err;
+ }
+
+mDNSexport void RecordUpdatedNiceLabel(mDNS *const m, mDNSs32 delay)
+ {
+ (void)m;
+ (void)delay;
+ // No-op, for now
+ }
+
+#if MACOSX_MDNS_MALLOC_DEBUGGING >= 1
+
+void *mallocL(char *msg, unsigned int size)
+ {
+ unsigned long *mem = malloc(size+8);
+ if (!mem)
+ {
+ LogMsg("malloc( %s : %d ) failed", msg, size);
+ return(NULL);
+ }
+ else
+ {
+ LogMalloc("malloc( %s : %lu ) = %p", msg, size, &mem[2]);
+ mem[0] = 0xDEAD1234;
+ mem[1] = size;
+ //bzero(&mem[2], size);
+ memset(&mem[2], 0xFF, size);
+// validatelists(&mDNSStorage);
+ return(&mem[2]);
+ }
+ }
+
+void freeL(char *msg, void *x)
+ {
+ if (!x)
+ LogMsg("free( %s @ NULL )!", msg);
+ else
+ {
+ unsigned long *mem = ((unsigned long *)x) - 2;
+ if (mem[0] != 0xDEAD1234)
+ { LogMsg("free( %s @ %p ) !!!! NOT ALLOCATED !!!!", msg, &mem[2]); return; }
+ if (mem[1] > 8000)
+ { LogMsg("free( %s : %ld @ %p) too big!", msg, mem[1], &mem[2]); return; }
+ LogMalloc("free( %s : %ld @ %p)", msg, mem[1], &mem[2]);
+ //bzero(mem, mem[1]+8);
+ memset(mem, 0xDD, mem[1]+8);
+// validatelists(&mDNSStorage);
+ free(mem);
+ }
+ }
+
+#endif // MACOSX_MDNS_MALLOC_DEBUGGING >= 1
+
+// For convenience when using the "strings" command, this is the last thing in the file
+#if mDNSResponderVersion > 1
+mDNSexport const char mDNSResponderVersionString[] = "mDNSResponder-" STRINGIFY(mDNSResponderVersion) " (" __DATE__ " " __TIME__ ") ";
+#else
+mDNSexport const char mDNSResponderVersionString[] = "mDNSResponder (Engineering Build) (" __DATE__ " " __TIME__ ") ";
+#endif
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNS.c b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNS.c
new file mode 100644
index 0000000000..2708aa0cac
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNS.c
@@ -0,0 +1,7362 @@
+/* -*- 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 complile 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.)
+
+ Change History (most recent first):
+
+$Log: mDNS.c,v $
+Revision 1.537.2.1 2006/08/29 06:24:22 cheshire
+Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
+
+Revision 1.537 2006/03/19 02:00:07 cheshire
+<rdar://problem/4073825> Improve logic for delaying packets after repeated interface transitions
+
+Revision 1.536 2006/03/08 23:29:53 cheshire
+<rdar://problem/4468716> Improve "Service Renamed" log message
+
+Revision 1.535 2006/03/02 20:41:17 cheshire
+<rdar://problem/4111464> After record update, old record sometimes remains in cache
+Minor code tidying and comments to reduce the risk of similar programming errors in future
+
+Revision 1.534 2006/03/02 03:25:46 cheshire
+<rdar://problem/4111464> After record update, old record sometimes remains in cache
+Code to harmonize RRSet TTLs was inadvertently rescuing expiring records
+
+Revision 1.533 2006/02/26 00:54:41 cheshire
+Fixes to avoid code generation warning/error on FreeBSD 7
+
+Revision 1.532 2005/12/02 20:24:36 cheshire
+<rdar://problem/4363209> Adjust cutoff time for KA list by one second
+
+Revision 1.531 2005/12/02 19:05:42 cheshire
+Tidy up constants
+
+Revision 1.530 2005/11/07 01:49:48 cheshire
+For consistency, use NonZeroTime() function instead of ?: expression
+
+Revision 1.529 2005/10/25 23:42:24 cheshire
+<rdar://problem/4316057> Error in ResolveSimultaneousProbe() when type or class don't match
+Changed switch statement to an "if"
+
+Revision 1.528 2005/10/25 23:34:22 cheshire
+<rdar://problem/4316048> RequireGoodbye state not set/respected sometimes when machine going to sleep
+
+Revision 1.527 2005/10/25 22:43:59 cheshire
+Add clarifying comments
+
+Revision 1.526 2005/10/20 00:10:33 cheshire
+<rdar://problem/4290265> Add check to avoid crashing NAT gateways that have buggy DNS relay code
+
+Revision 1.525 2005/09/24 00:47:17 cheshire
+Fix comment typos
+
+Revision 1.524 2005/09/16 21:06:49 cheshire
+Use mDNS_TimeNow_NoLock macro, instead of writing "mDNSPlatformRawTime() + m->timenow_adjust" all over the place
+
+Revision 1.523 2005/03/21 00:33:51 shersche
+<rdar://problem/4021486> Fix build warnings on Win32 platform
+
+Revision 1.522 2005/03/04 21:48:12 cheshire
+<rdar://problem/4037283> Fractional time rounded down instead of up on platforms with coarse clock granularity
+
+Revision 1.521 2005/02/25 04:21:00 cheshire
+<rdar://problem/4015377> mDNS -F returns the same domain multiple times with different casing
+
+Revision 1.520 2005/02/16 01:14:11 cheshire
+Convert RR Cache LogOperation() calls to debugf()
+
+Revision 1.519 2005/02/15 01:57:20 cheshire
+When setting "q->LastQTxTime = m->timenow", must also clear q->RecentAnswerPkts to zero
+
+Revision 1.518 2005/02/10 22:35:17 cheshire
+<rdar://problem/3727944> Update name
+
+Revision 1.517 2005/02/03 00:21:21 cheshire
+Update comments about BIND named and zero-length TXT records
+
+Revision 1.516 2005/01/28 06:06:32 cheshire
+Update comment
+
+Revision 1.515 2005/01/27 00:21:49 cheshire
+<rdar://problem/3973798> Remove mDNSResponder sleep/wake syslog message
+
+Revision 1.514 2005/01/21 01:33:45 cheshire
+<rdar://problem/3962979> Shutdown time regression: mDNSResponder not responding to SIGTERM
+
+Revision 1.513 2005/01/21 00:07:54 cheshire
+<rdar://problem/3962717> Infinite loop when the same service is registered twice, and then suffers a name conflict
+
+Revision 1.512 2005/01/20 00:37:45 cheshire
+<rdar://problem/3941448> mDNSResponder crashed in mDNSCoreReceiveResponse
+Take care not to recycle records while they are on the CacheFlushRecords list
+
+Revision 1.511 2005/01/19 22:48:53 cheshire
+<rdar://problem/3955355> Handle services with subtypes correctly when doing mDNS_RenameAndReregisterService()
+
+Revision 1.510 2005/01/19 03:12:45 cheshire
+Move LocalRecordReady() macro from mDNS.c to DNSCommon.h
+
+Revision 1.509 2005/01/19 03:08:49 cheshire
+<rdar://problem/3961051> CPU Spin in mDNSResponder
+Log messages to help catch and report CPU spins
+
+Revision 1.508 2005/01/18 18:56:32 cheshire
+<rdar://problem/3934245> QU responses not promoted to multicast responses when appropriate
+
+Revision 1.507 2005/01/18 01:12:07 cheshire
+<rdar://problem/3956258> Logging into VPN causes mDNSResponder to reissue multicast probes
+
+Revision 1.506 2005/01/17 23:28:53 cheshire
+Fix compile error
+
+Revision 1.505 2005/01/11 02:02:56 shersche
+Move variable declaration to the beginning of statement block
+
+Revision 1.504 2004/12/20 20:24:35 cheshire
+<rdar://problem/3928456> Network efficiency: Don't keep polling if we have at least one unique-type answer
+
+Revision 1.503 2004/12/20 18:41:47 cheshire
+<rdar://problem/3591622> Low memory support: Provide answers even when we don't have cache space
+
+Revision 1.502 2004/12/20 18:04:08 cheshire
+<rdar://problem/3923098> For now, don't put standard wide-area unicast responses in our main cache
+
+Revision 1.501 2004/12/19 23:50:18 cheshire
+<rdar://problem/3751638> kDNSServiceInterfaceIndexLocalOnly should return all local records
+Don't show "No active interface to send" messages for kDNSServiceInterfaceIndexLocalOnly services
+
+Revision 1.500 2004/12/18 03:13:46 cheshire
+<rdar://problem/3751638> kDNSServiceInterfaceIndexLocalOnly should return all local records
+
+Revision 1.499 2004/12/17 23:37:45 cheshire
+<rdar://problem/3485365> Guard against repeating wireless dissociation/re-association
+(and other repetitive configuration changes)
+
+Revision 1.498 2004/12/17 05:25:46 cheshire
+<rdar://problem/3925163> Shorten DNS-SD queries to avoid NAT bugs
+
+Revision 1.497 2004/12/17 03:20:58 cheshire
+<rdar://problem/3925168> Don't send unicast replies we know will be ignored
+
+Revision 1.496 2004/12/16 22:18:26 cheshire
+Make AddressIsLocalSubnet() a little more selective -- ignore point-to-point interfaces
+
+Revision 1.495 2004/12/16 21:27:37 ksekar
+Fixed build failures when compiled with verbose debugging messages
+
+Revision 1.494 2004/12/16 20:46:56 cheshire
+Fix compiler warnings
+
+Revision 1.493 2004/12/16 20:13:00 cheshire
+<rdar://problem/3324626> Cache memory management improvements
+
+Revision 1.492 2004/12/16 08:03:24 shersche
+Fix compilation error when UNICAST_DISABLED is set
+
+Revision 1.491 2004/12/11 01:52:11 cheshire
+<rdar://problem/3785820> Support kDNSServiceFlagsAllowRemoteQuery for registering services too
+
+Revision 1.490 2004/12/10 20:06:25 cheshire
+<rdar://problem/3915074> Reduce egregious stack space usage
+Reduced SendDelayedUnicastResponse() stack frame from 9K to 112 bytes
+
+Revision 1.489 2004/12/10 20:03:43 cheshire
+<rdar://problem/3915074> Reduce egregious stack space usage
+Reduced mDNSCoreReceiveQuery() stack frame from 9K to 144 bytes
+
+Revision 1.488 2004/12/10 19:50:41 cheshire
+<rdar://problem/3915074> Reduce egregious stack space usage
+Reduced SendResponses() stack frame from 9K to 176 bytes
+
+Revision 1.487 2004/12/10 19:39:13 cheshire
+<rdar://problem/3915074> Reduce egregious stack space usage
+Reduced SendQueries() stack frame from 18K to 112 bytes
+
+Revision 1.486 2004/12/10 14:16:17 cheshire
+<rdar://problem/3889788> Relax update rate limiting
+We now allow an average rate of ten updates per minute.
+Updates in excess of that are rate limited, but more gently than before.
+
+Revision 1.485 2004/12/10 02:09:24 cheshire
+<rdar://problem/3898376> Modify default TTLs
+
+Revision 1.484 2004/12/09 03:15:40 ksekar
+<rdar://problem/3806610> use _legacy instead of _default to find "empty string" browse domains
+
+Revision 1.483 2004/12/07 23:00:14 ksekar
+<rdar://problem/3908336> DNSServiceRegisterRecord() can crash on deregistration:
+Call RecordProbeFailure even if there is no record callback
+
+Revision 1.482 2004/12/07 22:49:06 cheshire
+<rdar://problem/3908850> BIND doesn't allow zero-length TXT records
+
+Revision 1.481 2004/12/07 21:26:04 ksekar
+<rdar://problem/3908336> DNSServiceRegisterRecord() can crash on deregistration
+
+Revision 1.480 2004/12/07 20:42:33 cheshire
+Add explicit context parameter to mDNS_RemoveRecordFromService()
+
+Revision 1.479 2004/12/07 17:50:49 ksekar
+<rdar://problem/3908850> BIND doesn't allow zero-length TXT records
+
+Revision 1.478 2004/12/06 21:15:22 ksekar
+<rdar://problem/3884386> mDNSResponder crashed in CheckServiceRegistrations
+
+Revision 1.477 2004/12/04 02:12:45 cheshire
+<rdar://problem/3517236> mDNSResponder puts LargeCacheRecord on the stack
+
+Revision 1.476 2004/11/29 23:34:31 cheshire
+On platforms with coarse time resolutions, ORing time values with one to ensure they are non-zero
+is crude, and effectively halves the time resolution. The more selective NonZeroTime() function
+only nudges the time value to 1 if the interval calculation happens to result in the value zero.
+
+Revision 1.475 2004/11/29 23:13:31 cheshire
+<rdar://problem/3484552> All unique records in a set should have the cache flush bit set
+Additional check: Make sure we don't unnecessarily send packets containing only additionals.
+(This could occur with multi-packet KA lists, if the answer and additionals were marked
+by the query packet, and then the answer were later suppressed in a subsequent KA packet.)
+
+Revision 1.474 2004/11/29 17:18:12 cheshire
+Remove "Unknown DNS packet type" message for update responses
+
+Revision 1.473 2004/11/25 01:57:52 cheshire
+<rdar://problem/3484552> All unique records in a set should have the cache flush bit set
+
+Revision 1.472 2004/11/25 01:28:09 cheshire
+<rdar://problem/3557050> Need to implement random delay for 'QU' unicast replies (and set cache flush bit too)
+
+Revision 1.471 2004/11/25 01:10:13 cheshire
+Move code to add additional records to a subroutine called AddAdditionalsToResponseList()
+
+Revision 1.470 2004/11/24 21:54:44 cheshire
+<rdar://problem/3894475> mDNSCore not receiving unicast responses properly
+
+Revision 1.469 2004/11/24 04:50:39 cheshire
+Minor tidying
+
+Revision 1.468 2004/11/24 01:47:07 cheshire
+<rdar://problem/3780207> DNSServiceRegisterRecord should call CallBack on success.
+
+Revision 1.467 2004/11/24 01:41:28 cheshire
+Rename CompleteProbing() to AcknowledgeRecord()
+
+Revision 1.466 2004/11/23 21:08:07 ksekar
+Don't use ID to demux multicast/unicast now that unicast uses random IDs
+
+Revision 1.465 2004/11/15 20:09:21 ksekar
+<rdar://problem/3719050> Wide Area support for Add/Remove record
+
+Revision 1.464 2004/11/03 01:44:36 cheshire
+Update debugging messages
+
+Revision 1.463 2004/10/29 02:38:48 cheshire
+Fix Windows compile errors
+
+Revision 1.462 2004/10/28 19:21:07 cheshire
+Guard against registering interface with zero InterfaceID
+
+Revision 1.461 2004/10/28 19:02:16 cheshire
+Remove \n from LogMsg() call
+
+Revision 1.460 2004/10/28 03:24:40 cheshire
+Rename m->CanReceiveUnicastOn as m->CanReceiveUnicastOn5353
+
+Revision 1.459 2004/10/26 22:34:37 cheshire
+<rdar://problem/3468995> Need to protect mDNSResponder from unbounded packet flooding
+
+Revision 1.458 2004/10/26 20:45:28 cheshire
+Show mask in "invalid mask" message
+
+Revision 1.457 2004/10/26 06:28:36 cheshire
+Now that we don't check IP TTL any more, remove associated log message
+
+Revision 1.456 2004/10/26 06:21:42 cheshire
+Adjust mask validity check to allow an all-ones mask (for IPv6 ::1 loopback address)
+
+Revision 1.455 2004/10/26 06:11:40 cheshire
+Add improved logging to aid in diagnosis of <rdar://problem/3842714> mDNSResponder crashed
+
+Revision 1.454 2004/10/23 01:16:00 cheshire
+<rdar://problem/3851677> uDNS operations not always reliable on multi-homed hosts
+
+Revision 1.453 2004/10/22 20:52:06 ksekar
+<rdar://problem/3799260> Create NAT port mappings for Long Lived Queries
+
+Revision 1.452 2004/10/20 01:50:40 cheshire
+<rdar://problem/3844991> Cannot resolve non-local registrations using the mach API
+Implemented ForceMCast mode for AuthRecords as well as for Questions
+
+Revision 1.451 2004/10/19 21:33:15 cheshire
+<rdar://problem/3844991> Cannot resolve non-local registrations using the mach API
+Added flag 'kDNSServiceFlagsForceMulticast'. Passing through an interface id for a unicast name
+doesn't force multicast unless you set this flag to indicate explicitly that this is what you want
+
+Revision 1.450 2004/10/19 17:42:59 ksekar
+Fixed compiler warnings for non-debug builds.
+
+Revision 1.449 2004/10/18 22:57:07 cheshire
+<rdar://problem/3711302> Seen in console: Ignored apparent spoof mDNS Response with TTL 1
+
+Revision 1.448 2004/10/16 00:16:59 cheshire
+<rdar://problem/3770558> Replace IP TTL 255 check with local subnet source address check
+
+Revision 1.447 2004/10/15 00:51:21 cheshire
+<rdar://problem/3711302> Seen in console: Ignored apparent spoof mDNS Response with TTL 1
+
+Revision 1.446 2004/10/14 00:43:34 cheshire
+<rdar://problem/3815984> Services continue to announce SRV and HINFO
+
+Revision 1.445 2004/10/12 21:07:09 cheshire
+Set up m->p in mDNS_Init() before calling mDNSPlatformTimeInit()
+
+Revision 1.444 2004/10/11 17:54:16 ksekar
+Changed hashtable pointer output from debugf to verbosedebugf.
+
+Revision 1.443 2004/10/10 07:05:45 cheshire
+For consistency, use symbol "localdomain" instead of literal string
+
+Revision 1.442 2004/10/08 20:25:10 cheshire
+Change of plan for <rdar://problem/3831716> -- we're not going to do that at this time
+
+Revision 1.441 2004/10/08 03:25:01 ksekar
+<rdar://problem/3831716> domain enumeration should use LLQs
+
+Revision 1.440 2004/10/06 01:44:19 cheshire
+<rdar://problem/3813936> Resolving too quickly sometimes returns stale TXT record
+
+Revision 1.439 2004/10/03 23:14:11 cheshire
+Add "mDNSEthAddr" type and "zeroEthAddr" constant
+
+Revision 1.438 2004/09/29 23:07:04 cheshire
+Patch from Pavel Repin to fix compile error on Windows
+
+Revision 1.437 2004/09/28 02:23:50 cheshire
+<rdar://problem/3637266> Deliver near-pending "remove" events before new "add" events
+Don't need to search the entire cache for nearly-expired records -- just the appropriate hash slot
+For records with the cache flush bit set, defer the decision until the end of the packet
+
+Revision 1.436 2004/09/28 01:27:04 cheshire
+Update incorrect log message
+
+Revision 1.435 2004/09/25 02:41:39 cheshire
+<rdar://problem/3637266> Deliver near-pending "remove" events before new "add" events
+
+Revision 1.434 2004/09/25 02:32:06 cheshire
+Update comments
+
+Revision 1.433 2004/09/25 02:24:27 cheshire
+Removed unused rr->UseCount
+
+Revision 1.432 2004/09/24 21:35:17 cheshire
+<rdar://problem/3561220> Browses are no longer piggybacking on other browses
+TargetPort and TargetQID are allowed to be undefined if no question->Target is set
+
+Revision 1.431 2004/09/24 21:33:12 cheshire
+Adjust comment
+
+Revision 1.430 2004/09/24 02:15:49 cheshire
+<rdar://problem/3680865> Late conflicts don't send goodbye packets on other interfaces
+
+Revision 1.429 2004/09/24 00:20:21 cheshire
+<rdar://problem/3483349> Any rrtype is a conflict for unique records
+
+Revision 1.428 2004/09/24 00:12:25 cheshire
+Get rid of unused RRUniqueOrKnownUnique(RR)
+
+Revision 1.427 2004/09/23 20:44:11 cheshire
+<rdar://problem/3813148> Reduce timeout before expiring records on failure
+
+Revision 1.426 2004/09/23 20:21:07 cheshire
+<rdar://problem/3426876> Refine "immediate answer burst; restarting exponential backoff sequence" logic
+Associate a unique sequence number with each received packet, and only increment the count of recent answer
+packets if the packet sequence number for this answer record is not one we've already seen and counted.
+
+Revision 1.425 2004/09/23 20:14:38 cheshire
+Rename "question->RecentAnswers" to "question->RecentAnswerPkts"
+
+Revision 1.424 2004/09/23 00:58:36 cheshire
+<rdar://problem/3781269> Rate limiting interferes with updating TXT records
+
+Revision 1.423 2004/09/23 00:50:53 cheshire
+<rdar://problem/3419452> Don't send a (DE) if a service is unregistered after wake from sleep
+
+Revision 1.422 2004/09/22 02:34:46 cheshire
+Move definitions of default TTL times from mDNS.c to mDNSEmbeddedAPI.h
+
+Revision 1.421 2004/09/21 23:29:49 cheshire
+<rdar://problem/3680045> DNSServiceResolve should delay sending packets
+
+Revision 1.420 2004/09/21 23:01:42 cheshire
+Update debugf messages
+
+Revision 1.419 2004/09/21 19:51:14 cheshire
+Move "Starting time value" message from mDNS.c to mDNSMacOSX/daemon.c
+
+Revision 1.418 2004/09/21 18:40:17 cheshire
+<rdar://problem/3376752> Adjust default record TTLs
+
+Revision 1.417 2004/09/21 17:32:16 cheshire
+<rdar://problem/3809484> Rate limiting imposed too soon
+
+Revision 1.416 2004/09/20 23:52:01 cheshire
+CFSocket{Puma}.c renamed to mDNSMacOSX{Puma}.c
+
+Revision 1.415 2004/09/18 01:14:09 cheshire
+<rdar://problem/3485375> Resolve() should not bother doing AAAA queries on machines with no IPv6 interfaces
+
+Revision 1.414 2004/09/18 01:06:48 cheshire
+Add comments
+
+Revision 1.413 2004/09/17 01:08:48 cheshire
+Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h
+ The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces
+ declared in that file are ONLY appropriate to single-address-space embedded applications.
+ For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used.
+
+Revision 1.412 2004/09/17 00:46:33 cheshire
+mDNS_TimeNow should take const mDNS parameter
+
+Revision 1.411 2004/09/17 00:31:51 cheshire
+For consistency with ipv6, renamed rdata field 'ip' to 'ipv4'
+
+Revision 1.410 2004/09/17 00:19:10 cheshire
+For consistency with AllDNSLinkGroupv6, rename AllDNSLinkGroup to AllDNSLinkGroupv4
+
+Revision 1.409 2004/09/16 21:59:15 cheshire
+For consistency with zerov6Addr, rename zeroIPAddr to zerov4Addr
+
+Revision 1.408 2004/09/16 21:36:36 cheshire
+<rdar://problem/3803162> Fix unsafe use of mDNSPlatformTimeNow()
+Changes to add necessary locking calls around unicast DNS operations
+
+Revision 1.407 2004/09/16 02:29:39 cheshire
+Moved mDNS_Lock/mDNS_Unlock to DNSCommon.c; Added necessary locking around
+uDNS_ReceiveMsg, uDNS_StartQuery, uDNS_UpdateRecord, uDNS_RegisterService
+
+Revision 1.406 2004/09/16 01:58:14 cheshire
+Fix compiler warnings
+
+Revision 1.405 2004/09/16 00:24:48 cheshire
+<rdar://problem/3803162> Fix unsafe use of mDNSPlatformTimeNow()
+
+Revision 1.404 2004/09/15 21:44:11 cheshire
+<rdar://problem/3681031> Randomize initial timenow_adjust value in mDNS_Init
+Show time value in log to help diagnose errors
+
+Revision 1.403 2004/09/15 00:46:32 ksekar
+Changed debugf to verbosedebugf in CheckCacheExpiration
+
+Revision 1.402 2004/09/14 23:59:55 cheshire
+<rdar://problem/3681031> Randomize initial timenow_adjust value in mDNS_Init
+
+Revision 1.401 2004/09/14 23:27:46 cheshire
+Fix compile errors
+
+Revision 1.400 2004/09/02 03:48:47 cheshire
+<rdar://problem/3709039> Disable targeted unicast query support by default
+1. New flag kDNSServiceFlagsAllowRemoteQuery to indicate we want to allow remote queries for this record
+2. New field AllowRemoteQuery in AuthRecord structure
+3. uds_daemon.c sets AllowRemoteQuery if kDNSServiceFlagsAllowRemoteQuery is set
+4. mDNS.c only answers remote queries if AllowRemoteQuery is set
+
+Revision 1.399 2004/09/02 01:39:40 cheshire
+For better readability, follow consistent convention that QR bit comes first, followed by OP bits
+
+Revision 1.398 2004/09/01 03:59:29 ksekar
+<rdar://problem/3783453>: Conditionally compile out uDNS code on Windows
+
+Revision 1.397 2004/08/25 22:04:25 rpantos
+Fix the standard Windows compile error.
+
+Revision 1.396 2004/08/25 00:37:27 ksekar
+<rdar://problem/3774635>: Cleanup DynDNS hostname registration code
+
+Revision 1.395 2004/08/18 17:21:18 ksekar
+Removed double-call of uDNS_AdvertiseInterface from mDNS_SetFQDNs()
+
+Revision 1.394 2004/08/14 03:22:41 cheshire
+<rdar://problem/3762579> Dynamic DNS UI <-> mDNSResponder glue
+Add GetUserSpecifiedDDNSName() routine
+Convert ServiceRegDomain to domainname instead of C string
+Replace mDNS_GenerateFQDN/mDNS_GenerateGlobalFQDN with mDNS_SetFQDNs
+
+Revision 1.393 2004/08/13 23:42:52 cheshire
+Removed unused "zeroDomainNamePtr"
+
+Revision 1.392 2004/08/13 23:37:02 cheshire
+Now that we do both uDNS and mDNS, global replace "uDNS_info.hostname" with
+"uDNS_info.UnicastHostname" for clarity
+
+Revision 1.391 2004/08/13 23:25:00 cheshire
+Now that we do both uDNS and mDNS, global replace "m->hostname" with
+"m->MulticastHostname" for clarity
+
+Revision 1.390 2004/08/11 02:17:01 cheshire
+<rdar://problem/3514236> Registering service with port number 0 should create a "No Such Service" record
+
+Revision 1.389 2004/08/10 23:19:14 ksekar
+<rdar://problem/3722542>: DNS Extension daemon for Wide Area Service Discovery
+Moved routines/constants to allow extern access for garbage collection daemon
+
+Revision 1.388 2004/07/30 17:40:06 ksekar
+<rdar://problem/3739115>: TXT Record updates not available for wide-area services
+
+Revision 1.387 2004/07/26 22:49:30 ksekar
+<rdar://problem/3651409>: Feature #9516: Need support for NATPMP in client
+
+Revision 1.386 2004/07/13 21:24:24 rpantos
+Fix for <rdar://problem/3701120>.
+
+Revision 1.385 2004/06/18 19:09:59 cheshire
+<rdar://problem/3588761> Current method of doing subtypes causes name collisions
+
+Revision 1.384 2004/06/15 04:31:23 cheshire
+Make sure to clear m->CurrentRecord at the end of AnswerNewLocalOnlyQuestion()
+
+Revision 1.383 2004/06/11 00:04:59 cheshire
+<rdar://problem/3595602> TTL must be greater than zero for DNSServiceRegisterRecord
+
+Revision 1.382 2004/06/08 04:59:40 cheshire
+Tidy up wording -- log messages are already prefixed with "mDNSResponder", so don't need to repeat it
+
+Revision 1.381 2004/06/05 00:57:30 cheshire
+Remove incorrect LogMsg()
+
+Revision 1.380 2004/06/05 00:04:26 cheshire
+<rdar://problem/3668639>: wide-area domains should be returned in reg. domain enumeration
+
+Revision 1.379 2004/05/28 23:42:36 ksekar
+<rdar://problem/3258021>: Feature: DNS server->client notification on record changes (#7805)
+
+Revision 1.378 2004/05/25 17:25:25 cheshire
+Remove extraneous blank lines and white space
+
+Revision 1.377 2004/05/18 23:51:25 cheshire
+Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers
+
+Revision 1.376 2004/05/05 18:30:44 ksekar
+Restored surpressed Cache Tail debug messages.
+
+Revision 1.375 2004/04/26 21:36:25 cheshire
+Only send IPv4 (or v6) multicast when IPv4 (or v6) multicast send/receive
+is indicated as being available on that interface
+
+Revision 1.374 2004/04/21 02:53:26 cheshire
+Typo in debugf statement
+
+Revision 1.373 2004/04/21 02:49:11 cheshire
+To reduce future confusion, renamed 'TxAndRx' to 'McastTxRx'
+
+Revision 1.372 2004/04/21 02:38:51 cheshire
+Add debugging checks
+
+Revision 1.371 2004/04/14 23:09:28 ksekar
+Support for TSIG signed dynamic updates.
+
+Revision 1.370 2004/04/09 17:40:26 cheshire
+Remove unnecessary "Multicast" field -- it duplicates the semantics of the existing McastTxRx field
+
+Revision 1.369 2004/04/09 16:34:00 cheshire
+Debugging code for later; currently unused
+
+Revision 1.368 2004/04/02 19:19:48 cheshire
+Add code to do optional logging of multi-packet KA list time intervals
+
+Revision 1.367 2004/03/20 03:16:10 cheshire
+Minor refinement to "Excessive update rate" message
+
+Revision 1.366 2004/03/20 03:12:57 cheshire
+<rdar://problem/3587619>: UpdateCredits not granted promptly enough
+
+Revision 1.365 2004/03/19 23:51:22 cheshire
+Change to use symbolic constant kUpdateCreditRefreshInterval instead of (mDNSPlatformOneSecond * 60)
+
+Revision 1.364 2004/03/13 01:57:33 ksekar
+<rdar://problem/3192546>: DynDNS: Dynamic update of service records
+
+Revision 1.363 2004/03/12 21:00:51 cheshire
+Also show port numbers when logging "apparent spoof mDNS Response" messages
+
+Revision 1.362 2004/03/12 08:58:18 cheshire
+Guard against empty TXT records
+
+Revision 1.361 2004/03/09 03:00:46 cheshire
+<rdar://problem/3581961> Don't take lock until after mDNS_Update() has validated that the data is good.
+
+Revision 1.360 2004/03/08 02:52:41 cheshire
+Minor debugging fix: Make sure 'target' is initialized so we don't crash writing debugging log messages
+
+Revision 1.359 2004/03/02 03:21:56 cheshire
+<rdar://problem/3549576> Properly support "_services._dns-sd._udp" meta-queries
+
+Revision 1.358 2004/02/20 08:18:34 cheshire
+<rdar://problem/3564799>: mDNSResponder sometimes announces AAAA records unnecessarily
+
+Revision 1.357 2004/02/18 01:47:41 cheshire
+<rdar://problem/3553472>: Insufficient delay waiting for multi-packet KA lists causes AirPort traffic storms
+
+Revision 1.356 2004/02/06 23:04:19 ksekar
+Basic Dynamic Update support via mDNS_Register (dissabled via
+UNICAST_REGISTRATION #define)
+
+Revision 1.355 2004/02/05 09:32:33 cheshire
+Fix from Bob Bradley: When using the "%.*s" string form,
+guard against truncating in the middle of a multi-byte UTF-8 character.
+
+Revision 1.354 2004/02/05 09:30:22 cheshire
+Update comments
+
+Revision 1.353 2004/01/28 03:41:00 cheshire
+<rdar://problem/3541946>: Need ability to do targeted queries as well as multicast queries
+
+Revision 1.352 2004/01/28 02:30:07 ksekar
+Added default Search Domains to unicast browsing, controlled via
+Networking sharing prefs pane. Stopped sending unicast messages on
+every interface. Fixed unicast resolving via mach-port API.
+
+Revision 1.351 2004/01/27 20:15:22 cheshire
+<rdar://problem/3541288>: Time to prune obsolete code for listening on port 53
+
+Revision 1.350 2004/01/24 23:38:16 cheshire
+Use mDNSVal16() instead of shifting and ORing operations
+
+Revision 1.349 2004/01/23 23:23:14 ksekar
+Added TCP support for truncated unicast messages.
+
+Revision 1.348 2004/01/22 03:54:11 cheshire
+Create special meta-interface 'mDNSInterface_ForceMCast' (-2),
+which means "do this query via multicast, even if it's apparently a unicast domain"
+
+Revision 1.347 2004/01/22 03:50:49 cheshire
+If the client has specified an explicit InterfaceID, then do query by multicast, not unicast
+
+Revision 1.346 2004/01/22 03:48:41 cheshire
+Make sure uDNS client doesn't accidentally use query ID zero
+
+Revision 1.345 2004/01/22 03:43:08 cheshire
+Export constants like mDNSInterface_LocalOnly so that the client layers can use them
+
+Revision 1.344 2004/01/21 21:53:18 cheshire
+<rdar://problem/3448144>: Don't try to receive unicast responses if we're not the first to bind to the UDP port
+
+Revision 1.343 2003/12/23 00:07:47 cheshire
+Make port number in debug message be five-character field, left justified
+
+Revision 1.342 2003/12/20 01:34:28 cheshire
+<rdar://problem/3515876>: Error putting additional records into packets
+Another fix from Rampi: responseptr needs to be updated inside the "for" loop,
+after every record, not once at the end.
+
+Revision 1.341 2003/12/18 22:56:12 cheshire
+<rdar://problem/3510798>: Reduce syslog messages about ignored spoof packets
+
+Revision 1.340 2003/12/16 02:31:37 cheshire
+Minor update to comments
+
+Revision 1.339 2003/12/13 05:50:33 bradley
+Fixed crash with mDNS_Lock/Unlock being called for the initial GrowCache before the platform
+layer has been initialized. Protect mDNS_reentrancy when completing the core initialization to
+fix a race condition during async initialization. Fixed buffer overrun for 1 byte mDNS_snprintf.
+
+Revision 1.338 2003/12/13 03:05:27 ksekar
+<rdar://problem/3192548>: DynDNS: Unicast query of service records
+
+Revision 1.337 2003/12/01 21:46:05 cheshire
+mDNS_StartQuery returns mStatus_BadInterfaceErr if the specified interface does not exist
+
+Revision 1.336 2003/12/01 21:26:19 cheshire
+Guard against zero-length sbuffer in mDNS_vsnprintf()
+
+Revision 1.335 2003/12/01 20:27:48 cheshire
+Display IPv6 addresses correctly (e.g. in log messages) on little-endian processors
+
+Revision 1.334 2003/11/20 22:59:53 cheshire
+Changed runtime checks in mDNS.c to be compile-time checks in mDNSEmbeddedAPI.h
+Thanks to Bob Bradley for suggesting the ingenious compiler trick to make this work.
+
+Revision 1.333 2003/11/20 20:49:53 cheshire
+Another fix from HP: Use packedstruct macro to ensure proper packing for on-the-wire packet structures
+
+Revision 1.332 2003/11/20 05:47:37 cheshire
+<rdar://problem/3490355>: Don't exclude known answers whose expiry time is before the next query
+Now that we only include answers in the known answer list if they are less than
+halfway to expiry, the check to also see if we have another query scheduled
+before the record expires is no longer necessary (and in fact, not correct).
+
+Revision 1.331 2003/11/19 22:31:48 cheshire
+When automatically adding A records to SRVs, add them as additionals, not answers
+
+Revision 1.330 2003/11/19 22:28:50 cheshire
+Increment/Decrement mDNS_reentrancy around calls to m->MainCallback()
+to allow client to make mDNS calls (specifically the call to mDNS_GrowCache())
+
+Revision 1.329 2003/11/19 22:19:24 cheshire
+Show log message when ignoring packets with bad TTL.
+This is to help diagnose problems on Linux versions that may not report the TTL reliably.
+
+Revision 1.328 2003/11/19 22:06:38 cheshire
+Show log messages when a service or hostname is renamed
+
+Revision 1.327 2003/11/19 22:03:44 cheshire
+Move common "m->NextScheduledResponse = m->timenow" to before "if" statement
+
+Revision 1.326 2003/11/17 22:27:02 cheshire
+Another fix from ramaprasad.kr@hp.com: Improve reply delay computation
+on platforms that have native clock rates below fifty ticks per second.
+
+Revision 1.325 2003/11/17 20:41:44 cheshire
+Fix some missing mDNS_Lock(m)/mDNS_Unlock(m) calls.
+
+Revision 1.324 2003/11/17 20:36:32 cheshire
+Function rename: Remove "mDNS_" prefix from AdvertiseInterface() and
+DeadvertiseInterface() -- they're internal private routines, not API routines.
+
+Revision 1.323 2003/11/14 20:59:08 cheshire
+Clients can't use AssignDomainName macro because mDNSPlatformMemCopy is defined in mDNSPlatformFunctions.h.
+Best solution is just to combine mDNSEmbeddedAPI.h and mDNSPlatformFunctions.h into a single file.
+
+Revision 1.322 2003/11/14 19:47:52 cheshire
+Define symbol MAX_ESCAPED_DOMAIN_NAME to indicate recommended buffer size for ConvertDomainNameToCString
+
+Revision 1.321 2003/11/14 19:18:34 cheshire
+Move AssignDomainName macro to mDNSEmbeddedAPI.h to that client layers can use it too
+
+Revision 1.320 2003/11/13 06:45:04 cheshire
+Fix compiler warning on certain compilers
+
+Revision 1.319 2003/11/13 00:47:40 cheshire
+<rdar://problem/3437556> We should delay AAAA record query if A record already in cache.
+
+Revision 1.318 2003/11/13 00:33:26 cheshire
+Change macro "RRIsAddressType" to "RRTypeIsAddressType"
+
+Revision 1.317 2003/11/13 00:10:49 cheshire
+<rdar://problem/3436412>: Verify that rr data is different before updating.
+
+Revision 1.316 2003/11/08 23:37:54 cheshire
+Give explicit zero initializers to blank static structure, required by certain compilers.
+(Thanks to ramaprasad.kr@hp.com for reporting this.)
+
+Revision 1.315 2003/11/07 03:32:56 cheshire
+<rdar://problem/3472153> mDNSResponder delivers answers in inconsistent order
+This is the real fix. Checkin 1.312 was overly simplistic; Calling GetFreeCacheRR() can sometimes
+purge records from the cache, causing tail pointer *rp to be stale on return. The correct fix is
+to maintain a system-wide tail pointer for each cache slot, and then if neccesary GetFreeCacheRR()
+can update this pointer, so that mDNSCoreReceiveResponse() appends records in the right place.
+
+Revision 1.314 2003/11/07 03:19:49 cheshire
+Minor variable renaming for clarity
+
+Revision 1.313 2003/11/07 03:14:49 cheshire
+Previous checkin proved to be overly simplistic; reversing
+
+Revision 1.312 2003/11/03 23:45:15 cheshire
+<rdar://problem/3472153> mDNSResponder delivers answers in inconsistent order
+Build cache lists in FIFO order, not customary C LIFO order
+(Append new elements to tail of cache list, instead of prepending at the head.)
+
+Revision 1.311 2003/10/09 18:00:11 cheshire
+Another compiler warning fix.
+
+Revision 1.310 2003/10/07 20:27:05 cheshire
+Patch from Bob Bradley, to fix warning and compile error on Windows
+
+Revision 1.309 2003/09/26 01:06:36 cheshire
+<rdar://problem/3427923> Set kDNSClass_UniqueRRSet bit for updates too
+Made new routine HaveSentEntireRRSet() to check if flag should be set
+
+Revision 1.308 2003/09/23 01:05:01 cheshire
+Minor changes to comments and debugf() message
+
+Revision 1.307 2003/09/09 20:13:30 cheshire
+<rdar://problem/3411105> Don't send a Goodbye record if we never announced it
+Ammend checkin 1.304: Off-by-one error: By this place in the function we've already decremented
+rr->AnnounceCount, so the check needs to be for InitialAnnounceCount-1, not InitialAnnounceCount
+
+Revision 1.306 2003/09/09 03:00:03 cheshire
+<rdar://problem/3413099> Services take a long time to disappear when switching networks.
+Added two constants: kDefaultReconfirmTimeForNoAnswer and kDefaultReconfirmTimeForCableDisconnect
+
+Revision 1.305 2003/09/09 02:49:31 cheshire
+<rdar://problem/3413975> Initial probes and queries not grouped on wake-from-sleep
+
+Revision 1.304 2003/09/09 02:41:19 cheshire
+<rdar://problem/3411105> Don't send a Goodbye record if we never announced it
+
+Revision 1.303 2003/09/05 19:55:02 cheshire
+<rdar://problem/3409533> Include address records when announcing SRV records
+
+Revision 1.302 2003/09/05 00:01:36 cheshire
+<rdar://problem/3407549> Don't accelerate queries that have large KA lists
+
+Revision 1.301 2003/09/04 22:51:13 cheshire
+<rdar://problem/3398213> Group probes and goodbyes better
+
+Revision 1.300 2003/09/03 02:40:37 cheshire
+<rdar://problem/3404842> mDNSResponder complains about '_'s
+Underscores are not supposed to be legal in standard DNS names, but IANA appears
+to have allowed them in previous service name registrations, so we should too.
+
+Revision 1.299 2003/09/03 02:33:09 cheshire
+<rdar://problem/3404795> CacheRecordRmv ERROR
+Don't update m->NewQuestions until *after* CheckCacheExpiration();
+
+Revision 1.298 2003/09/03 01:47:01 cheshire
+<rdar://problem/3319418> Services always in a state of flux
+Change mDNS_Reconfirm_internal() minimum timeout from 5 seconds to 45-60 seconds
+
+Revision 1.297 2003/08/29 19:44:15 cheshire
+<rdar://problem/3400967> Traffic reduction: Eliminate synchronized QUs when a new service appears
+1. Use m->RandomQueryDelay to impose a random delay in the range 0-500ms on queries
+ that already have at least one unique answer in the cache
+2. For these queries, go straight to QM, skipping QU
+
+Revision 1.296 2003/08/29 19:08:21 cheshire
+<rdar://problem/3400986> Traffic reduction: Eliminate huge KA lists after wake from sleep
+Known answers are no longer eligible to go in the KA list if they are more than half-way to their expiry time.
+
+Revision 1.295 2003/08/28 01:10:59 cheshire
+<rdar://problem/3396034> Add syslog message to report when query is reset because of immediate answer burst
+
+Revision 1.294 2003/08/27 02:30:22 cheshire
+<rdar://problem/3395909> Traffic Reduction: Inefficiencies in DNSServiceResolverResolve()
+One more change: "query->GotTXT" is now a straightforward bi-state boolean again
+
+Revision 1.293 2003/08/27 02:25:31 cheshire
+<rdar://problem/3395909> Traffic Reduction: Inefficiencies in DNSServiceResolverResolve()
+
+Revision 1.292 2003/08/21 19:27:36 cheshire
+<rdar://problem/3387878> Traffic reduction: No need to announce record for longer than TTL
+
+Revision 1.291 2003/08/21 18:57:44 cheshire
+<rdar://problem/3387140> Synchronized queries on the network
+
+Revision 1.290 2003/08/21 02:25:23 cheshire
+Minor changes to comments and debugf() messages
+
+Revision 1.289 2003/08/21 02:21:50 cheshire
+<rdar://problem/3386473> Efficiency: Reduce repeated queries
+
+Revision 1.288 2003/08/20 23:39:30 cheshire
+<rdar://problem/3344098> Review syslog messages, and remove as appropriate
+
+Revision 1.287 2003/08/20 20:47:18 cheshire
+Fix compiler warning
+
+Revision 1.286 2003/08/20 02:18:51 cheshire
+<rdar://problem/3344098> Cleanup: Review syslog messages
+
+Revision 1.285 2003/08/20 01:59:06 cheshire
+<rdar://problem/3384478> rdatahash and rdnamehash not updated after changing rdata
+Made new routine SetNewRData() to update rdlength, rdestimate, rdatahash and rdnamehash in one place
+
+Revision 1.284 2003/08/19 22:20:00 cheshire
+<rdar://problem/3376721> Don't use IPv6 on interfaces that have a routable IPv4 address configured
+More minor refinements
+
+Revision 1.283 2003/08/19 22:16:27 cheshire
+Minor fix: Add missing "mDNS_Unlock(m);" in mDNS_DeregisterInterface() error case.
+
+Revision 1.282 2003/08/19 06:48:25 cheshire
+<rdar://problem/3376552> Guard against excessive record updates
+Each record starts with 10 UpdateCredits.
+Every update consumes one UpdateCredit.
+UpdateCredits are replenished at a rate of one one per minute, up to a maximum of 10.
+As the number of UpdateCredits declines, the number of announcements is similarly scaled back.
+When fewer than 5 UpdateCredits remain, the first announcement is also delayed by an increasing amount.
+
+Revision 1.281 2003/08/19 04:49:28 cheshire
+<rdar://problem/3368159> Interaction between v4, v6 and dual-stack hosts not working quite right
+1. A dual-stack host should only suppress its own query if it sees the same query from other hosts on BOTH IPv4 and IPv6.
+2. When we see the first v4 (or first v6) member of a group, we re-trigger questions and probes on that interface.
+3. When we see the last v4 (or v6) member of a group go away, we revalidate all the records received on that interface.
+
+Revision 1.280 2003/08/19 02:33:36 cheshire
+Update comments
+
+Revision 1.279 2003/08/19 02:31:11 cheshire
+<rdar://problem/3378386> mDNSResponder overenthusiastic with final expiration queries
+Final expiration queries now only mark the question for sending on the particular interface
+pertaining to the record that's expiring.
+
+Revision 1.278 2003/08/18 22:53:37 cheshire
+<rdar://problem/3382647> mDNSResponder divide by zero in mDNSPlatformRawTime()
+
+Revision 1.277 2003/08/18 19:05:44 cheshire
+<rdar://problem/3382423> UpdateRecord not working right
+Added "newrdlength" field to hold new length of updated rdata
+
+Revision 1.276 2003/08/16 03:39:00 cheshire
+<rdar://problem/3338440> InterfaceID -1 indicates "local only"
+
+Revision 1.275 2003/08/16 02:51:27 cheshire
+<rdar://problem/3366590> mDNSResponder takes too much RPRVT
+Don't try to compute namehash etc, until *after* validating the name
+
+Revision 1.274 2003/08/16 01:12:40 cheshire
+<rdar://problem/3366590> mDNSResponder takes too much RPRVT
+Now that the minimum rdata object size has been reduced to 64 bytes, it is no longer safe to do a
+simple C structure assignment of a domainname, because that object is defined to be 256 bytes long,
+and in the process of copying it, the C compiler may run off the end of the rdata object into
+unmapped memory. All assignments of domainname objects of uncertain size are now replaced with a
+call to the macro AssignDomainName(), which is careful to copy only as many bytes as are valid.
+
+Revision 1.273 2003/08/15 20:16:02 cheshire
+<rdar://problem/3366590> mDNSResponder takes too much RPRVT
+We want to avoid touching the rdata pages, so we don't page them in.
+1. RDLength was stored with the rdata, which meant touching the page just to find the length.
+ Moved this from the RData to the ResourceRecord object.
+2. To avoid unnecessarily touching the rdata just to compare it,
+ compute a hash of the rdata and store the hash in the ResourceRecord object.
+
+Revision 1.272 2003/08/14 19:29:04 cheshire
+<rdar://problem/3378473> Include cache records in SIGINFO output
+Moved declarations of DNSTypeName() and GetRRDisplayString to mDNSEmbeddedAPI.h so daemon.c can use them
+
+Revision 1.271 2003/08/14 02:17:05 cheshire
+<rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord
+
+Revision 1.270 2003/08/13 17:07:28 ksekar
+<rdar://problem/3376458>: Extra RR linked to list even if registration fails - causes crash
+Added check to result of mDNS_Register() before linking extra record into list.
+
+Revision 1.269 2003/08/12 19:56:23 cheshire
+Update to APSL 2.0
+
+Revision 1.268 2003/08/12 15:01:10 cheshire
+Add comments
+
+Revision 1.267 2003/08/12 14:59:27 cheshire
+<rdar://problem/3374490> Rate-limiting blocks some legitimate responses
+When setting LastMCTime also record LastMCInterface. When checking LastMCTime to determine
+whether to suppress the response, also check LastMCInterface to see if it matches.
+
+Revision 1.266 2003/08/12 12:47:16 cheshire
+In mDNSCoreMachineSleep debugf message, display value of m->timenow
+
+Revision 1.265 2003/08/11 20:04:28 cheshire
+<rdar://problem/3366553> Improve efficiency by restricting cases where we have to walk the entire cache
+
+Revision 1.264 2003/08/09 00:55:02 cheshire
+<rdar://problem/3366553> mDNSResponder is taking 20-30% of the CPU
+Don't scan the whole cache after every packet.
+
+Revision 1.263 2003/08/09 00:35:29 cheshire
+Moved AnswerNewQuestion() later in the file, in preparation for next checkin
+
+Revision 1.262 2003/08/08 19:50:33 cheshire
+<rdar://problem/3370332> Remove "Cache size now xxx" messages
+
+Revision 1.261 2003/08/08 19:18:45 cheshire
+<rdar://problem/3271219> Only retrigger questions on platforms with the "PhantomInterfaces" bug
+
+Revision 1.260 2003/08/08 18:55:48 cheshire
+<rdar://problem/3370365> Guard against time going backwards
+
+Revision 1.259 2003/08/08 18:36:04 cheshire
+<rdar://problem/3344154> Only need to revalidate on interface removal on platforms that have the PhantomInterfaces bug
+
+Revision 1.258 2003/08/08 16:22:05 cheshire
+<rdar://problem/3335473> Need to check validity of TXT (and other) records
+Remove unneeded LogMsg
+
+Revision 1.257 2003/08/07 01:41:08 cheshire
+<rdar://problem/3367346> Ignore packets with invalid source address (all zeroes or all ones)
+
+Revision 1.256 2003/08/06 23:25:51 cheshire
+<rdar://problem/3290674> Increase TTL for A/AAAA/SRV from one minute to four
+
+Revision 1.255 2003/08/06 23:22:50 cheshire
+Add symbolic constants: kDefaultTTLforUnique (one minute) and kDefaultTTLforShared (two hours)
+
+Revision 1.254 2003/08/06 21:33:39 cheshire
+Fix compiler warnings on PocketPC 2003 (Windows CE)
+
+Revision 1.253 2003/08/06 20:43:57 cheshire
+<rdar://problem/3335473> Need to check validity of TXT (and other) records
+Created ValidateDomainName() and ValidateRData(), used by mDNS_Register_internal() and mDNS_Update()
+
+Revision 1.252 2003/08/06 20:35:47 cheshire
+Enhance debugging routine GetRRDisplayString() so it can also be used to display
+other RDataBody objects, not just the one currently attached the given ResourceRecord
+
+Revision 1.251 2003/08/06 19:07:34 cheshire
+<rdar://problem/3366251> mDNSResponder not inhibiting multicast responses as much as it should
+Was checking LastAPTime instead of LastMCTime
+
+Revision 1.250 2003/08/06 19:01:55 cheshire
+Update comments
+
+Revision 1.249 2003/08/06 00:13:28 cheshire
+Tidy up debugf messages
+
+Revision 1.248 2003/08/05 22:20:15 cheshire
+<rdar://problem/3330324> Need to check IP TTL on responses
+
+Revision 1.247 2003/08/05 00:56:39 cheshire
+<rdar://problem/3357075> mDNSResponder sending additional records, even after precursor record suppressed
+
+Revision 1.246 2003/08/04 19:20:49 cheshire
+Add kDNSQType_ANY to list in DNSTypeName() so it can be displayed in debugging messages
+
+Revision 1.245 2003/08/02 01:56:29 cheshire
+For debugging: log message if we ever get more than one question in a truncated packet
+
+Revision 1.244 2003/08/01 23:55:32 cheshire
+Fix for compiler warnings on Windows, submitted by Bob Bradley
+
+Revision 1.243 2003/07/25 02:26:09 cheshire
+Typo: FIxed missing semicolon
+
+Revision 1.242 2003/07/25 01:18:41 cheshire
+Fix memory leak on shutdown in mDNS_Close() (detected in Windows version)
+
+Revision 1.241 2003/07/23 21:03:42 cheshire
+Only show "Found record..." debugf message in verbose mode
+
+Revision 1.240 2003/07/23 21:01:11 cheshire
+<rdar://problem/3340584> Need Nagle-style algorithm to coalesce multiple packets into one
+After sending a packet, suppress further sending for the next 100ms.
+
+Revision 1.239 2003/07/22 01:30:05 cheshire
+<rdar://problem/3329099> Don't try to add the same question to the duplicate-questions list more than once
+
+Revision 1.238 2003/07/22 00:10:20 cheshire
+<rdar://problem/3337355> ConvertDomainLabelToCString() needs to escape escape characters
+
+Revision 1.237 2003/07/19 03:23:13 cheshire
+<rdar://problem/2986147> mDNSResponder needs to receive and cache larger records
+
+Revision 1.236 2003/07/19 03:04:55 cheshire
+Fix warnings; some debugf message improvements
+
+Revision 1.235 2003/07/19 00:03:32 cheshire
+<rdar://problem/3160248> ScheduleNextTask needs to be smarter after a no-op packet is received
+ScheduleNextTask is quite an expensive operation.
+We don't need to do all that work after receiving a no-op packet that didn't change our state.
+
+Revision 1.234 2003/07/18 23:52:11 cheshire
+To improve consistency of field naming, global search-and-replace:
+NextProbeTime -> NextScheduledProbe
+NextResponseTime -> NextScheduledResponse
+
+Revision 1.233 2003/07/18 00:29:59 cheshire
+<rdar://problem/3268878> Remove mDNSResponder version from packet header and use HINFO record instead
+
+Revision 1.232 2003/07/18 00:11:38 cheshire
+Add extra case to switch statements to handle HINFO data for Get, Put and Display
+(In all but GetRDLength(), this is is just a fall-through to kDNSType_TXT)
+
+Revision 1.231 2003/07/18 00:06:37 cheshire
+To make code a little easier to read in GetRDLength(), search-and-replace "rr->rdata->u." with "rd->"
+
+Revision 1.230 2003/07/17 18:16:54 cheshire
+<rdar://problem/3319418> Services always in a state of flux
+In preparation for working on this, made some debugf messages a little more selective
+
+Revision 1.229 2003/07/17 17:35:04 cheshire
+<rdar://problem/3325583> Rate-limit responses, to guard against packet flooding
+
+Revision 1.228 2003/07/16 20:50:27 cheshire
+<rdar://problem/3315761> Need to implement "unicast response" request, using top bit of qclass
+
+Revision 1.227 2003/07/16 05:01:36 cheshire
+Add fields 'LargeAnswers' and 'ExpectUnicastResponse' in preparation for
+<rdar://problem/3315761> Need to implement "unicast response" request, using top bit of qclass
+
+Revision 1.226 2003/07/16 04:51:44 cheshire
+Fix use of constant 'mDNSPlatformOneSecond' where it should have said 'InitialQuestionInterval'
+
+Revision 1.225 2003/07/16 04:46:41 cheshire
+Minor wording cleanup: The correct DNS term is "response", not "reply"
+
+Revision 1.224 2003/07/16 04:39:02 cheshire
+Textual cleanup (no change to functionality):
+Construct "c >= 'A' && c <= 'Z'" appears in too many places; replaced with macro "mDNSIsUpperCase(c)"
+
+Revision 1.223 2003/07/16 00:09:22 cheshire
+Textual cleanup (no change to functionality):
+Construct "((mDNSs32)rr->rroriginalttl * mDNSPlatformOneSecond)" appears in too many places;
+replace with macro "TicksTTL(rr)"
+Construct "rr->TimeRcvd + ((mDNSs32)rr->rroriginalttl * mDNSPlatformOneSecond)"
+replaced with macro "RRExpireTime(rr)"
+
+Revision 1.222 2003/07/15 23:40:46 cheshire
+Function rename: UpdateDupSuppressInfo() is more accurately called ExpireDupSuppressInfo()
+
+Revision 1.221 2003/07/15 22:17:56 cheshire
+<rdar://problem/3328394> mDNSResponder is not being efficient when doing certain queries
+
+Revision 1.220 2003/07/15 02:12:51 cheshire
+Slight tidy-up of debugf messages and comments
+
+Revision 1.219 2003/07/15 01:55:12 cheshire
+<rdar://problem/3315777> Need to implement service registration with subtypes
+
+Revision 1.218 2003/07/14 16:26:06 cheshire
+<rdar://problem/3324795> Duplicate query suppression not working right
+Refinement: Don't record DS information for a question in the first quarter second
+right after we send it -- in the case where a question happens to be accelerated by
+the maximum allowed amount, we don't want it to then be suppressed because the previous
+time *we* sent that question falls (just) within the valid duplicate suppression window.
+
+Revision 1.217 2003/07/13 04:43:53 cheshire
+<rdar://problem/3325169> Services on multiple interfaces not always resolving
+Minor refinement: No need to make address query broader than the original SRV query that provoked it
+
+Revision 1.216 2003/07/13 03:13:17 cheshire
+<rdar://problem/3325169> Services on multiple interfaces not always resolving
+If we get an identical SRV on a second interface, convert address queries to non-specific
+
+Revision 1.215 2003/07/13 02:28:00 cheshire
+<rdar://problem/3325166> SendResponses didn't all its responses
+Delete all references to RRInterfaceActive -- it's now superfluous
+
+Revision 1.214 2003/07/13 01:47:53 cheshire
+Fix one error and one warning in the Windows build
+
+Revision 1.213 2003/07/12 04:25:48 cheshire
+Fix minor signed/unsigned warnings
+
+Revision 1.212 2003/07/12 01:59:11 cheshire
+Minor changes to debugf messages
+
+Revision 1.211 2003/07/12 01:47:01 cheshire
+<rdar://problem/3324495> After name conflict, appended number should be higher than previous number
+
+Revision 1.210 2003/07/12 01:43:28 cheshire
+<rdar://problem/3324795> Duplicate query suppression not working right
+The correct cutoff time for duplicate query suppression is timenow less one-half the query interval.
+The code was incorrectly using the last query time plus one-half the query interval.
+This was only correct in the case where query acceleration was not in effect.
+
+Revision 1.209 2003/07/12 01:27:50 cheshire
+<rdar://problem/3320079> Hostname conflict naming should not use two hyphens
+Fix missing "-1" in RemoveLabelSuffix()
+
+Revision 1.208 2003/07/11 01:32:38 cheshire
+Syntactic cleanup (no change to funcationality): Now that we only have one host name,
+rename field "hostname1" to "hostname", and field "RR_A1" to "RR_A".
+
+Revision 1.207 2003/07/11 01:28:00 cheshire
+<rdar://problem/3161289> No more local.arpa
+
+Revision 1.206 2003/07/11 00:45:02 cheshire
+<rdar://problem/3321909> Client should get callback confirming successful host name registration
+
+Revision 1.205 2003/07/11 00:40:18 cheshire
+Tidy up debug message in HostNameCallback()
+
+Revision 1.204 2003/07/11 00:20:32 cheshire
+<rdar://problem/3320087> mDNSResponder should log a message after 16 unsuccessful probes
+
+Revision 1.203 2003/07/10 23:53:41 cheshire
+<rdar://problem/3320079> Hostname conflict naming should not use two hyphens
+
+Revision 1.202 2003/07/04 02:23:20 cheshire
+<rdar://problem/3311955> Responder too aggressive at flushing stale data
+Changed mDNSResponder to require four unanswered queries before purging a record, instead of two.
+
+Revision 1.201 2003/07/04 01:09:41 cheshire
+<rdar://problem/3315775> Need to implement subtype queries
+Modified ConstructServiceName() to allow three-part service types
+
+Revision 1.200 2003/07/03 23:55:26 cheshire
+Minor change to wording of syslog warning messages
+
+Revision 1.199 2003/07/03 23:51:13 cheshire
+<rdar://problem/3315652>: Lots of "have given xxx answers" syslog warnings
+Added more detailed debugging information
+
+Revision 1.198 2003/07/03 22:19:30 cheshire
+<rdar://problem/3314346> Bug fix in 3274153 breaks TiVo
+Make exception to allow _tivo_servemedia._tcp.
+
+Revision 1.197 2003/07/02 22:33:05 cheshire
+<rdar://problem/2986146> mDNSResponder needs to start with a smaller cache and then grow it as needed
+Minor refinements:
+When cache is exhausted, verify that rrcache_totalused == rrcache_size and report if not
+Allow cache to grow to 512 records before considering it a potential denial-of-service attack
+
+Revision 1.196 2003/07/02 21:19:45 cheshire
+<rdar://problem/3313413> Update copyright notices, etc., in source code comments
+
+Revision 1.195 2003/07/02 19:56:58 cheshire
+<rdar://problem/2986146> mDNSResponder needs to start with a smaller cache and then grow it as needed
+Minor refinement: m->rrcache_active was not being decremented when
+an active record was deleted because its TTL expired
+
+Revision 1.194 2003/07/02 18:47:40 cheshire
+Minor wording change to log messages
+
+Revision 1.193 2003/07/02 02:44:13 cheshire
+Fix warning in non-debug build
+
+Revision 1.192 2003/07/02 02:41:23 cheshire
+<rdar://problem/2986146> mDNSResponder needs to start with a smaller cache and then grow it as needed
+
+Revision 1.191 2003/07/02 02:30:51 cheshire
+HashSlot() returns an array index. It can't be negative; hence it should not be signed.
+
+Revision 1.190 2003/06/27 00:03:05 vlubet
+<rdar://problem/3304625> Merge of build failure fix for gcc 3.3
+
+Revision 1.189 2003/06/11 19:24:03 cheshire
+<rdar://problem/3287141> Crash in SendQueries/SendResponses when no active interfaces
+Slight refinement to previous checkin
+
+Revision 1.188 2003/06/10 20:33:28 cheshire
+<rdar://problem/3287141> Crash in SendQueries/SendResponses when no active interfaces
+
+Revision 1.187 2003/06/10 04:30:44 cheshire
+<rdar://problem/3286234> Need to re-probe/re-announce on configuration change
+Only interface-specific records were re-probing and re-announcing, not non-specific records.
+
+Revision 1.186 2003/06/10 04:24:39 cheshire
+<rdar://problem/3283637> React when we observe other people query unsuccessfully for a record that's in our cache
+Some additional refinements:
+Don't try to do this for unicast-response queries
+better tracking of Qs and KAs in multi-packet KA lists
+
+Revision 1.185 2003/06/10 03:52:49 cheshire
+Update comments and debug messages
+
+Revision 1.184 2003/06/10 02:26:39 cheshire
+<rdar://problem/3283516> mDNSResponder needs an mDNS_Reconfirm() function
+Make mDNS_Reconfirm() call mDNS_Lock(), like the other API routines
+
+Revision 1.183 2003/06/09 18:53:13 cheshire
+Simplify some debugf() statements (replaced block of 25 lines with 2 lines)
+
+Revision 1.182 2003/06/09 18:38:42 cheshire
+<rdar://problem/3285082> Need to be more tolerant when there are mDNS proxies on the network
+Only issue a correction if the TTL in the proxy packet is less than half the correct value.
+
+Revision 1.181 2003/06/07 06:45:05 cheshire
+<rdar://problem/3283666> No need for multiple machines to all be sending the same queries
+
+Revision 1.180 2003/06/07 06:31:07 cheshire
+Create little four-line helper function "FindIdenticalRecordInCache()"
+
+Revision 1.179 2003/06/07 06:28:13 cheshire
+For clarity, change name of "DNSQuestion q" to "DNSQuestion pktq"
+
+Revision 1.178 2003/06/07 06:25:12 cheshire
+Update some comments
+
+Revision 1.177 2003/06/07 04:50:53 cheshire
+<rdar://problem/3283637> React when we observe other people query unsuccessfully for a record that's in our cache
+
+Revision 1.176 2003/06/07 04:33:26 cheshire
+<rdar://problem/3283540> When query produces zero results, call mDNS_Reconfirm() on any antecedent records
+Minor change: Increment/decrement logic for q->CurrentAnswers should be in
+CacheRecordAdd() and CacheRecordRmv(), not AnswerQuestionWithResourceRecord()
+
+Revision 1.175 2003/06/07 04:11:52 cheshire
+Minor changes to comments and debug messages
+
+Revision 1.174 2003/06/07 01:46:38 cheshire
+<rdar://problem/3283540> When query produces zero results, call mDNS_Reconfirm() on any antecedent records
+
+Revision 1.173 2003/06/07 01:22:13 cheshire
+<rdar://problem/3283516> mDNSResponder needs an mDNS_Reconfirm() function
+
+Revision 1.172 2003/06/07 00:59:42 cheshire
+<rdar://problem/3283454> Need some randomness to spread queries on the network
+
+Revision 1.171 2003/06/06 21:41:10 cheshire
+For consistency, mDNS_StopQuery() should return an mStatus result, just like all the other mDNSCore routines
+
+Revision 1.170 2003/06/06 21:38:55 cheshire
+Renamed 'NewData' as 'FreshData' (The data may not be new data, just a refresh of data that we
+already had in our cache. This refreshes our TTL on the data, but the data itself stays the same.)
+
+Revision 1.169 2003/06/06 21:35:55 cheshire
+Fix mis-named macro: GetRRHostNameTarget is really GetRRDomainNameTarget
+(the target is a domain name, but not necessarily a host name)
+
+Revision 1.168 2003/06/06 21:33:31 cheshire
+Instead of using (mDNSPlatformOneSecond/2) all over the place, define a constant "InitialQuestionInterval"
+
+Revision 1.167 2003/06/06 21:30:42 cheshire
+<rdar://problem/3282962> Don't delay queries for shared record types
+
+Revision 1.166 2003/06/06 17:20:14 cheshire
+For clarity, rename question fields name/rrtype/rrclass as qname/qtype/qclass
+(Global search-and-replace; no functional change to code execution.)
+
+Revision 1.165 2003/06/04 02:53:21 cheshire
+Add some "#pragma warning" lines so it compiles clean on Microsoft compilers
+
+Revision 1.164 2003/06/04 01:25:33 cheshire
+<rdar://problem/3274950> Cannot perform multi-packet known-answer suppression messages
+Display time interval between first and subsequent queries
+
+Revision 1.163 2003/06/03 19:58:14 cheshire
+<rdar://problem/3277665> mDNS_DeregisterService() fixes:
+When forcibly deregistering after a conflict, ensure we don't send an incorrect goodbye packet.
+Guard against a couple of possible mDNS_DeregisterService() race conditions.
+
+Revision 1.162 2003/06/03 19:30:39 cheshire
+Minor addition refinements for
+<rdar://problem/3277080> Duplicate registrations not handled as efficiently as they should be
+
+Revision 1.161 2003/06/03 18:29:03 cheshire
+Minor changes to comments and debugf() messages
+
+Revision 1.160 2003/06/03 05:02:16 cheshire
+<rdar://problem/3277080> Duplicate registrations not handled as efficiently as they should be
+
+Revision 1.159 2003/06/03 03:31:57 cheshire
+<rdar://problem/3277033> False self-conflict when there are duplicate registrations on one machine
+
+Revision 1.158 2003/06/02 22:57:09 cheshire
+Minor clarifying changes to comments and log messages;
+IdenticalResourceRecordAnyInterface() is really more accurately called just IdenticalResourceRecord()
+
+Revision 1.157 2003/05/31 00:09:49 cheshire
+<rdar://problem/3274862> Add ability to discover what services are on a network
+
+Revision 1.156 2003/05/30 23:56:49 cheshire
+<rdar://problem/3274847> Crash after error in mDNS_RegisterService()
+Need to set "sr->Extras = mDNSNULL" before returning
+
+Revision 1.155 2003/05/30 23:48:00 cheshire
+<rdar://problem/3274832> Announcements not properly grouped
+Due to inconsistent setting of rr->LastAPTime at different places in the
+code, announcements were not properly grouped into a single packet.
+Fixed by creating a single routine called InitializeLastAPTime().
+
+Revision 1.154 2003/05/30 23:38:14 cheshire
+<rdar://problem/3274814> Fix error in IPv6 reverse-mapping PTR records
+Wrote buffer[32] where it should have said buffer[64]
+
+Revision 1.153 2003/05/30 19:10:56 cheshire
+<rdar://problem/3274153> ConstructServiceName needs to be more restrictive
+
+Revision 1.152 2003/05/29 22:39:16 cheshire
+<rdar://problem/3273209> Don't truncate strings in the middle of a UTF-8 character
+
+Revision 1.151 2003/05/29 06:35:42 cheshire
+<rdar://problem/3272221> mDNSCoreReceiveResponse() purging wrong record
+
+Revision 1.150 2003/05/29 06:25:45 cheshire
+<rdar://problem/3272218> Need to call CheckCacheExpiration() *before* AnswerNewQuestion()
+
+Revision 1.149 2003/05/29 06:18:39 cheshire
+<rdar://problem/3272217> Split AnswerLocalQuestions into CacheRecordAdd and CacheRecordRmv
+
+Revision 1.148 2003/05/29 06:11:34 cheshire
+<rdar://problem/3272214> Report if there appear to be too many "Resolve" callbacks
+
+Revision 1.147 2003/05/29 06:01:18 cheshire
+Change some debugf() calls to LogMsg() calls to help with debugging
+
+Revision 1.146 2003/05/28 21:00:44 cheshire
+Re-enable "immediate answer burst" debugf message
+
+Revision 1.145 2003/05/28 20:57:44 cheshire
+<rdar://problem/3271550> mDNSResponder reports "Cannot perform multi-packet
+known-answer suppression ..." This is a known issue caused by a bug in the OS X 10.2
+version of mDNSResponder, so for now we should suppress this warning message.
+
+Revision 1.144 2003/05/28 18:05:12 cheshire
+<rdar://problem/3009899> mDNSResponder allows invalid service registrations
+Fix silly mistake: old logic allowed "TDP" and "UCP" as valid names
+
+Revision 1.143 2003/05/28 04:31:29 cheshire
+<rdar://problem/3270733> mDNSResponder not sending probes at the prescribed time
+
+Revision 1.142 2003/05/28 03:13:07 cheshire
+<rdar://problem/3009899> mDNSResponder allows invalid service registrations
+Require that the transport protocol be _udp or _tcp
+
+Revision 1.141 2003/05/28 02:19:12 cheshire
+<rdar://problem/3270634> Misleading messages generated by iChat
+Better fix: Only generate the log message for queries where the TC bit is set.
+
+Revision 1.140 2003/05/28 01:55:24 cheshire
+Minor change to log messages
+
+Revision 1.139 2003/05/28 01:52:51 cheshire
+<rdar://problem/3270634> Misleading messages generated by iChat
+
+Revision 1.138 2003/05/27 22:35:00 cheshire
+<rdar://problem/3270277> mDNS_RegisterInterface needs to retrigger questions
+
+Revision 1.137 2003/05/27 20:04:33 cheshire
+<rdar://problem/3269900> mDNSResponder crash in mDNS_vsnprintf()
+
+Revision 1.136 2003/05/27 18:50:07 cheshire
+<rdar://problem/3269768> mDNS_StartResolveService doesn't inform client of port number changes
+
+Revision 1.135 2003/05/26 04:57:28 cheshire
+<rdar://problem/3268953> Delay queries when there are already answers in the cache
+
+Revision 1.134 2003/05/26 04:54:54 cheshire
+<rdar://problem/3268904> sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead
+Accidentally deleted '%' case from the switch statement
+
+Revision 1.133 2003/05/26 03:21:27 cheshire
+Tidy up address structure naming:
+mDNSIPAddr => mDNSv4Addr (for consistency with mDNSv6Addr)
+mDNSAddr.addr.ipv4 => mDNSAddr.ip.v4
+mDNSAddr.addr.ipv6 => mDNSAddr.ip.v6
+
+Revision 1.132 2003/05/26 03:01:26 cheshire
+<rdar://problem/3268904> sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead
+
+Revision 1.131 2003/05/26 00:42:05 cheshire
+<rdar://problem/3268876> Temporarily include mDNSResponder version in packets
+
+Revision 1.130 2003/05/24 16:39:48 cheshire
+<rdar://problem/3268631> SendResponses also needs to handle multihoming better
+
+Revision 1.129 2003/05/23 02:15:37 cheshire
+Fixed misleading use of the term "duplicate suppression" where it should have
+said "known answer suppression". (Duplicate answer suppression is something
+different, and duplicate question suppression is yet another thing, so the use
+of the completely vague term "duplicate suppression" was particularly bad.)
+
+Revision 1.128 2003/05/23 01:55:13 cheshire
+<rdar://problem/3267127> After name change, mDNSResponder needs to re-probe for name uniqueness
+
+Revision 1.127 2003/05/23 01:02:15 ksekar
+<rdar://problem/3032577>: mDNSResponder needs to include unique id in default name
+
+Revision 1.126 2003/05/22 02:29:22 cheshire
+<rdar://problem/2984918> SendQueries needs to handle multihoming better
+Complete rewrite of SendQueries. Works much better now :-)
+
+Revision 1.125 2003/05/22 01:50:45 cheshire
+Fix warnings, and improve log messages
+
+Revision 1.124 2003/05/22 01:41:50 cheshire
+DiscardDeregistrations doesn't need InterfaceID parameter
+
+Revision 1.123 2003/05/22 01:38:55 cheshire
+Change bracketing of #pragma mark
+
+Revision 1.122 2003/05/21 19:59:04 cheshire
+<rdar://problem/3148431> ER: Tweak responder's default name conflict behavior
+Minor refinements; make sure we don't truncate in the middle of a multi-byte UTF-8 character
+
+Revision 1.121 2003/05/21 17:54:07 ksekar
+<rdar://problem/3148431> ER: Tweak responder's default name conflict behavior
+New rename behavior - domain name "foo" becomes "foo--2" on conflict, richtext name becomes "foo (2)"
+
+Revision 1.120 2003/05/19 22:14:14 ksekar
+<rdar://problem/3162914> mDNS probe denials/conflicts not detected unless conflict is of the same type
+
+Revision 1.119 2003/05/16 01:34:10 cheshire
+Fix some warnings
+
+Revision 1.118 2003/05/14 18:48:40 cheshire
+<rdar://problem/3159272> mDNSResponder should be smarter about reconfigurations
+More minor refinements:
+mDNSMacOSX.c needs to do *all* its mDNS_DeregisterInterface calls before freeing memory
+mDNS_DeregisterInterface revalidates cache record when *any* representative of an interface goes away
+
+Revision 1.117 2003/05/14 07:08:36 cheshire
+<rdar://problem/3159272> mDNSResponder should be smarter about reconfigurations
+Previously, when there was any network configuration change, mDNSResponder
+would tear down the entire list of active interfaces and start again.
+That was very disruptive, and caused the entire cache to be flushed,
+and caused lots of extra network traffic. Now it only removes interfaces
+that have really gone, and only adds new ones that weren't there before.
+
+Revision 1.116 2003/05/14 06:51:56 cheshire
+<rdar://problem/3027144> mDNSResponder doesn't refresh server info if changed during sleep
+
+Revision 1.115 2003/05/14 06:44:31 cheshire
+Improve debugging message
+
+Revision 1.114 2003/05/07 01:47:03 cheshire
+<rdar://problem/3250330> Also protect against NULL domainlabels
+
+Revision 1.113 2003/05/07 00:28:18 cheshire
+<rdar://problem/3250330> Need to make mDNSResponder more defensive against bad clients
+
+Revision 1.112 2003/05/06 00:00:46 cheshire
+<rdar://problem/3248914> Rationalize naming of domainname manipulation functions
+
+Revision 1.111 2003/05/05 23:42:08 cheshire
+<rdar://problem/3245631> Resolves never succeed
+Was setting "rr->LastAPTime = timenow - rr->LastAPTime"
+instead of "rr->LastAPTime = timenow - rr->ThisAPInterval"
+
+Revision 1.110 2003/04/30 21:09:59 cheshire
+<rdar://problem/3244727> mDNS_vsnprintf needs to be more defensive against invalid domain names
+
+Revision 1.109 2003/04/26 02:41:56 cheshire
+<rdar://problem/3241281> Change timenow from a local variable to a structure member
+
+Revision 1.108 2003/04/25 01:45:56 cheshire
+<rdar://problem/3240002> mDNS_RegisterNoSuchService needs to include a host name
+
+Revision 1.107 2003/04/25 00:41:31 cheshire
+<rdar://problem/3239912> Create single routine PurgeCacheResourceRecord(), to avoid bugs in future
+
+Revision 1.106 2003/04/22 03:14:45 cheshire
+<rdar://problem/3232229> Include Include instrumented mDNSResponder in panther now
+
+Revision 1.105 2003/04/22 01:07:43 cheshire
+<rdar://problem/3176248> DNSServiceRegistrationUpdateRecord should support a default ttl
+If TTL parameter is zero, leave record TTL unchanged
+
+Revision 1.104 2003/04/21 19:15:52 cheshire
+Fix some compiler warnings
+
+Revision 1.103 2003/04/19 02:26:35 cheshire
+<rdar://problem/3233804> Incorrect goodbye packet after conflict
+
+Revision 1.102 2003/04/17 03:06:28 cheshire
+<rdar://problem/3231321> No need to query again when a service goes away
+Set UnansweredQueries to 2 when receiving a "goodbye" packet
+
+Revision 1.101 2003/04/15 20:58:31 jgraessl
+<rdar://problem/3229014> Added a hash to lookup records in the cache.
+
+Revision 1.100 2003/04/15 18:53:14 cheshire
+<rdar://problem/3229064> Bug in ScheduleNextTask
+mDNS.c 1.94 incorrectly combined two "if" statements into one.
+
+Revision 1.99 2003/04/15 18:09:13 jgraessl
+<rdar://problem/3228892>
+Reviewed by: Stuart Cheshire
+Added code to keep track of when the next cache item will expire so we can
+call TidyRRCache only when necessary.
+
+Revision 1.98 2003/04/03 03:43:55 cheshire
+<rdar://problem/3216837> Off-by-one error in probe rate limiting
+
+Revision 1.97 2003/04/02 01:48:17 cheshire
+<rdar://problem/3212360> mDNSResponder sometimes suffers false self-conflicts when it sees its own packets
+Additional fix pointed out by Josh:
+Also set ProbeFailTime when incrementing NumFailedProbes when resetting a record back to probing state
+
+Revision 1.96 2003/04/01 23:58:55 cheshire
+Minor comment changes
+
+Revision 1.95 2003/04/01 23:46:05 cheshire
+<rdar://problem/3214832> mDNSResponder can get stuck in infinite loop after many location cycles
+mDNS_DeregisterInterface() flushes the RR cache by marking all records received on that interface
+to expire in one second. However, if a mDNS_StartResolveService() call is made in that one-second
+window, it can get an SRV answer from one of those soon-to-be-deleted records, resulting in
+FoundServiceInfoSRV() making an interface-specific query on the interface that was just removed.
+
+Revision 1.94 2003/03/29 01:55:19 cheshire
+<rdar://problem/3212360> mDNSResponder sometimes suffers false self-conflicts when it sees its own packets
+Solution: Major cleanup of packet timing and conflict handling rules
+
+Revision 1.93 2003/03/28 01:54:36 cheshire
+Minor tidyup of IPv6 (AAAA) code
+
+Revision 1.92 2003/03/27 03:30:55 cheshire
+<rdar://problem/3210018> Name conflicts not handled properly, resulting in memory corruption, and eventual crash
+Problem was that HostNameCallback() was calling mDNS_DeregisterInterface(), which is not safe in a callback
+Fixes:
+1. Make mDNS_DeregisterInterface() safe to call from a callback
+2. Make HostNameCallback() use DeadvertiseInterface() instead
+ (it never really needed to deregister the interface at all)
+
+Revision 1.91 2003/03/15 04:40:36 cheshire
+Change type called "mDNSOpaqueID" to the more descriptive name "mDNSInterfaceID"
+
+Revision 1.90 2003/03/14 20:26:37 cheshire
+Reduce debugging messages (reclassify some "debugf" as "verbosedebugf")
+
+Revision 1.89 2003/03/12 19:57:50 cheshire
+Fixed typo in debug message
+
+Revision 1.88 2003/03/12 00:17:44 cheshire
+<rdar://problem/3195426> GetFreeCacheRR needs to be more willing to throw away recent records
+
+Revision 1.87 2003/03/11 01:27:20 cheshire
+Reduce debugging messages (reclassify some "debugf" as "verbosedebugf")
+
+Revision 1.86 2003/03/06 20:44:33 cheshire
+Comment tidyup
+
+Revision 1.85 2003/03/05 03:38:35 cheshire
+<rdar://problem/3185731> Bogus error message in console: died or deallocated, but no record of client can be found!
+Fixed by leaving client in list after conflict, until client explicitly deallocates
+
+Revision 1.84 2003/03/05 01:27:30 cheshire
+<rdar://problem/3185482> Different TTL for multicast versus unicast responses
+When building unicast responses, record TTLs are capped to 10 seconds
+
+Revision 1.83 2003/03/04 23:48:52 cheshire
+<rdar://problem/3188865> Double probes after wake from sleep
+Don't reset record type to kDNSRecordTypeUnique if record is DependentOn another
+
+Revision 1.82 2003/03/04 23:38:29 cheshire
+<rdar://problem/3099194> mDNSResponder needs performance improvements
+Only set rr->CRActiveQuestion to point to the
+currently active representative of a question set
+
+Revision 1.81 2003/02/21 03:35:34 cheshire
+<rdar://problem/3179007> mDNSResponder needs to include AAAA records in additional answer section
+
+Revision 1.80 2003/02/21 02:47:53 cheshire
+<rdar://problem/3099194> mDNSResponder needs performance improvements
+Several places in the code were calling CacheRRActive(), which searched the entire
+question list every time, to see if this cache resource record answers any question.
+Instead, we now have a field "CRActiveQuestion" in the resource record structure
+
+Revision 1.79 2003/02/21 01:54:07 cheshire
+<rdar://problem/3099194> mDNSResponder needs performance improvements
+Switched to using new "mDNS_Execute" model (see "Implementer Notes.txt")
+
+Revision 1.78 2003/02/20 06:48:32 cheshire
+<rdar://problem/3169535> Xserve RAID needs to do interface-specific registrations
+Reviewed by: Josh Graessley, Bob Bradley
+
+Revision 1.77 2003/01/31 03:35:59 cheshire
+<rdar://problem/3147097> mDNSResponder sometimes fails to find the correct results
+When there were *two* active questions in the list, they were incorrectly
+finding *each other* and *both* being marked as duplicates of another question
+
+Revision 1.76 2003/01/29 02:46:37 cheshire
+Fix for IPv6:
+A physical interface is identified solely by its InterfaceID (not by IP and type).
+On a given InterfaceID, mDNSCore may send both v4 and v6 multicasts.
+In cases where the requested outbound protocol (v4 or v6) is not supported on
+that InterfaceID, the platform support layer should simply discard that packet.
+
+Revision 1.75 2003/01/29 01:47:40 cheshire
+Rename 'Active' to 'CRActive' or 'InterfaceActive' for improved clarity
+
+Revision 1.74 2003/01/28 05:26:25 cheshire
+<rdar://problem/3147097> mDNSResponder sometimes fails to find the correct results
+Add 'Active' flag for interfaces
+
+Revision 1.73 2003/01/28 03:45:12 cheshire
+Fixed missing "not" in "!mDNSAddrIsDNSMulticast(dstaddr)"
+
+Revision 1.72 2003/01/28 01:49:48 cheshire
+<rdar://problem/3147097> mDNSResponder sometimes fails to find the correct results
+FindDuplicateQuestion() was incorrectly finding the question itself in the list,
+and incorrectly marking it as a duplicate (of itself), so that it became inactive.
+
+Revision 1.71 2003/01/28 01:41:44 cheshire
+<rdar://problem/3153091> Race condition when network change causes bad stuff
+When an interface goes away, interface-specific questions on that interface become orphaned.
+Orphan questions cause HaveQueries to return true, but there's no interface to send them on.
+Fix: mDNS_DeregisterInterface() now calls DeActivateInterfaceQuestions()
+
+Revision 1.70 2003/01/23 19:00:20 cheshire
+Protect against infinite loops in mDNS_Execute
+
+Revision 1.69 2003/01/21 22:56:32 jgraessl
+<rdar://problem/3124348> service name changes are not properly handled
+Submitted by: Stuart Cheshire
+Reviewed by: Joshua Graessley
+Applying changes for 3124348 to main branch. 3124348 changes went in to a
+branch for SU.
+
+Revision 1.68 2003/01/17 04:09:27 cheshire
+<rdar://problem/3141038> mDNSResponder Resolves are unreliable on multi-homed hosts
+
+Revision 1.67 2003/01/17 03:56:45 cheshire
+Default 24-hour TTL is far too long. Changing to two hours.
+
+Revision 1.66 2003/01/13 23:49:41 jgraessl
+Merged changes for the following fixes in to top of tree:
+<rdar://problem/3086540> computer name changes not handled properly
+<rdar://problem/3124348> service name changes are not properly handled
+<rdar://problem/3124352> announcements sent in pairs, failing chattiness test
+
+Revision 1.65 2002/12/23 22:13:28 jgraessl
+Reviewed by: Stuart Cheshire
+Initial IPv6 support for mDNSResponder.
+
+Revision 1.64 2002/11/26 20:49:06 cheshire
+<rdar://problem/3104543> RFC 1123 allows the first character of a name label to be either a letter or a digit
+
+Revision 1.63 2002/09/21 20:44:49 zarzycki
+Added APSL info
+
+Revision 1.62 2002/09/20 03:25:37 cheshire
+Fix some compiler warnings
+
+Revision 1.61 2002/09/20 01:05:24 cheshire
+Don't kill the Extras list in mDNS_DeregisterService()
+
+Revision 1.60 2002/09/19 23:47:35 cheshire
+Added mDNS_RegisterNoSuchService() function for assertion of non-existence
+of a particular named service
+
+Revision 1.59 2002/09/19 21:25:34 cheshire
+mDNS_snprintf() doesn't need to be in a separate file
+
+Revision 1.58 2002/09/19 04:20:43 cheshire
+Remove high-ascii characters that confuse some systems
+
+Revision 1.57 2002/09/17 01:07:08 cheshire
+Change mDNS_AdvertiseLocalAddresses to be a parameter to mDNS_Init()
+
+Revision 1.56 2002/09/16 19:44:17 cheshire
+Merge in license terms from Quinn's copy, in preparation for Darwin release
+*/
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#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 COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - Program Constants
+#endif
+
+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 mDNSAddr zeroAddr = { mDNSAddrType_None, {{{ 0 }}} };
+
+mDNSexport const mDNSInterfaceID mDNSInterface_Any = 0;
+mDNSexport const mDNSInterfaceID mDNSInterface_LocalOnly = (mDNSInterfaceID)1;
+
+mDNSlocal const mDNSInterfaceID mDNSInterfaceMark = (mDNSInterfaceID)~0;
+
+#define UnicastDNSPortAsNumber 53
+#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
+
+mDNSexport const mDNSIPPort UnicastDNSPort = { { UnicastDNSPortAsNumber >> 8, UnicastDNSPortAsNumber & 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 mDNSv4Addr AllDNSAdminGroup = { { 239, 255, 255, 251 } };
+mDNSexport const mDNSAddr AllDNSLinkGroup_v4 = { mDNSAddrType_IPv4, { { { 224, 0, 0, 251 } } } };
+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 mDNSOpaque16 zeroID = { { 0, 0 } };
+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 } };
+
+// 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.", // Legacy 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 - 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;
+ }
+ }
+ 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 = 8;
+ 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)
+ {
+ 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; }
+ s += mDNS_snprintf(s, mDNS_VACB_Remain(s), "%#s.", a);
+ 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);
+ }
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - General Utility Functions
+#endif
+
+#define InitialQuestionInterval (mDNSPlatformOneSecond/2)
+#define ActiveQuestion(Q) ((Q)->ThisQInterval > 0 && !(Q)->DuplicateOf)
+#define TimeToSendThisQuestion(Q,time) (ActiveQuestion(Q) && (time) - ((Q)->LastQTime + (Q)->ThisQInterval) >= 0)
+
+mDNSlocal void SetNextQueryTime(mDNS *const m, const DNSQuestion *const q)
+ {
+ if (ActiveQuestion(q))
+ if (m->NextScheduledQuery - (q->LastQTime + q->ThisQInterval) > 0)
+ m->NextScheduledQuery = (q->LastQTime + q->ThisQInterval);
+ }
+
+mDNSlocal 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));
+ }
+
+mDNSlocal mDNSBool AddressIsLocalSubnet(mDNS *const m, const mDNSInterfaceID InterfaceID, const mDNSAddr *addr)
+ {
+ NetworkInterfaceInfo *intf;
+
+ if (addr->type == mDNSAddrType_IPv4)
+ {
+ if (addr->ip.v4.b[0] == 169 && addr->ip.v4.b[1] == 254) 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 (addr->ip.v6.b[0] == 0xFE && addr->ip.v6.b[1] == 0x80) 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);
+ }
+
+// 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, mDNSRecordCallback Callback, void *Context)
+ {
+ mDNSPlatformMemZero(&rr->uDNS_info, sizeof(uDNS_RegInfo));
+ // 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.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->HostTarget = mDNSfalse;
+ rr->AllowRemoteQuery = mDNSfalse;
+ rr->ForceMCast = mDNSfalse;
+
+ // Field Group 3: Transient state for Authoritative Records (set in mDNS_Register_internal)
+
+ rr->namestorage.c[0] = 0; // MUST be set by client before calling mDNS_Register()
+ }
+
+// For a single given DNSQuestion, deliver an add/remove result for the single given AuthRecord
+// Used by AnswerLocalQuestions() and AnswerNewLocalOnlyQuestion()
+mDNSlocal void AnswerLocalOnlyQuestionWithResourceRecord(mDNS *const m, DNSQuestion *q, AuthRecord *rr, mDNSBool AddRecord)
+ {
+ // 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->LocalAnswer = mDNStrue;
+ m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback
+ if (q->QuestionCallback)
+ q->QuestionCallback(m, q, &rr->resrec, AddRecord);
+ m->mDNS_reentrancy--; // Decrement to block mDNS API calls again
+ }
+
+// When a new local AuthRecord is created or deleted, AnswerLocalQuestions() runs though our LocalOnlyQuestions delivering answers
+// to each, stopping if it reaches a NewLocalOnlyQuestion -- brand-new questions are handled by AnswerNewLocalOnlyQuestion().
+// If the AuthRecord is marked mDNSInterface_LocalOnly, then we also deliver it to any other questions we have using mDNSInterface_Any.
+// Used by AnswerForNewLocalRecords() and mDNS_Deregister_internal()
+mDNSlocal void AnswerLocalQuestions(mDNS *const m, AuthRecord *rr, mDNSBool AddRecord)
+ {
+ if (m->CurrentQuestion) LogMsg("AnswerLocalQuestions ERROR m->CurrentQuestion already set");
+
+ m->CurrentQuestion = m->LocalOnlyQuestions;
+ while (m->CurrentQuestion && m->CurrentQuestion != m->NewLocalOnlyQuestions)
+ {
+ DNSQuestion *q = m->CurrentQuestion;
+ m->CurrentQuestion = q->next;
+ if (ResourceRecordAnswersQuestion(&rr->resrec, q))
+ AnswerLocalOnlyQuestionWithResourceRecord(m, q, rr, AddRecord); // MUST NOT dereference q again
+ }
+
+ // If this AuthRecord is marked LocalOnly, then we want to deliver it to all local 'mDNSInterface_Any' questions
+ if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly)
+ {
+ m->CurrentQuestion = m->Questions;
+ while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions)
+ {
+ DNSQuestion *q = m->CurrentQuestion;
+ m->CurrentQuestion = q->next;
+ if (ResourceRecordAnswersQuestion(&rr->resrec, q))
+ AnswerLocalOnlyQuestionWithResourceRecord(m, q, rr, AddRecord); // MUST NOT dereference q again
+ }
+ }
+
+ m->CurrentQuestion = mDNSNULL;
+ }
+
+// ***************************************************************************
+#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)10)
+
+// 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) & (kDNSRecordTypeAdvisory | kDNSRecordTypeShared ) ? DefaultAnnounceIntervalForTypeShared : \
+ (X) & (kDNSRecordTypeUnique ) ? DefaultProbeIntervalForTypeUnique : \
+ (X) & (kDNSRecordTypeVerified | kDNSRecordTypeKnownUnique) ? 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
+mDNSlocal mDNSBool SameResourceRecordSignature(const ResourceRecord *const r1, const ResourceRecord *const r2)
+ {
+ if (!r1) { LogMsg("SameResourceRecordSignature ERROR: r1 is NULL"); return(mDNSfalse); }
+ if (!r2) { LogMsg("SameResourceRecordSignature ERROR: r2 is NULL"); return(mDNSfalse); }
+ if (r1->InterfaceID &&
+ r2->InterfaceID &&
+ r1->InterfaceID != r2->InterfaceID) return(mDNSfalse);
+ return(mDNSBool)(
+ r1->rrtype == r2->rrtype &&
+ r1->rrclass == r2->rrclass &&
+ r1->namehash == r2->namehash &&
+ SameDomainName(r1->name, r2->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.
+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) && 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));
+ }
+
+// IdenticalResourceRecord returns true if two resources records have
+// the same name, type, class, and identical rdata (InterfaceID and TTL may differ)
+mDNSlocal mDNSBool IdenticalResourceRecord(const ResourceRecord *const r1, const ResourceRecord *const r2)
+ {
+ if (!r1) { LogMsg("IdenticalResourceRecord ERROR: r1 is NULL"); return(mDNSfalse); }
+ if (!r2) { LogMsg("IdenticalResourceRecord ERROR: r2 is NULL"); return(mDNSfalse); }
+ if (r1->rrtype != r2->rrtype || r1->rrclass != r2->rrclass || r1->namehash != r2->namehash || !SameDomainName(r1->name, r2->name))
+ return(mDNSfalse);
+ return(SameRData(r1, r2));
+ }
+
+// CacheRecord *ks 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)
+ {
+ //LogMsg("ProbeCount %d Next %ld %s",
+ // rr->ProbeCount, (rr->LastAPTime + rr->ThisAPInterval) - m->timenow, ARDisplayString(m, rr));
+ if (m->NextScheduledProbe - (rr->LastAPTime + rr->ThisAPInterval) >= 0)
+ m->NextScheduledProbe = (rr->LastAPTime + rr->ThisAPInterval);
+ }
+ else if (rr->AnnounceCount && ResourceRecordIsValidAnswer(rr))
+ {
+ if (m->NextScheduledResponse - (rr->LastAPTime + rr->ThisAPInterval) >= 0)
+ m->NextScheduledResponse = (rr->LastAPTime + rr->ThisAPInterval);
+ }
+ }
+
+mDNSlocal void InitializeLastAPTime(mDNS *const m, AuthRecord *const rr)
+ {
+ // 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)
+
+ // 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)
+ {
+ m->SuppressProbes = NonZeroTime(m->timenow + DefaultProbeIntervalForTypeUnique);
+ // 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 = m->NextScheduledProbe;
+ // 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 = m->NextScheduledQuery;
+ }
+
+ // We announce to flush stale data from other caches. It is a reasonable assumption that any
+ // old stale copies will probably have the same TTL we're using, so announcing longer than
+ // this serves no purpose -- any stale copies of that record will have expired by then anyway.
+ rr->AnnounceUntil = m->timenow + TicksTTL(rr);
+ rr->LastAPTime = m->SuppressProbes - rr->ThisAPInterval;
+ // 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;
+
+ // If this is a record type that's not going to probe, then 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 "rr->ThisAPInterval / 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 (rr->resrec.RecordType != kDNSRecordTypeUnique)
+ rr->LastAPTime += DefaultProbeIntervalForTypeUnique * DefaultProbeCountForTypeUnique + rr->ThisAPInterval / 2;
+
+ SetNextAnnounceProbeTime(m, rr);
+ }
+
+#define HashSlot(X) (DomainNameHashValue(X) % CACHE_HASH_SLOTS)
+
+mDNSlocal void SetTargetToHostName(mDNS *const m, AuthRecord *const rr)
+ {
+ domainname *target = GetRRDomainNameTarget(&rr->resrec);
+
+ if (!target) debugf("SetTargetToHostName: Don't know how to set the target of rrtype %d", rr->resrec.rrtype);
+
+ if (target && SameDomainName(target, &m->MulticastHostname))
+ debugf("SetTargetToHostName: Target of %##s is already %##s", rr->resrec.name->c, target->c);
+
+ if (target && !SameDomainName(target, &m->MulticastHostname))
+ {
+ AssignDomainName(target, &m->MulticastHostname);
+ SetNewRData(&rr->resrec, mDNSNULL, 0);
+
+ // 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;
+ rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType);
+ InitializeLastAPTime(m,rr);
+ }
+ }
+
+mDNSlocal void AcknowledgeRecord(mDNS *const m, AuthRecord *const rr)
+ {
+ if (!rr->Acknowledged && 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;
+ m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback
+ rr->RecordCallback(m, rr, mStatus_NoError);
+ m->mDNS_reentrancy--; // Decrement to block mDNS API calls again
+ }
+ }
+
+// Two records qualify to be local duplicates if the RecordTypes are the same, or if one is Unique and the other Verified
+#define RecordLDT(A,B) ((A)->resrec.RecordType == (B)->resrec.RecordType || \
+ ((A)->resrec.RecordType | (B)->resrec.RecordType) == (kDNSRecordTypeUnique | kDNSRecordTypeVerified))
+#define RecordIsLocalDuplicate(A,B) \
+ ((A)->resrec.InterfaceID == (B)->resrec.InterfaceID && RecordLDT((A),(B)) && IdenticalResourceRecord(&(A)->resrec, &(B)->resrec))
+
+mDNSlocal 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;
+
+ mDNSPlatformMemZero(&rr->uDNS_info, sizeof(uDNS_RegInfo));
+
+ if ((mDNSs32)rr->resrec.rroriginalttl <= 0)
+ { LogMsg("mDNS_Register_internal: TTL must be 1 - 0x7FFFFFFF %s", ARDisplayString(m, rr)); return(mStatus_BadParamErr); }
+
+#ifndef UNICAST_DISABLED
+ if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly || rr->ForceMCast || IsLocalDomain(rr->resrec.name))
+ rr->uDNS_info.id = zeroID;
+ else return uDNS_RegisterRecord(m, rr);
+#endif
+
+ while (*p && *p != rr) p=&(*p)->next;
+ while (*d && *d != rr) d=&(*d)->next;
+ if (*d || *p)
+ {
+ LogMsg("Error! Tried to register a AuthRecord %p %##s (%s) that's already in the 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)))
+ {
+ 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
+ if (rr->resrec.InterfaceID && rr->resrec.InterfaceID != mDNSInterface_LocalOnly)
+ {
+ NetworkInterfaceInfo *intf;
+ for (intf = m->HostInterfaces; intf; intf = intf->next)
+ if (intf->InterfaceID == rr->resrec.InterfaceID) break;
+ if (!intf)
+ {
+ debugf("mDNS_Register_internal: Bogus InterfaceID %p in resource record", rr->resrec.InterfaceID);
+ return(mStatus_BadReferenceErr);
+ }
+ }
+
+ rr->next = mDNSNULL;
+
+ // Field Group 1: 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->HostTarget && target) target->c[0] = 0;
+
+ // Field Group 2: Transient state for Authoritative Records
+ rr->Acknowledged = mDNSfalse;
+ rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType);
+ rr->AnnounceCount = InitialAnnounceCount;
+ rr->RequireGoodbye = mDNSfalse;
+ rr->LocalAnswer = mDNSfalse;
+ rr->IncludeInProbe = mDNSfalse;
+ rr->ImmedAnswer = mDNSNULL;
+ rr->ImmedUnicast = mDNSfalse;
+ rr->ImmedAdditional = mDNSNULL;
+ rr->SendRNow = mDNSNULL;
+ rr->v4Requester = zerov4Addr;
+ rr->v6Requester = zerov6Addr;
+ rr->NextResponse = mDNSNULL;
+ rr->NR_AnswerTo = mDNSNULL;
+ rr->NR_AdditionalTo = mDNSNULL;
+ rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType);
+ if (!rr->HostTarget) InitializeLastAPTime(m, rr);
+// rr->AnnounceUntil = Set for us in InitializeLastAPTime()
+// 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;
+
+// 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
+
+ if (rr->HostTarget)
+ SetTargetToHostName(m, rr); // Also sets rdlength and rdestimate for us, and calls InitializeLastAPTime();
+ 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); }
+
+ // 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; }
+
+ // 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.rdlength, &rr->resrec.rdata->u);
+
+ if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly)
+ {
+ // If this is supposed to be unique, make sure we don't have any name conflicts
+ if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask)
+ {
+ const AuthRecord *s1 = rr->RRSet ? rr->RRSet : rr;
+ for (r = m->ResourceRecords; r; r=r->next)
+ {
+ const AuthRecord *s2 = r->RRSet ? r->RRSet : r;
+ if (s1 != s2 && SameResourceRecordSignature(&r->resrec, &rr->resrec) && !SameRData(&r->resrec, &rr->resrec))
+ break;
+ }
+ if (r) // If we found a conflict, set RecordType = kDNSRecordTypeDeregistering so we'll deliver the callback
+ {
+ debugf("Name conflict %p %##s (%s)", rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
+ rr->resrec.RecordType = kDNSRecordTypeDeregistering;
+ rr->resrec.rroriginalttl = 0;
+ rr->ImmedAnswer = mDNSInterfaceMark;
+ m->NextScheduledResponse = m->timenow;
+ }
+ }
+ }
+
+ // Now that we've finished building our new record, make sure it's not identical to one we already have
+ for (r = m->ResourceRecords; r; r=r->next) if (RecordIsLocalDuplicate(r, rr)) break;
+
+ if (r)
+ {
+ debugf("Adding to duplicate list %p %s", rr, 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("Adding to active record list %p %s", rr, ARDisplayString(m,rr));
+ if (!m->NewLocalRecords) m->NewLocalRecords = rr;
+ *p = rr;
+ }
+
+ // For records that are not going to probe, acknowledge them right away
+ if (rr->resrec.RecordType != kDNSRecordTypeUnique && rr->resrec.RecordType != kDNSRecordTypeDeregistering)
+ AcknowledgeRecord(m, rr);
+
+ 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;
+ 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); // ... and let the client know
+ }
+
+// mDNS_Dereg_normal is used for most calls to mDNS_Deregister_internal
+// 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_conflict, mDNS_Dereg_repeat } mDNS_Dereg_type;
+
+// 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.
+mDNSlocal 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
+
+#ifndef UNICAST_DISABLED
+ if (!(rr->resrec.InterfaceID == mDNSInterface_LocalOnly || rr->ForceMCast || IsLocalDomain(rr->resrec.name)))
+ return uDNS_DeregisterRecord(m, rr);
+#endif
+
+ 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("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
+ 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->ImmedAnswer = rr->ImmedAnswer;
+ dup->ImmedUnicast = rr->ImmedUnicast;
+ dup->ImmedAdditional = rr->ImmedAdditional;
+ dup->v4Requester = rr->v4Requester;
+ dup->v6Requester = rr->v6Requester;
+ dup->ThisAPInterval = rr->ThisAPInterval;
+ dup->AnnounceUntil = rr->AnnounceUntil;
+ dup->LastAPTime = rr->LastAPTime;
+ dup->LastMCTime = rr->LastMCTime;
+ dup->LastMCInterface = rr->LastMCInterface;
+ rr->RequireGoodbye = 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;
+ if (*p) debugf("DNS_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 %##s (%s) not found in list",
+ rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
+ 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 (RecordType == kDNSRecordTypeShared && rr->RequireGoodbye)
+ {
+ verbosedebugf("mDNS_Deregister_internal: Sending deregister for %##s (%s)",
+ rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
+ rr->resrec.RecordType = kDNSRecordTypeDeregistering;
+ rr->resrec.rroriginalttl = 0;
+ rr->ImmedAnswer = mDNSInterfaceMark;
+ if (m->NextScheduledResponse - (m->timenow + mDNSPlatformOneSecond/10) >= 0)
+ m->NextScheduledResponse = (m->timenow + mDNSPlatformOneSecond/10);
+ }
+ else
+ {
+ *p = rr->next; // Cut this record from the list
+ // If someone is about to look at this, bump the pointer forward
+ if (m->CurrentRecord == rr) m->CurrentRecord = rr->next;
+ if (m->NewLocalRecords == rr) m->NewLocalRecords = rr->next;
+ rr->next = mDNSNULL;
+
+ if (RecordType == kDNSRecordTypeUnregistered)
+ debugf("mDNS_Deregister_internal: Record %##s (%s) already marked kDNSRecordTypeUnregistered",
+ rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
+ else if (RecordType == kDNSRecordTypeDeregistering)
+ debugf("mDNS_Deregister_internal: Record %##s (%s) already marked kDNSRecordTypeDeregistering",
+ rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
+ else
+ {
+ verbosedebugf("mDNS_Deregister_internal: Deleting record for %##s (%s)",
+ rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
+ 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
+
+ if (rr->LocalAnswer) AnswerLocalQuestions(m, rr, mDNSfalse);
+
+ // 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.
+ m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback
+ if (drt != mDNS_Dereg_conflict)
+ {
+ if (rr->RecordCallback) rr->RecordCallback(m, rr, mStatus_MemFree); // MUST NOT touch rr after this
+ }
+ else
+ {
+ RecordProbeFailure(m, rr);
+ if (rr->RecordCallback) rr->RecordCallback(m, rr, mStatus_NameConflict); // MUST NOT touch rr after this
+ // 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; }
+ }
+ }
+ m->mDNS_reentrancy--; // Decrement to block mDNS API calls again
+ }
+ return(mStatus_NoError);
+ }
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#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);
+ }
+ }
+
+mDNSlocal void SendDelayedUnicastResponse(mDNS *const m, const mDNSAddr *const dest, const mDNSInterfaceID InterfaceID)
+ {
+ AuthRecord *rr;
+ AuthRecord *ResponseRecords = mDNSNULL;
+ AuthRecord **nrp = &ResponseRecords;
+
+ // 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;
+ 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, mDNSInterface_Any, dest, MulticastDNSPort, -1, mDNSNULL);
+ }
+ }
+
+mDNSlocal void CompleteDeregistration(mDNS *const m, AuthRecord *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;
+ mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal); // Don't touch rr after this
+ }
+
+// NOTE: 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");
+ m->CurrentRecord = m->ResourceRecords;
+
+ while (m->CurrentRecord)
+ {
+ AuthRecord *rr = m->CurrentRecord;
+ if (rr->resrec.RecordType == kDNSRecordTypeDeregistering)
+ CompleteDeregistration(m, rr); // Don't touch rr after this
+ else
+ m->CurrentRecord = rr->next;
+ }
+ }
+
+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;
+
+ 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) && ResourceRecordIsValidAnswer(rr))
+ {
+ 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) &&
+ 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
+
+ // 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->resrec, &rr->resrec))
+ r2->ImmedAnswer = rr->ImmedAnswer;
+ }
+ 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->resrec, &rr->resrec))
+ 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--;
+ rr->ThisAPInterval *= 2;
+ rr->LastAPTime = m->timenow;
+ if (rr->LastAPTime + rr->ThisAPInterval - rr->AnnounceUntil >= 0) rr->AnnounceCount = 0;
+ 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)
+ {
+ 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
+ // In all cases, if we fail, and we've put at least one answer, we break out of the for loop so we can
+ // send this packet and then try again.
+ // If we have not put even one answer, then we don't bail out. We pretend we succeeded anyway,
+ // because otherwise we'll end up in an infinite loop trying to send a record that will never fit.
+ for (rr = m->ResourceRecords; rr; rr=rr->next)
+ if (rr->SendRNow == intf->InterfaceID)
+ {
+ if (rr->resrec.RecordType == kDNSRecordTypeDeregistering)
+ {
+ newptr = PutResourceRecordTTL(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec, 0);
+ if (!newptr && m->omsg.h.numAnswers) break;
+ numDereg++;
+ responseptr = newptr;
+ }
+ else if (rr->NewRData && !m->SleepState) // If we have new data for this record
+ {
+ RData *OldRData = rr->resrec.rdata;
+ mDNSu16 oldrdlength = rr->resrec.rdlength;
+ // See if we should send a courtesy "goodbye" for the old data before we replace it.
+ if (ResourceRecordIsValidAnswer(rr) && rr->RequireGoodbye)
+ {
+ newptr = PutResourceRecordTTL(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec, 0);
+ if (!newptr && m->omsg.h.numAnswers) break;
+ numDereg++;
+ responseptr = newptr;
+ rr->RequireGoodbye = mDNSfalse;
+ }
+ // Now try to see if we can fit the update in the same packet (not fatal if we can't)
+ 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 = 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) { responseptr = newptr; rr->RequireGoodbye = mDNStrue; }
+ SetNewRData(&rr->resrec, OldRData, oldrdlength);
+ }
+ else
+ {
+ if (rr->resrec.RecordType & kDNSRecordTypeUniqueMask)
+ rr->resrec.rrclass |= kDNSClass_UniqueRRSet; // Temporarily set the cache flush bit so PutResourceRecord will set it
+ newptr = PutResourceRecordTTL(&m->omsg, responseptr, &m->omsg.h.numAnswers, &rr->resrec, m->SleepState ? 0 : rr->resrec.rroriginalttl);
+ rr->resrec.rrclass &= ~kDNSClass_UniqueRRSet; // Make sure to clear cache flush bit back to normal state
+ if (!newptr && m->omsg.h.numAnswers) break;
+ rr->RequireGoodbye = (mDNSu8) (!m->SleepState);
+ if (rr->LastAPTime == m->timenow) numAnnounce++; else numAnswer++;
+ responseptr = newptr;
+ }
+ // 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->resrec, &rr->resrec)) { 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
+ {
+ 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, 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;
+ }
+ }
+ }
+
+ if (m->omsg.h.numAnswers > 0 || m->omsg.h.numAdditionals)
+ {
+ 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, &AllDNSLinkGroup_v4, MulticastDNSPort, -1, mDNSNULL);
+ if (intf->IPv6Available) mDNSSendDNSMessage(m, &m->omsg, responseptr, intf->InterfaceID, &AllDNSLinkGroup_v6, MulticastDNSPort, -1, 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;
+ }
+ }
+
+ // ***
+ // *** 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");
+ m->CurrentRecord = m->ResourceRecords;
+ while (m->CurrentRecord)
+ {
+ rr = m->CurrentRecord;
+ m->CurrentRecord = rr->next;
+
+ if (rr->SendRNow)
+ {
+ if (rr->resrec.InterfaceID != mDNSInterface_LocalOnly)
+ LogMsg("SendResponses: No active interface to send: %s", ARDisplayString(m, rr));
+ rr->SendRNow = mDNSNULL;
+ }
+
+ if (rr->ImmedAnswer)
+ {
+ if (rr->NewRData) CompleteRDataUpdate(m,rr); // Update our rdata, clear the NewRData pointer, and return memory to the client
+
+ if (rr->resrec.RecordType == kDNSRecordTypeDeregistering)
+ 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
+#define CacheCheckGracePeriod(RR) ( \
+ ((RR)->DelayDelivery ) ? (mDNSPlatformOneSecond/10) : \
+ ((RR)->CRActiveQuestion == mDNSNULL ) ? (60 * mDNSPlatformOneSecond) : \
+ ((RR)->UnansweredQueries < MaxUnansweredQueries) ? (TicksTTL(rr)/50) : \
+ ((RR)->resrec.rroriginalttl > 10 ) ? (mDNSPlatformOneSecond) : (mDNSPlatformOneSecond/10))
+
+// Note: MUST call SetNextCacheCheckTime any time we change:
+// rr->TimeRcvd
+// rr->resrec.rroriginalttl
+// rr->UnansweredQueries
+// rr->CRActiveQuestion
+// Also, any time we set rr->DelayDelivery we should call SetNextCacheCheckTime to ensure m->NextCacheCheck is set if necessary
+// Clearing rr->DelayDelivery does not require a call to SetNextCacheCheckTime
+mDNSlocal void SetNextCacheCheckTime(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("SetNextCacheCheckTime: %##s (%s) NextRequiredQuery in %ld sec CacheCheckGracePeriod %d ticks",
+ rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype),
+ (rr->NextRequiredQuery - m->timenow) / mDNSPlatformOneSecond, CacheCheckGracePeriod(rr));
+ }
+
+ if (m->NextCacheCheck - (rr->NextRequiredQuery + CacheCheckGracePeriod(rr)) > 0)
+ m->NextCacheCheck = (rr->NextRequiredQuery + CacheCheckGracePeriod(rr));
+
+ if (rr->DelayDelivery)
+ if (m->NextCacheCheck - rr->DelayDelivery > 0)
+ m->NextCacheCheck = rr->DelayDelivery;
+ }
+
+#define kMinimumReconfirmTime ((mDNSu32)mDNSPlatformOneSecond * 5)
+#define kDefaultReconfirmTimeForWake ((mDNSu32)mDNSPlatformOneSecond * 5)
+#define kDefaultReconfirmTimeForNoAnswer ((mDNSu32)mDNSPlatformOneSecond * 15)
+#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 += mDNSRandomFromFixedSeed(m->RandomReconfirmDelay, interval/3);
+ rr->TimeRcvd = m->timenow - (mDNSs32)interval * 3;
+ rr->resrec.rroriginalttl = (interval * 4 + mDNSPlatformOneSecond - 1) / mDNSPlatformOneSecond;
+ SetNextCacheCheckTime(m, rr);
+ }
+ debugf("mDNS_Reconfirm_internal:%6ld ticks to go for %s", RRExpireTime(rr) - m->timenow, CRDisplayString(m, rr));
+ 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, &q->qname, q->qtype, (mDNSu16)(q->qclass | ucbit));
+ if (!newptr)
+ {
+ debugf("BuildQuestion: No more space in this packet for question %##s", q->qname.c);
+ return(mDNSfalse);
+ }
+ else if (newptr + *answerforecast >= limit)
+ {
+ verbosedebugf("BuildQuestion: Retracting question %##s new forecast total %d",
+ q->qname.c, newptr + *answerforecast - query->data);
+ query->h.numQuestions--;
+ return(mDNSfalse);
+ }
+ else
+ {
+ mDNSu32 forecast = *answerforecast;
+ const mDNSu32 slot = HashSlot(&q->qname);
+ CacheGroup *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->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
+ ResourceRecordAnswersQuestion(&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)
+ {
+ *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 *rr = *ka; *ka = mDNSNULL; ka = &rr->NextInKAList; }
+ return(mDNSfalse); // Return false, so we'll try again in the next packet
+ }
+ }
+
+ // Traffic reduction:
+ // If we already have at least one unique answer in the cache,
+ // OR we have so many shared answers that the KA list is too big to fit in one packet
+ // The we suppress queries number 3 and 5:
+ // Query 1 (immediately; ThisQInterval = 1 sec; request unicast replies)
+ // Query 2 (after 1 second; ThisQInterval = 2 sec; send normally)
+ // Query 3 (after 2 seconds; ThisQInterval = 4 sec; may suppress)
+ // Query 4 (after 4 seconds; ThisQInterval = 8 sec; send normally)
+ // Query 5 (after 8 seconds; ThisQInterval = 16 sec; may suppress)
+ // Query 6 (after 16 seconds; ThisQInterval = 32 sec; send normally)
+ if (q->UniqueAnswers || newptr + forecast >= limit)
+ if (q->ThisQInterval == InitialQuestionInterval * 8 || q->ThisQInterval == InitialQuestionInterval * 32)
+ {
+ query->h.numQuestions--;
+ ka = *kalistptrptr; // Go back to where we started and retract these answer records
+ while (*ka) { CacheRecord *rr = *ka; *ka = mDNSNULL; ka = &rr->NextInKAList; }
+ return(mDNStrue); // Return true: pretend we succeeded, even though we actually suppressed this question
+ }
+
+ // 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) m->ExpectUnicastResponse = 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
+ ResourceRecordAnswersQuestion(&rr->resrec, q)) // which answers our question
+ {
+ rr->UnansweredQueries++; // indicate that we're expecting a response
+ rr->LastUnansweredTime = m->timenow;
+ SetNextCacheCheckTime(m, rr);
+ }
+
+ return(mDNStrue);
+ }
+ }
+
+mDNSlocal void ReconfirmAntecedents(mDNS *const m, DNSQuestion *q)
+ {
+ mDNSu32 slot;
+ CacheGroup *cg;
+ CacheRecord *rr;
+ domainname *target;
+ FORALL_CACHERECORDS(slot, cg, rr)
+ if ((target = GetRRDomainNameTarget(&rr->resrec)) && rr->resrec.rdatahash == q->qnamehash && SameDomainName(target, &q->qname))
+ mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForNoAnswer);
+ }
+
+// 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 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);
+ CacheGroup *cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname);
+ 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
+ ResourceRecordAnswersQuestion(&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
+ if (m->timenow - m->NextScheduledQuery >= 0)
+ {
+ CacheRecord *rr;
+ m->NextScheduledQuery = m->timenow + 0x78000000;
+
+ // 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, rr)
+ if (rr->CRActiveQuestion && rr->UnansweredQueries < MaxUnansweredQueries)
+ if (m->timenow + TicksTTL(rr)/50 - rr->NextRequiredQuery >= 0)
+ {
+ q = rr->CRActiveQuestion;
+ ExpireDupSuppressInfoOnInterface(q->DupSuppress, m->timenow - TicksTTL(rr)/20, rr->resrec.InterfaceID);
+ if (q->Target.type) q->SendQNow = mDNSInterfaceMark; // If unicast query, mark it
+ else if (q->SendQNow == mDNSNULL) q->SendQNow = rr->resrec.InterfaceID;
+ else if (q->SendQNow != rr->resrec.InterfaceID) q->SendQNow = mDNSInterfaceMark;
+ }
+
+ // Scan our list of questions to see which *unicast* queries need to be sent
+ for (q = m->Questions; q; q=q->next)
+ 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);
+ 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->Target, q->TargetPort, -1, mDNSNULL);
+ q->ThisQInterval *= 2;
+ if (q->ThisQInterval > MaxQuestionInterval)
+ q->ThisQInterval = MaxQuestionInterval;
+ q->LastQTime = m->timenow;
+ q->LastQTxTime = m->timenow;
+ q->RecentAnswerPkts = 0;
+ q->SendQNow = mDNSNULL;
+ m->ExpectUnicastResponse = m->timenow;
+ }
+
+ // Scan our list of questions to see which *multicast* queries we're definitely going to send
+ for (q = m->Questions; q; q=q->next)
+ if (!q->Target.type && TimeToSendThisQuestion(q, m->timenow))
+ {
+ q->SendQNow = mDNSInterfaceMark; // Mark this question for sending on all interfaces
+ if (maxExistingQuestionInterval < q->ThisQInterval)
+ maxExistingQuestionInterval = q->ThisQInterval;
+ }
+
+ // 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
+ for (q = m->Questions; q; q=q->next)
+ {
+ if (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)
+ {
+ q->SendQNow = mDNSInterfaceMark; // Mark this question for sending on all interfaces
+ q->ThisQInterval *= 2;
+ if (q->ThisQInterval > MaxQuestionInterval)
+ q->ThisQInterval = MaxQuestionInterval;
+ else if (q->CurrentAnswers == 0 && q->ThisQInterval == InitialQuestionInterval * 8)
+ {
+ debugf("SendQueries: Zero current answers for %##s (%s); will reconfirm antecedents",
+ q->qname.c, DNSTypeName(q->qtype));
+ ReconfirmAntecedents(m, q); // Sending third query, and no answers yet; time to begin doubting the source
+ }
+ }
+
+ // 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
+ SetNextQueryTime(m,q);
+ }
+ }
+
+ // 2. Scan our authoritative RR list to see what probes we might need to send
+ if (m->timenow - m->NextScheduledProbe >= 0)
+ {
+ m->NextScheduledProbe = m->timenow + 0x78000000;
+
+ if (m->CurrentRecord) LogMsg("SendQueries: ERROR m->CurrentRecord already set");
+ m->CurrentRecord = m->ResourceRecords;
+ while (m->CurrentRecord)
+ {
+ AuthRecord *rr = m->CurrentRecord;
+ m->CurrentRecord = rr->next;
+ if (rr->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 - (rr->LastAPTime + rr->ThisAPInterval) < 0)
+ {
+ SetNextAnnounceProbeTime(m, rr);
+ }
+ // 2. else, if it has reached its probe time, mark it for sending and then update m->NextScheduledProbe correctly
+ else if (rr->ProbeCount)
+ {
+ // Mark for sending. (If no active interfaces, then don't even try.)
+ rr->SendRNow = !intf ? mDNSNULL : (rr->resrec.InterfaceID) ? rr->resrec.InterfaceID : intf->InterfaceID;
+ rr->LastAPTime = m->timenow;
+ rr->ProbeCount--;
+ SetNextAnnounceProbeTime(m, rr);
+ }
+ // else, if it has now finished probing, move it to state Verified,
+ // and update m->NextScheduledResponse so it will be announced
+ else
+ {
+ AuthRecord *r2;
+ rr->resrec.RecordType = kDNSRecordTypeVerified;
+ rr->ThisAPInterval = DefaultAnnounceIntervalForTypeUnique;
+ rr->LastAPTime = m->timenow - DefaultAnnounceIntervalForTypeUnique;
+ SetNextAnnounceProbeTime(m, rr);
+ // If we have any records on our duplicate list that match this one, they have now also completed probing
+ for (r2 = m->DuplicateRecords; r2; r2=r2->next)
+ if (r2->resrec.RecordType == kDNSRecordTypeUnique && RecordIsLocalDuplicate(r2, rr))
+ r2->ProbeCount = 0;
+ AcknowledgeRecord(m, rr);
+ }
+ }
+ }
+ m->CurrentRecord = m->DuplicateRecords;
+ while (m->CurrentRecord)
+ {
+ AuthRecord *rr = m->CurrentRecord;
+ m->CurrentRecord = rr->next;
+ if (rr->resrec.RecordType == kDNSRecordTypeUnique && rr->ProbeCount == 0)
+ AcknowledgeRecord(m, rr);
+ }
+ }
+
+ // 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)
+ {
+ AuthRecord *rr;
+ 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 = 0;
+
+ // Put query questions in this packet
+ for (q = m->Questions; q; q=q->next)
+ if (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);
+ }
+
+ // Put probe questions in this packet
+ for (rr = m->ResourceRecords; rr; rr=rr->next)
+ if (rr->SendRNow == intf->InterfaceID)
+ {
+ mDNSBool ucast = (rr->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);
+ mDNSu8 *newptr = putQuestion(&m->omsg, queryptr, limit, rr->resrec.name, kDNSQType_ANY, (mDNSu16)(rr->resrec.rrclass | ucbit));
+ // We forecast: compressed name (2) type (2) class (2) TTL (4) rdlength (2) rdata (n)
+ mDNSu32 forecast = answerforecast + 12 + rr->resrec.rdestimate;
+ if (newptr && newptr + forecast < limit)
+ {
+ queryptr = newptr;
+ answerforecast = forecast;
+ rr->SendRNow = (rr->resrec.InterfaceID) ? mDNSNULL : GetNextActiveInterfaceID(intf);
+ rr->IncludeInProbe = mDNStrue;
+ verbosedebugf("SendQueries: Put Question %##s (%s) probecount %d",
+ rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), rr->ProbeCount);
+ }
+ else
+ {
+ verbosedebugf("SendQueries: Retracting Question %##s (%s)",
+ rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
+ m->omsg.h.numQuestions--;
+ }
+ }
+ }
+
+ // Put our known answer list (either new one from this question or questions, or remainder of old one from last time)
+ while (KnownAnswerList)
+ {
+ CacheRecord *rr = KnownAnswerList;
+ mDNSu32 SecsSinceRcvd = ((mDNSu32)(m->timenow - rr->TimeRcvd)) / mDNSPlatformOneSecond;
+ mDNSu8 *newptr = PutResourceRecordTTL(&m->omsg, queryptr, &m->omsg.h.numAnswers, &rr->resrec, rr->resrec.rroriginalttl - SecsSinceRcvd);
+ if (newptr)
+ {
+ verbosedebugf("SendQueries: Put %##s (%s) at %d - %d",
+ rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), queryptr - m->omsg.data, newptr - m->omsg.data);
+ queryptr = newptr;
+ KnownAnswerList = rr->NextInKAList;
+ rr->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 (rr = m->ResourceRecords; rr; rr=rr->next)
+ if (rr->IncludeInProbe)
+ {
+ mDNSu8 *newptr = PutResourceRecord(&m->omsg, queryptr, &m->omsg.h.numAuthorities, &rr->resrec);
+ rr->IncludeInProbe = mDNSfalse;
+ if (newptr) queryptr = newptr;
+ else LogMsg("SendQueries: How did we fail to have space for the Update record %##s (%s)?",
+ rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
+ }
+
+ if (queryptr > m->omsg.data)
+ {
+ 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, &AllDNSLinkGroup_v4, MulticastDNSPort, -1, mDNSNULL);
+ if (intf->IPv6Available) mDNSSendDNSMessage(m, &m->omsg, queryptr, intf->InterfaceID, &AllDNSLinkGroup_v6, MulticastDNSPort, -1, 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->resrec.InterfaceID != mDNSInterface_LocalOnly)
+ LogMsg("SendQueries: No active interface to send: %s", 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 && m->timenow - cr->NextRequiredQuery >= 0)
+ {
+ cr->UnansweredQueries++;
+ cr->CRActiveQuestion->SendQNow = mDNSNULL;
+ SetNextCacheCheckTime(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)
+ {
+ LogMsg("SendQueries: No active interface to send: %##s %s", q->qname.c, DNSTypeName(q->qtype));
+ q->SendQNow = mDNSNULL;
+ }
+ }
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark -
+#pragma mark - RR List Management & Task Management
+#endif
+
+// NOTE: AnswerQuestionWithResourceRecord 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 AnswerQuestionWithResourceRecord(mDNS *const m, DNSQuestion *q, CacheRecord *rr, mDNSBool AddRecord)
+ {
+ verbosedebugf("AnswerQuestionWithResourceRecord:%4lu %s TTL%6lu %##s (%s)",
+ q->CurrentAnswers, AddRecord ? "Add" : "Rmv", rr->resrec.rroriginalttl, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
+
+ // Note: Use caution here. In the case of records with rr->DelayDelivery set, AnswerQuestionWithResourceRecord(... 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 (ActiveQuestion(q) && rr->CRActiveQuestion != q)
+ {
+ if (!rr->CRActiveQuestion) m->rrcache_active++; // If not previously active, increment rrcache_active count
+ rr->CRActiveQuestion = q; // We know q is non-null
+ SetNextCacheCheckTime(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.)
+ if ((AddRecord == 2 && !q->RequestUnicast) ||
+ (AddRecord == 1 && (q->ExpectUnique || (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask))))
+ if (ActiveQuestion(q))
+ {
+ q->LastQTime = m->timenow;
+ q->LastQTxTime = m->timenow;
+ q->RecentAnswerPkts = 0;
+ q->ThisQInterval = MaxQuestionInterval;
+ q->RequestUnicast = mDNSfalse;
+ }
+
+ if (rr->DelayDelivery) return; // We'll come back later when CacheRecordDeferredAdd() calls us
+
+ m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback
+ if (q->QuestionCallback)
+ q->QuestionCallback(m, q, &rr->resrec, AddRecord);
+ m->mDNS_reentrancy--; // Decrement to block mDNS API calls again
+ // CAUTION: MUST NOT do anything more with q after calling q->QuestionCallback(), because the client's callback function
+ // is allowed to do anything, including starting/stopping queries, registering/deregistering records, etc.
+ // Right now the only routines that call AnswerQuestionWithResourceRecord() are CacheRecordAdd(), CacheRecordRmv()
+ // and AnswerNewQuestion(), and all of them use the "m->CurrentQuestion" mechanism to protect against questions
+ // being deleted out from under them.
+ }
+
+mDNSlocal void CacheRecordDeferredAdd(mDNS *const m, CacheRecord *rr)
+ {
+ rr->DelayDelivery = 0; // Note, only need to call SetNextCacheCheckTime() when DelayDelivery is set, not when it's cleared
+ if (m->CurrentQuestion) LogMsg("CacheRecordDeferredAdd ERROR m->CurrentQuestion already set");
+ m->CurrentQuestion = m->Questions;
+ while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions)
+ {
+ DNSQuestion *q = m->CurrentQuestion;
+ m->CurrentQuestion = q->next;
+ if (ResourceRecordAnswersQuestion(&rr->resrec, q))
+ AnswerQuestionWithResourceRecord(m, q, rr, mDNStrue);
+ }
+ 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);
+ CacheRecord *rr;
+ for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
+ if (rr->resrec.namehash == namehash && SameDomainName(rr->resrec.name, name))
+ 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 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 into our cache
+// (kDNSRecordTypePacketAns/PacketAnsUnique/PacketAdd/PacketAddUnique).
+// NOTE: CacheRecordAdd calls AnswerQuestionWithResourceRecord 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)
+ {
+ if (m->CurrentQuestion) LogMsg("CacheRecordAdd ERROR m->CurrentQuestion already set");
+ m->CurrentQuestion = m->Questions;
+ while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions)
+ {
+ DNSQuestion *q = m->CurrentQuestion;
+ m->CurrentQuestion = 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 (ActiveQuestion(q) && ++q->RecentAnswerPkts >= 10 &&
+ q->ThisQInterval > InitialQuestionInterval*32 && m->timenow - q->LastQTxTime < mDNSPlatformOneSecond)
+ {
+ LogMsg("CacheRecordAdd: %##s (%s) got immediate answer burst; restarting exponential backoff sequence",
+ q->qname.c, DNSTypeName(q->qtype));
+ q->LastQTime = m->timenow - InitialQuestionInterval + (mDNSs32)mDNSRandom((mDNSu32)mDNSPlatformOneSecond*4);
+ q->ThisQInterval = InitialQuestionInterval;
+ SetNextQueryTime(m,q);
+ }
+ }
+ verbosedebugf("CacheRecordAdd %p %##s (%s) %lu",
+ rr, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), rr->resrec.rroriginalttl);
+ q->CurrentAnswers++;
+ 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 = 1;
+ rr->UnansweredQueries = MaxUnansweredQueries;
+ }
+ AnswerQuestionWithResourceRecord(m, q, rr, mDNStrue);
+ // MUST NOT dereference q again after calling AnswerQuestionWithResourceRecord()
+ }
+ }
+ m->CurrentQuestion = mDNSNULL;
+ SetNextCacheCheckTime(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 AnswerQuestionWithResourceRecord 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");
+ m->CurrentQuestion = m->Questions;
+ while (m->CurrentQuestion)
+ {
+ DNSQuestion *q = m->CurrentQuestion;
+ m->CurrentQuestion = q->next;
+ if (ResourceRecordAnswersQuestion(&rr->resrec, q))
+ AnswerQuestionWithResourceRecord(m, q, rr, 2); // Value '2' indicates "don't expect 'remove' events for this"
+ // MUST NOT dereference q again after calling AnswerQuestionWithResourceRecord()
+ }
+ m->CurrentQuestion = mDNSNULL;
+ }
+
+// CacheRecordRmv is only called from CheckCacheExpiration, which is called from mDNS_Execute
+// 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 AnswerQuestionWithResourceRecord 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");
+ m->CurrentQuestion = m->Questions;
+ while (m->CurrentQuestion && m->CurrentQuestion != m->NewQuestions)
+ {
+ DNSQuestion *q = m->CurrentQuestion;
+ m->CurrentQuestion = q->next;
+ if (ResourceRecordAnswersQuestion(&rr->resrec, q))
+ {
+ verbosedebugf("CacheRecordRmv %p %s", rr, CRDisplayString(m, rr));
+ if (q->CurrentAnswers == 0)
+ LogMsg("CacheRecordRmv ERROR: How can CurrentAnswers already be zero for %p %##s (%s)?",
+ q, q->qname.c, DNSTypeName(q->qtype));
+ else
+ {
+ q->CurrentAnswers--;
+ if (rr->resrec.rdlength > SmallRecordLimit) q->LargeAnswers--;
+ if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) q->UniqueAnswers--;
+ }
+ if (q->CurrentAnswers == 0)
+ {
+ debugf("CacheRecordRmv: Zero current answers for %##s (%s); will reconfirm antecedents",
+ q->qname.c, DNSTypeName(q->qtype));
+ ReconfirmAntecedents(m, q);
+ }
+ q->FlappingInterface = mDNSNULL;
+ AnswerQuestionWithResourceRecord(m, q, rr, mDNSfalse);
+ // MUST NOT dereference q again after calling AnswerQuestionWithResourceRecord()
+ }
+ }
+ m->CurrentQuestion = mDNSNULL;
+ }
+
+mDNSlocal void ReleaseCacheEntity(mDNS *const m, CacheEntity *e)
+ {
+#if 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)
+ {
+ if (r->resrec.rdata && r->resrec.rdata != (RData*)&r->rdatastorage) 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, CacheGroup *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 %s", CRDisplayString(m, rr));
+ if (rr->CRActiveQuestion) // If this record has one or more active questions, tell them it's going away
+ {
+ CacheRecordRmv(m, rr);
+ m->rrcache_active--;
+ }
+ ReleaseCacheRecord(m, rr);
+ }
+ else // else, not expired; see if we need to query
+ {
+ 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 = rr->NextRequiredQuery; // 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 SetNextCacheCheckTime(),
+ // 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->NextCacheCheck - (event + CacheCheckGracePeriod(rr)) > 0)
+ m->NextCacheCheck = (event + CacheCheckGracePeriod(rr));
+ 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;
+ CacheRecord *rr;
+ DNSQuestion *q = m->NewQuestions; // Grab the question we're going to answer
+ const mDNSu32 slot = HashSlot(&q->qname);
+ CacheGroup *cg = CacheGroupForName(m, slot, q->qnamehash, &q->qname);
+
+ verbosedebugf("AnswerNewQuestion: Answering %##s (%s)", q->qname.c, DNSTypeName(q->qtype));
+
+ if (cg) CheckCacheExpiration(m, cg);
+ m->NewQuestions = q->next; // Advance NewQuestions to the next *after* calling CheckCacheExpiration();
+
+ 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");
+ m->CurrentQuestion = q; // Indicate which question we're answering, so we'll know if it gets deleted
+
+ if (q->InterfaceID == mDNSInterface_Any) // If 'mDNSInterface_Any' question, see if we want to tell it about LocalOnly records
+ {
+ if (m->CurrentRecord) LogMsg("AnswerNewLocalOnlyQuestion ERROR m->CurrentRecord already set");
+ m->CurrentRecord = m->ResourceRecords;
+ while (m->CurrentRecord && m->CurrentRecord != m->NewLocalRecords)
+ {
+ AuthRecord *rr = m->CurrentRecord;
+ m->CurrentRecord = rr->next;
+ if (rr->resrec.InterfaceID == mDNSInterface_LocalOnly)
+ if (ResourceRecordAnswersQuestion(&rr->resrec, q))
+ {
+ AnswerLocalOnlyQuestionWithResourceRecord(m, q, rr, mDNStrue);
+ // MUST NOT dereference q again after calling AnswerLocalOnlyQuestionWithResourceRecord()
+ if (m->CurrentQuestion != q) break; // If callback deleted q, then we're finished here
+ }
+ }
+ m->CurrentRecord = mDNSNULL;
+ }
+
+ for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
+ if (ResourceRecordAnswersQuestion(&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 (%s)",
+ rr->resrec.rroriginalttl, SecsSinceRcvd, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
+ 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++;
+ AnswerQuestionWithResourceRecord(m, q, rr, mDNStrue);
+ // MUST NOT dereference q again after calling AnswerQuestionWithResourceRecord()
+ if (m->CurrentQuestion != q) break; // If callback deleted q, then we're finished here
+ }
+ else if (RRTypeIsAddressType(rr->resrec.rrtype) && RRTypeIsAddressType(q->qtype))
+ if (rr->resrec.namehash == q->qnamehash && SameDomainName(rr->resrec.name, &q->qname))
+ ShouldQueryImmediately = mDNSfalse;
+
+ if (ShouldQueryImmediately && m->CurrentQuestion == q)
+ {
+ q->ThisQInterval = InitialQuestionInterval;
+ q->LastQTime = m->timenow - q->ThisQInterval;
+ m->NextScheduledQuery = m->timenow;
+ }
+ 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 NewLocalRecord -- these will be handled by AnswerLocalQuestions
+mDNSlocal void AnswerNewLocalOnlyQuestion(mDNS *const m)
+ {
+ DNSQuestion *q = m->NewLocalOnlyQuestions; // Grab the question we're going to answer
+ m->NewLocalOnlyQuestions = q->next; // Advance NewQuestions 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");
+ 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");
+ m->CurrentRecord = m->ResourceRecords;
+ while (m->CurrentRecord && m->CurrentRecord != m->NewLocalRecords)
+ {
+ AuthRecord *rr = m->CurrentRecord;
+ m->CurrentRecord = rr->next;
+ if (ResourceRecordAnswersQuestion(&rr->resrec, q))
+ {
+ AnswerLocalOnlyQuestionWithResourceRecord(m, q, rr, mDNStrue);
+ // MUST NOT dereference q again after calling AnswerLocalOnlyQuestionWithResourceRecord()
+ 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 we're actively using less than 1/32 of our cache, then we
+ // purge all the unused records and recycle them, instead of allocating more memory.
+ if (m->rrcache_size >= 512 && m->rrcache_size / 32 > m->rrcache_active)
+ debugf("Possible denial-of-service attack in progress: m->rrcache_size %lu; m->rrcache_active %lu",
+ m->rrcache_size, m->rrcache_active);
+ else
+ {
+ m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback
+ m->MainCallback(m, mStatus_GrowCache);
+ m->mDNS_reentrancy--; // Decrement 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)
+ {
+ #if MDNS_DEBUGMSGS
+ mDNSu32 oldtotalused = m->rrcache_totalused;
+ #endif
+ 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);
+ }
+ }
+ #if MDNS_DEBUGMSGS
+ debugf("Clear unused records; m->rrcache_totalused was %lu; now %lu", oldtotalused, m->rrcache_totalused);
+ #endif
+ }
+
+ 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)
+ {
+ debugf("RR Cache now using %ld objects", m->rrcache_totalused);
+ if (m->rrcache_report < 100) m->rrcache_report += 10;
+ else m->rrcache_report += 100;
+ }
+ 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->rdatastorage; // 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);
+ }
+
+mDNSlocal void PurgeCacheResourceRecord(mDNS *const m, CacheRecord *rr)
+ {
+ // 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;
+ SetNextCacheCheckTime(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);
+ }
+
+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;
+
+ verbosedebugf("mDNS_Execute");
+ if (m->CurrentQuestion) LogMsg("mDNS_Execute: ERROR! m->CurrentQuestion already set");
+
+ // 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 slot;
+ m->NextCacheCheck = m->timenow + 0x3FFFFFFF;
+ for (slot = 0; slot < CACHE_HASH_SLOTS; slot++)
+ {
+ CacheGroup **cp = &m->rrcache_hash[slot];
+ while (*cp)
+ {
+ CheckCacheExpiration(m, *cp);
+ if ((*cp)->members) cp=&(*cp)->next;
+ else ReleaseCacheGroup(m, cp);
+ }
+ }
+ LogOperation("Cache checked. Next in %ld ticks", m->NextCacheCheck - m->timenow);
+ }
+
+ // 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");
+
+ for (i=0; m->NewLocalOnlyQuestions && i<1000; i++) AnswerNewLocalOnlyQuestion(m);
+ if (i >= 1000) LogMsg("mDNS_Execute: AnswerNewLocalOnlyQuestion exceeded loop limit");
+
+ for (i=0; i<1000 && m->NewLocalRecords && LocalRecordReady(m->NewLocalRecords); i++)
+ {
+ AuthRecord *rr = m->NewLocalRecords;
+ m->NewLocalRecords = m->NewLocalRecords->next;
+ AnswerLocalQuestions(m, rr, mDNStrue);
+ }
+ if (i >= 1000) LogMsg("mDNS_Execute: AnswerForNewLocalRecords exceeded loop limit");
+
+ // 5. See what packets we need to send
+ if (m->mDNSPlatformStatus != mStatus_NoError || m->SleepState) DiscardDeregistrations(m);
+ else if (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;
+
+ // 6. 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)
+ {
+ LogMsg("mDNS_Execute: SendQueries didn't send all its queries; will try again in one second");
+ m->NextScheduledQuery = m->timenow + mDNSPlatformOneSecond;
+ }
+ if (m->timenow - m->NextScheduledProbe >= 0)
+ {
+ LogMsg("mDNS_Execute: SendQueries didn't send all its probes; will try again in one second");
+ m->NextScheduledProbe = m->timenow + mDNSPlatformOneSecond;
+ }
+
+ // 7. 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;
+ }
+
+ // 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).
+
+#ifndef UNICAST_DISABLED
+ uDNS_Execute(m);
+#endif
+ mDNS_Unlock(m); // Calling mDNS_Unlock is what gives m->NextScheduledEvent its new value
+ return(m->NextScheduledEvent);
+ }
+
+// 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.
+// Note that sleep/wake calls do not have to be paired one-for-one; it is acceptable to call
+// mDNSCoreMachineSleep(m, mDNSfalse) any time there is reason to believe that the machine may have just
+// found itself in a new network environment. For example, if the Ethernet hardware indicates that the
+// cable has just been connected, the platform support layer should call mDNSCoreMachineSleep(m, mDNSfalse)
+// to make mDNSCore re-issue its outstanding queries, probe for record uniqueness, etc.
+// While it is safe to call mDNSCoreMachineSleep(m, mDNSfalse) at any time, it does cause extra network
+// traffic, so it should only be called when there is legitimate reason to believe the machine
+// may have become attached to a new network.
+mDNSexport void mDNSCoreMachineSleep(mDNS *const m, mDNSBool sleepstate)
+ {
+ AuthRecord *rr;
+
+ mDNS_Lock(m);
+
+ m->SleepState = sleepstate;
+ LogOperation("%s at %ld", sleepstate ? "Sleeping" : "Waking", m->timenow);
+
+ if (sleepstate)
+ {
+#ifndef UNICAST_DISABLED
+ uDNS_Sleep(m);
+#endif
+ // 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);
+ }
+ else
+ {
+ DNSQuestion *q;
+ mDNSu32 slot;
+ CacheGroup *cg;
+ CacheRecord *cr;
+
+#ifndef UNICAST_DISABLED
+ uDNS_Wake(m);
+#endif
+ // 1. Retrigger all our questions
+ for (q = m->Questions; q; q=q->next) // Scan our list of questions
+ if (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;
+ }
+
+ // 2. Re-validate our cache records
+ m->NextCacheCheck = m->timenow;
+ 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 (rr->resrec.RecordType == kDNSRecordTypeVerified && !rr->DependentOn) rr->resrec.RecordType = kDNSRecordTypeUnique;
+ rr->ProbeCount = DefaultProbeCountForRecordType(rr->resrec.RecordType);
+ rr->AnnounceCount = InitialAnnounceCount;
+ rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType);
+ InitializeLastAPTime(m, rr);
+ }
+ }
+
+ mDNS_Unlock(m);
+ }
+
+// ***************************************************************************
+#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 = 10;
+ 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 = PutResourceRecordCappedTTL(response, responseptr, &response->h.numAnswers, &rr->resrec, maxttl);
+ 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 = PutResourceRecordCappedTTL(response, responseptr, &response->h.numAdditionals, &rr->resrec, maxttl);
+ 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(AuthRecord *our, CacheRecord *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)
+ {
+ const AuthRecord *ourset = our->RRSet ? our->RRSet : our;
+
+ // 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);
+
+ // If the pktrr matches a member of ourset, not a conflict
+ if (FindRRSet(m, pktrr) == ourset) 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 (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 > 0)
+ debugf("ResolveSimultaneousProbe: %##s (%s): We won", our->resrec.name->c, DNSTypeName(our->resrec.rrtype));
+ else if (result < 0)
+ {
+ debugf("ResolveSimultaneousProbe: %##s (%s): We lost", our->resrec.name->c, DNSTypeName(our->resrec.rrtype));
+ mDNS_Deregister_internal(m, our, mDNS_Dereg_conflict);
+ goto exit;
+ }
+ }
+ }
+ m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
+ }
+ if (!FoundUpdate)
+ debugf("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, ResourceRecord *pktrr)
+ {
+ mDNSu32 slot = HashSlot(pktrr->name);
+ CacheGroup *cg = CacheGroupForRecord(m, slot, pktrr);
+ CacheRecord *rr;
+ for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
+ if (pktrr->InterfaceID == rr->resrec.InterfaceID && IdenticalResourceRecord(pktrr, &rr->resrec)) break;
+ return(rr);
+ }
+
+// 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 = 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 = query->data;
+ mDNSu8 *responseptr = mDNSNULL;
+ AuthRecord *rr;
+ int i;
+
+ // ***
+ // *** 1. Parse Question Section and mark potential answers
+ // ***
+ for (i=0; i<query->h.numQuestions; i++) // For each question...
+ {
+ mDNSBool QuestionNeedsMulticastResponse;
+ int NumAnswersForThisQuestion = 0;
+ 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");
+ m->CurrentRecord = m->ResourceRecords;
+ while (m->CurrentRecord)
+ {
+ rr = m->CurrentRecord;
+ m->CurrentRecord = rr->next;
+ if (ResourceRecordAnswersQuestion(&rr->resrec, &pktq) && (QueryWasMulticast || QueryWasLocalUnicast || rr->AllowRemoteQuery))
+ {
+ if (rr->resrec.RecordType == kDNSRecordTypeUnique)
+ ResolveSimultaneousProbe(m, query, end, &pktq, rr);
+ else if (ResourceRecordIsValidAnswer(rr))
+ {
+ NumAnswersForThisQuestion++;
+ // 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 overalyed 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;
+ }
+ }
+ }
+
+ // 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
+
+ // We only do the following accelerated cache expiration processing and duplicate question suppression processing
+ // for 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
+ if (QuestionNeedsMulticastResponse)
+ {
+ const mDNSu32 slot = HashSlot(&pktq.qname);
+ CacheGroup *cg = CacheGroupForName(m, slot, pktq.qnamehash, &pktq.qname);
+ CacheRecord *rr;
+
+ // 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
+ for (rr = cg ? cg->members : mDNSNULL; rr; rr=rr->next)
+ if (ResourceRecordAnswersQuestion(&rr->resrec, &pktq) && rr->resrec.rdlength <= SmallRecordLimit)
+ if (!rr->NextInKAList && eap != &rr->NextInKAList)
+ {
+ *eap = rr;
+ eap = &rr->NextInKAList;
+ if (rr->MPUnansweredQ == 0 || m->timenow - rr->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.
+ rr->MPUnansweredQ++;
+ rr->MPLastUnansweredQT = m->timenow;
+ rr->MPExpectingKA = mDNStrue;
+ }
+ }
+
+ // 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 (!(query->h.flags.b[0] & kDNSFlag0_TC))
+ 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; }
+ }
+ }
+
+ // ***
+ // *** 2. 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
+
+ // ***
+ // *** 3. Add additional records
+ // ***
+ AddAdditionalsToResponseList(m, ResponseRecords, &nrp, InterfaceID);
+
+ // ***
+ // *** 4. 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...
+ AuthRecord *rr;
+ CacheRecord *ourcacherr;
+ ptr = GetLargeResourceRecord(m, query, ptr, end, InterfaceID, kDNSRecordTypePacketAns, &m->rec);
+ if (!ptr) goto exit;
+
+ // 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
+ }
+ }
+ }
+
+ // 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).
+ ourcacherr = FindIdenticalRecordInCache(m, &m->rec.r.resrec);
+ if (ourcacherr && ourcacherr->MPExpectingKA && m->timenow - ourcacherr->MPLastUnansweredQT < mDNSPlatformOneSecond)
+ {
+ ourcacherr->MPUnansweredKA++;
+ ourcacherr->MPExpectingKA = mDNSfalse;
+ }
+
+ // Having built our ExpectedAnswers list from the questions in this packet, we can definitively
+ // remove from our ExpectedAnswers list any records that are suppressed in the very same packet.
+ // For answers that are suppressed in subsequent KA list packets, we rely on the MPQ/MPKA counting to track them.
+ eap = &ExpectedAnswers;
+ while (*eap)
+ {
+ CacheRecord *rr = *eap;
+ if (rr->resrec.InterfaceID == InterfaceID && IdenticalResourceRecord(&m->rec.r.resrec, &rr->resrec))
+ { *eap = rr->NextInKAList; rr->NextInKAList = mDNSNULL; }
+ else eap = &rr->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
+ }
+
+ // ***
+ // *** 5. 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; }
+
+ // ***
+ // *** 6. 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
+ }
+ }
+
+ // ***
+ // *** 7. 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
+ }
+
+ // ***
+ // *** 8. 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
+
+ // ***
+ // *** 9. 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 *rr;
+ rr = ExpectedAnswers;
+ ExpectedAnswers = rr->NextInKAList;
+ rr->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 (rr->UnansweredQueries == 0 || m->timenow - rr->LastUnansweredTime >= mDNSPlatformOneSecond)
+ {
+ rr->UnansweredQueries++;
+ rr->LastUnansweredTime = m->timenow;
+ if (rr->UnansweredQueries > 1)
+ debugf("ProcessQuery: (!TC) UAQ %lu MPQ %lu MPKA %lu %s",
+ rr->UnansweredQueries, rr->MPUnansweredQ, rr->MPUnansweredKA, CRDisplayString(m, rr));
+ SetNextCacheCheckTime(m, rr);
+ }
+
+ // 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 (rr->UnansweredQueries >= MaxUnansweredQueries)
+ {
+ // Only show debugging message if this record was not about to expire anyway
+ if (RRExpireTime(rr) - m->timenow > 4 * mDNSPlatformOneSecond)
+ debugf("ProcessQuery: (Max) UAQ %lu MPQ %lu MPKA %lu mDNS_Reconfirm() for %s",
+ rr->UnansweredQueries, rr->MPUnansweredQ, rr->MPUnansweredKA, CRDisplayString(m, rr));
+ mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForNoAnswer);
+ }
+ // 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 (rr->MPUnansweredQ * 4 > rr->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(rr) - 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(rr) - m->timenow > 4 * mDNSPlatformOneSecond)
+ debugf("ProcessQuery: (MPQ) UAQ %lu MPQ %lu MPKA %lu mDNS_Reconfirm() for %s",
+ rr->UnansweredQueries, rr->MPUnansweredQ, rr->MPUnansweredKA, CRDisplayString(m, rr));
+
+ if (remain <= 60 * (mDNSu32)mDNSPlatformOneSecond)
+ rr->UnansweredQueries++; // Treat this as equivalent to one definite unanswered query
+ rr->MPUnansweredQ = 0; // Clear MPQ/MPKA statistics
+ rr->MPUnansweredKA = 0;
+ rr->MPExpectingKA = mDNSfalse;
+
+ if (remain < kDefaultReconfirmTimeForNoAnswer)
+ remain = kDefaultReconfirmTimeForNoAnswer;
+ mDNS_Reconfirm_internal(m, rr, remain);
+ }
+ }
+
+ while (DupQuestions)
+ {
+ int i;
+ 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 = !mDNSAddrIsDNSMulticast(dstaddr) && AddressIsLocalSubnet(m, InterfaceID, srcaddr);
+
+ if (!InterfaceID && 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 (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");
+ 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",
+ 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");
+
+ responseend = ProcessQuery(m, msg, end, srcaddr, InterfaceID,
+ (srcport.NotAnInteger != MulticastDNSPort.NotAnInteger), 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, srcaddr, srcport, -1, mDNSNULL);
+ }
+ }
+
+// 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.
+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;
+
+ // We ignore questions (if any) in a DNS response packet
+ const mDNSu8 *ptr = LocateAnswers(response, end);
+
+ // "(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 totalrecords = response->h.numAnswers + response->h.numAuthorities + response->h.numAdditionals;
+
+ (void)srcaddr; // Currently used only for display in debugging message
+ (void)srcport;
+ (void)dstport;
+
+ verbosedebugf("Received Response from %#-15a addressed to %#-15a on %p with "
+ "%2d Question%s %2d Answer%s %2d Authorit%s %2d Additional%s",
+ 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");
+
+ // If we get a unicast response when we weren't expecting one, then we assume it is someone trying to spoof us
+ if (!mDNSAddrIsDNSMulticast(dstaddr))
+ {
+ if (!AddressIsLocalSubnet(m, InterfaceID, srcaddr) || (mDNSu32)(m->timenow - m->ExpectUnicastResponse) > (mDNSu32)(mDNSPlatformOneSecond*2))
+ return;
+ // For now we don't put standard wide-area unicast responses in our main cache
+ // (Later we should fix this and cache all known results in a unified manner.)
+ if (response->h.id.NotAnInteger != 0 || srcport.NotAnInteger != MulticastDNSPort.NotAnInteger)
+ return;
+ }
+
+ for (i = 0; i < totalrecords && ptr && ptr < end; i++)
+ {
+ const mDNSu8 RecordType = (mDNSu8)((i < response->h.numAnswers) ? kDNSRecordTypePacketAns : 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
+
+ // 1. Check that this packet resource record does not conflict with any of ours
+ if (m->CurrentRecord) LogMsg("mDNSCoreReceiveResponse ERROR m->CurrentRecord already set");
+ m->CurrentRecord = m->ResourceRecords;
+ while (m->CurrentRecord)
+ {
+ AuthRecord *rr = m->CurrentRecord;
+ m->CurrentRecord = rr->next;
+ 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 (m->rec.r.resrec.rrtype == rr->resrec.rrtype && SameRData(&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))
+ {
+ debugf("mDNSCoreReceiveResponse: Our Record: %08lX %s", rr-> resrec.rdatahash, ARDisplayString(m, rr));
+ debugf("mDNSCoreReceiveResponse: Pkt Record: %08lX %s", m->rec.r.resrec.rdatahash, CRDisplayString(m, &m->rec.r));
+
+ // 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
+ while (rr->DependentOn) rr = rr->DependentOn;
+
+ // If we've just whacked this record's ProbeCount, don't need to do it again
+ if (rr->ProbeCount <= DefaultProbeCountForTypeUnique)
+ {
+ // If we'd previously verified this record, put it back to probing state and try again
+ if (rr->resrec.RecordType == kDNSRecordTypeVerified)
+ {
+ debugf("mDNSCoreReceiveResponse: Reseting to Probing: %##s (%s)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
+ rr->resrec.RecordType = kDNSRecordTypeUnique;
+ rr->ProbeCount = DefaultProbeCountForTypeUnique + 1;
+ rr->ThisAPInterval = DefaultAPIntervalForRecordType(kDNSRecordTypeUnique);
+ 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)
+ {
+ debugf("mDNSCoreReceiveResponse: Will rename %##s (%s)", rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
+ 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.)
+ // This is simply a misconfiguration, and we don't try to recover from it.
+ else if (rr->resrec.RecordType == kDNSRecordTypeKnownUnique)
+ {
+ debugf("mDNSCoreReceiveResponse: Unexpected conflict on %##s (%s) -- discarding our record",
+ rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
+ mDNS_Deregister_internal(m, rr, mDNS_Dereg_conflict);
+ }
+ else
+ debugf("mDNSCoreReceiveResponse: Unexpected record type %X %##s (%s)",
+ rr->resrec.RecordType, rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype));
+ }
+ }
+ // 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; }
+ }
+ }
+
+ // 2. See if we want to add this packet resource record to our cache
+ if (m->rrcache_size) // Only try to cache answers if we have a cache to put them in
+ {
+ 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)
+ {
+ // If we found this exact resource record, refresh its TTL
+ if (rr->resrec.InterfaceID == InterfaceID && IdenticalResourceRecord(&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));
+ rr->TimeRcvd = m->timenow;
+
+ 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)
+ { *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 (!mDNSPlatformMemSame(m->rec.r.resrec.rdata->u.data, rr->resrec.rdata->u.data, m->rec.r.resrec.rdlength))
+ {
+ // 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.
+ rr->resrec.rroriginalttl = 0;
+ rr->UnansweredQueries = MaxUnansweredQueries;
+ SetNextCacheCheckTime(m, rr);
+ // DO NOT break out here -- we want to continue as if we never found it
+ }
+ else if (m->rec.r.resrec.rroriginalttl > 0)
+ {
+ rr->resrec.rroriginalttl = m->rec.r.resrec.rroriginalttl;
+ rr->UnansweredQueries = 0;
+ rr->MPUnansweredQ = 0;
+ rr->MPUnansweredKA = 0;
+ rr->MPExpectingKA = mDNSfalse;
+ SetNextCacheCheckTime(m, rr);
+ 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.
+ rr->resrec.rroriginalttl = 1;
+ rr->UnansweredQueries = MaxUnansweredQueries;
+ SetNextCacheCheckTime(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)
+ {
+ // If we don't have a CacheGroup for this name, make one now
+ if (!cg) cg = GetCacheGroup(m, slot, &m->rec.r.resrec);
+ if (cg) rr = GetCacheRecord(m, cg, m->rec.r.resrec.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
+ if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask)
+ { *cfp = rr; cfp = &rr->NextInCFList; *cfp = (CacheRecord*)1; }
+ // If this is an oversized record with external storage allocated, copy rdata to external storage
+ if (rr->resrec.rdata != (RData*)&rr->rdatastorage && !(m->rec.r.resrec.rdlength > InlineCacheRDSize))
+ LogMsg("rr->resrec.rdata != &rr->rdatastorage but length <= InlineCacheRDSize %##s", m->rec.r.resrec.name->c);
+ if (m->rec.r.resrec.rdlength > InlineCacheRDSize)
+ mDNSPlatformMemCopy(m->rec.r.resrec.rdata, rr->resrec.rdata, sizeofRDataHeader + m->rec.r.resrec.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
+ if (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) // If marked unique, assume we may have
+ rr->DelayDelivery = m->timenow + mDNSPlatformOneSecond; // to delay delivery of this 'add' event
+ else
+ rr->DelayDelivery = CheckForSoonToExpireRecords(m, rr->resrec.name, rr->resrec.namehash, slot);
+ CacheRecordAdd(m, rr); // CacheRecordAdd calls SetNextCacheCheckTime(m, rr); for us
+ }
+ }
+ }
+ 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);
+ CacheGroup *cg = CacheGroupForRecord(m, slot, &r1->resrec);
+ CacheFlushRecords = CacheFlushRecords->NextInCFList;
+ r1->NextInCFList = mDNSNULL;
+ for (r2 = cg ? cg->members : mDNSNULL; r2; r2=r2->next)
+ if (SameResourceRecordSignature(&r1->resrec, &r2->resrec))
+ {
+ // If record was recently positively received
+ // (i.e. not counting goodbye packets or cache flush events that set the TTL to 1)
+ // then we need to ensure the whole RRSet has the same TTL (as required by DNS semantics)
+ if (r2->resrec.rroriginalttl > 1 && m->timenow - r2->TimeRcvd < mDNSPlatformOneSecond)
+ {
+ if (r2->resrec.rroriginalttl != r1->resrec.rroriginalttl)
+ LogMsg("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 %p X %p %s", r1, r2, 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.
+ r2->resrec.rroriginalttl = 1;
+ r2->TimeRcvd = m->timenow;
+ r2->UnansweredQueries = MaxUnansweredQueries;
+ }
+ SetNextCacheCheckTime(m, r2);
+ }
+ if (r1->DelayDelivery) // If we were planning to delay delivery of this record, see if we still need to
+ {
+ // Note, only need to call SetNextCacheCheckTime() when DelayDelivery is set, not when it's cleared
+ r1->DelayDelivery = CheckForSoonToExpireRecords(m, r1->resrec.name, r1->resrec.namehash, slot);
+ if (!r1->DelayDelivery) CacheRecordDeferredAdd(m, r1);
+ }
+ }
+ }
+
+mDNSexport void mDNSCoreReceive(mDNS *const m, void *const pkt, const mDNSu8 *const end,
+ const mDNSAddr *const srcaddr, const mDNSIPPort srcport, const mDNSAddr *const dstaddr, const mDNSIPPort dstport,
+ const mDNSInterfaceID InterfaceID)
+ {
+ DNSMessage *msg = (DNSMessage *)pkt;
+ const mDNSu8 StdQ = kDNSFlag0_QR_Query | kDNSFlag0_OP_StdQuery;
+ const mDNSu8 StdR = kDNSFlag0_QR_Response | kDNSFlag0_OP_StdQuery;
+ mDNSu8 QR_OP;
+ mDNSu8 *ptr = mDNSNULL;
+ const mDNSu8 UpdateR = kDNSFlag0_QR_Response | kDNSFlag0_OP_Update;
+
+#ifndef UNICAST_DISABLED
+ if (srcport.NotAnInteger == NATPMPPort.NotAnInteger)
+ {
+ mDNS_Lock(m);
+ uDNS_ReceiveNATMap(m, pkt, (mDNSu16)(end - (mDNSu8 *)pkt));
+ mDNS_Unlock(m);
+ return;
+ }
+#endif
+ if ((unsigned)(end - (mDNSu8 *)pkt) < sizeof(DNSMessageHeader)) { LogMsg("DNS Message too short"); 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 (!mDNSAddressIsValid(srcaddr)) { debugf("mDNSCoreReceive ignoring packet from %#a", srcaddr); return; }
+
+ mDNS_Lock(m);
+ m->PktNum++;
+#ifndef UNICAST_DISABLED
+ if (!mDNSAddressIsAllDNSLinkGroup(dstaddr) && (QR_OP == StdR || QR_OP == UpdateR))
+ uDNS_ReceiveMsg(m, msg, end, srcaddr, srcport, dstaddr, dstport, InterfaceID);
+ // Note: mDNSCore also needs to get access to received unicast responses
+#endif
+ if (QR_OP == StdQ) mDNSCoreReceiveQuery (m, msg, end, srcaddr, srcport, dstaddr, dstport, InterfaceID);
+ else if (QR_OP == StdR) mDNSCoreReceiveResponse(m, msg, end, srcaddr, srcport, dstaddr, dstport, InterfaceID);
+ else if (QR_OP != UpdateR)
+ LogMsg("Unknown DNS packet type %02X%02X from %#-15a:%-5d to %#-15a:%-5d on %p (ignored)",
+ msg->h.flags.b[0], msg->h.flags.b[1], srcaddr, mDNSVal16(srcport), dstaddr, mDNSVal16(dstport), InterfaceID);
+
+ // 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 -
+#pragma mark - Searcher Functions
+#endif
+
+#define SameQTarget(A,B) (mDNSSameAddress(&(A)->Target, &(B)->Target) && (A)->TargetPort.NotAnInteger == (B)->TargetPort.NotAnInteger)
+
+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 of questions
+ if (q->InterfaceID == question->InterfaceID && // for another question with the same InterfaceID,
+ SameQTarget(q, question) && // and same unicast/multicast target settings
+ q->qtype == question->qtype && // type,
+ q->qclass == question->qclass && // class,
+ 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, const DNSQuestion *const question)
+ {
+ DNSQuestion *q;
+ 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->ThisQInterval = question->ThisQInterval;
+ q->RequestUnicast = question->RequestUnicast;
+ q->LastQTime = question->LastQTime;
+ q->RecentAnswerPkts = 0;
+ q->DuplicateOf = FindDuplicateQuestion(m, q);
+ q->LastQTxTime = question->LastQTxTime;
+ SetNextQueryTime(m,q);
+ }
+ }
+
+#define ValidQuestionTarget(Q) (((Q)->Target.type == mDNSAddrType_IPv4 || (Q)->Target.type == mDNSAddrType_IPv6) && \
+ ((Q)->TargetPort.NotAnInteger == UnicastDNSPort.NotAnInteger || (Q)->TargetPort.NotAnInteger == MulticastDNSPort.NotAnInteger))
+
+mDNSlocal mStatus mDNS_StartQuery_internal(mDNS *const m, DNSQuestion *const question)
+ {
+ if (question->Target.type && !ValidQuestionTarget(question))
+ {
+ LogMsg("Warning! Target.type = %ld port = %u (Client forgot to initialize before calling mDNS_StartQuery?)",
+ question->Target.type, mDNSVal16(question->TargetPort));
+ question->Target.type = mDNSAddrType_None;
+ }
+
+ if (!question->Target.type) // No question->Target specified, so clear TargetPort and TargetQID
+ {
+ question->TargetPort = zeroIPPort;
+ question->TargetQID = zeroID;
+ }
+
+#ifndef UNICAST_DISABLED
+ // If the client has specified 'kDNSServiceFlagsForceMulticast'
+ // then we do a multicast query on that interface, even for unicast domains.
+ if (question->InterfaceID == mDNSInterface_LocalOnly || question->ForceMCast || IsLocalDomain(&question->qname))
+ question->uDNS_info.id = zeroID;
+ else return uDNS_StartQuery(m, question);
+#else
+ question->uDNS_info.id = zeroID;
+#endif // UNICAST_DISABLED
+
+ //LogOperation("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;
+ // Note: It important that new questions are appended at the *end* of the list, not prepended at the start
+ DNSQuestion **q = &m->Questions;
+ if (question->InterfaceID == mDNSInterface_LocalOnly) q = &m->LocalOnlyQuestions;
+ while (*q && *q != question) q=&(*q)->next;
+
+ if (*q)
+ {
+ LogMsg("Error! Tried to add a question %##s (%s) that's already in the active list",
+ question->qname.c, DNSTypeName(question->qtype));
+ return(mStatus_AlreadyRegistered);
+ }
+
+ // If this question is referencing a specific interface, verify it exists
+ if (question->InterfaceID && question->InterfaceID != mDNSInterface_LocalOnly)
+ {
+ NetworkInterfaceInfo *intf;
+ for (intf = m->HostInterfaces; intf; intf = intf->next)
+ if (intf->InterfaceID == question->InterfaceID) break;
+ if (!intf)
+ LogMsg("Note: InterfaceID %p for question %##s not currently found in active interface list",
+ question->InterfaceID, question->qname.c);
+ }
+
+ if (!ValidateDomainName(&question->qname))
+ {
+ LogMsg("Attempt to start query with invalid qname %##s (%s)", question->qname.c, DNSTypeName(question->qtype));
+ return(mStatus_Invalid);
+ }
+
+ // 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. If AnswerNewQuestion() finds
+ // that we have *no* relevant answers currently in our cache, then it will accelerate that to go out immediately.
+ if (!m->RandomQueryDelay) m->RandomQueryDelay = 1 + (mDNSs32)mDNSRandom((mDNSu32)InitialQuestionInterval);
+
+ 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->ThisQInterval = InitialQuestionInterval * 2; // MUST be > zero for an active question
+ question->RequestUnicast = 2; // Set to 2 because is decremented once *before* we check it
+ question->LastQTime = m->timenow - m->RandomQueryDelay; // Avoid inter-machine synchronization
+ question->LastAnswerPktNum = m->PktNum;
+ question->RecentAnswerPkts = 0;
+ question->CurrentAnswers = 0;
+ question->LargeAnswers = 0;
+ question->UniqueAnswers = 0;
+ question->FlappingInterface = mDNSNULL;
+ question->DuplicateOf = FindDuplicateQuestion(m, question);
+ question->NextInDQList = mDNSNULL;
+ for (i=0; i<DupSuppressInfoSize; i++)
+ question->DupSuppress[i].InterfaceID = mDNSNULL;
+ // question->InterfaceID must be already set by caller
+ question->SendQNow = mDNSNULL;
+ question->SendOnAll = mDNSfalse;
+ question->LastQTxTime = m->timenow;
+
+ if (!question->DuplicateOf)
+ verbosedebugf("mDNS_StartQuery_internal: Question %##s (%s) %p %d (%p) started",
+ question->qname.c, DNSTypeName(question->qtype), question->InterfaceID,
+ question->LastQTime + question->ThisQInterval - m->timenow, question);
+ else
+ verbosedebugf("mDNS_StartQuery_internal: Question %##s (%s) %p %d (%p) duplicate of (%p)",
+ question->qname.c, DNSTypeName(question->qtype), question->InterfaceID,
+ question->LastQTime + question->ThisQInterval - m->timenow, question, question->DuplicateOf);
+
+ *q = question;
+ if (question->InterfaceID == mDNSInterface_LocalOnly)
+ {
+ if (!m->NewLocalOnlyQuestions) m->NewLocalOnlyQuestions = question;
+ }
+ else
+ {
+ if (!m->NewQuestions) m->NewQuestions = question;
+ SetNextQueryTime(m,question);
+ }
+
+ return(mStatus_NoError);
+ }
+ }
+
+mDNSlocal 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 **q = &m->Questions;
+
+ if (uDNS_IsActiveQuery(question, &m->uDNS_info)) return uDNS_StopQuery(m, question);
+
+ if (question->InterfaceID == mDNSInterface_LocalOnly) q = &m->LocalOnlyQuestions;
+ while (*q && *q != question) q=&(*q)->next;
+ if (*q) *q = (*q)->next;
+ else
+ {
+ if (question->ThisQInterval >= 0) // Only log error message if the query was supposed to be active
+ LogMsg("mDNS_StopQuery_internal: Question %##s (%s) not found in active list",
+ question->qname.c, DNSTypeName(question->qtype));
+ 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 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;
+ for (q = m->Questions; q; q=q->next) // Scan our list of questions
+ if (ActiveQuestion(q) && ResourceRecordAnswersQuestion(&rr->resrec, q))
+ break;
+ verbosedebugf("mDNS_StopQuery_internal: Cache RR %##s (%s) setting CRActiveQuestion to %p",
+ rr->resrec.name->c, DNSTypeName(rr->resrec.rrtype), q);
+ 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;
+
+ // Take care not to trash question->next until *after* we've updated m->CurrentQuestion and m->NewQuestions
+ question->next = 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);
+ }
+
+mDNSexport mStatus mDNS_Reconfirm(mDNS *const m, CacheRecord *const rr)
+ {
+ mStatus status;
+ mDNS_Lock(m);
+ status = mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForNoAnswer);
+ 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);
+ if (cr) status = mDNS_Reconfirm_internal(m, cr, kDefaultReconfirmTimeForNoAnswer);
+ mDNS_Unlock(m);
+ return(status);
+ }
+
+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)
+ {
+ question->InterfaceID = InterfaceID;
+ question->Target = zeroAddr;
+ question->qtype = kDNSType_PTR;
+ question->qclass = kDNSClass_IN;
+ question->LongLived = mDNSfalse;
+ question->ExpectUnique = mDNSfalse;
+ question->ForceMCast = ForceMCast;
+ question->QuestionCallback = Callback;
+ question->QuestionContext = Context;
+ if (!ConstructServiceName(&question->qname, mDNSNULL, srv, domain)) return(mStatus_BadParamErr);
+
+#ifndef UNICAST_DISABLED
+ if (question->InterfaceID == mDNSInterface_LocalOnly || question->ForceMCast || IsLocalDomain(&question->qname))
+ {
+ question->LongLived = mDNSfalse;
+ question->uDNS_info.id = zeroID;
+ return(mDNS_StartQuery(m, question));
+ }
+ else
+ {
+ mStatus status;
+ // Need to explicitly lock here, because mDNS_StartQuery does locking but uDNS_StartQuery does not
+ mDNS_Lock(m);
+ question->LongLived = mDNStrue;
+ status = uDNS_StartQuery(m, question);
+ mDNS_Unlock(m);
+ return(status);
+ }
+#else
+ return(mDNS_StartQuery(m, question));
+#endif // UNICAST_DISABLED
+ }
+
+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, mDNSBool AddRecord)
+ {
+ ServiceInfoQuery *query = (ServiceInfoQuery *)question->QuestionContext;
+ mDNSBool PortChanged = (mDNSBool)(query->info->port.NotAnInteger != answer->rdata->u.srv.port.NotAnInteger);
+ 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", query->qAv4.qname.c);
+ 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, mDNSBool 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(answer->rdata->u.txt.c, query->info->TXTinfo, 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, mDNSBool AddRecord)
+ {
+ ServiceInfoQuery *query = (ServiceInfoQuery *)question->QuestionContext;
+ //LogOperation("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.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.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.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.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 || uDNS_IsActiveQuery(&q->qSRV, &m->uDNS_info)) mDNS_StopQuery_internal(m, &q->qSRV);
+ if (q->qTXT.ThisQInterval >= 0 || uDNS_IsActiveQuery(&q->qTXT, &m->uDNS_info)) mDNS_StopQuery_internal(m, &q->qTXT);
+ if (q->qAv4.ThisQInterval >= 0 || uDNS_IsActiveQuery(&q->qAv4, &m->uDNS_info)) mDNS_StopQuery_internal(m, &q->qAv4);
+ if (q->qAv6.ThisQInterval >= 0 || uDNS_IsActiveQuery(&q->qAv6, &m->uDNS_info)) 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->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)
+ {
+#ifndef UNICAST_DISABLED
+ mDNSBool unicast = !(rr->resrec.InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(rr->resrec.name));
+#else
+ mDNSBool unicast = mDNSfalse;
+#endif
+
+ 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 (!unicast && rr->NewRData)
+ {
+ RData *n = rr->NewRData;
+ rr->NewRData = mDNSNULL; // Clear the NewRData pointer ...
+ if (rr->UpdateCallback)
+ rr->UpdateCallback(m, rr, n); // ...and let the client free this memory, if necessary
+ }
+
+ rr->NewRData = newrdata;
+ rr->newrdlength = newrdlength;
+ rr->UpdateCallback = Callback;
+
+ if (unicast) { mStatus status = uDNS_UpdateRecord(m, rr); mDNS_Unlock(m); return(status); }
+
+ if (rr->resrec.rroriginalttl == newttl &&
+ rr->resrec.rdlength == newrdlength && mDNSPlatformMemSame(rr->resrec.rdata->u.data, newrdata->u.data, newrdlength))
+ CompleteRDataUpdate(m, rr);
+ else
+ {
+ domainlabel name;
+ domainname type, domain;
+ DeconstructServiceName(rr->resrec.name, &name, &type, &domain);
+ rr->AnnounceCount = InitialAnnounceCount;
+ // iChat often does suprious record updates where no data has changed. For the _presence service type, using
+ // name/value pairs, the mDNSPlatformMemSame() check above catches this and correctly suppresses the wasteful
+ // update. For the _ichat service type, the XML encoding introduces spurious noise differences into the data
+ // even though there's no actual semantic change, so the mDNSPlatformMemSame() check doesn't help us.
+ // To work around this, we simply unilaterally limit all legacy _ichat-type updates to a single announcement.
+ if (SameDomainLabel(type.c, (mDNSu8*)"\x6_ichat")) rr->AnnounceCount = 1;
+ rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType);
+ 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);
+ }
+
+mDNSexport 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[256];
+ 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, mDNS_HostNameCallback, set);
+ mDNS_SetupResourceRecord(&set->RR_PTR, mDNSNULL, set->InterfaceID, kDNSType_PTR, kHostNameTTL, kDNSRecordTypeKnownUnique, mDNSNULL, mDNSNULL);
+ mDNS_SetupResourceRecord(&set->RR_HINFO, mDNSNULL, set->InterfaceID, kDNSType_HINFO, kHostNameTTL, kDNSRecordTypeUnique, 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.resrec.name, &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
+ 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.resrec.name, buffer);
+ set->RR_PTR.HostTarget = mDNStrue; // 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 (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.resrec.name, &m->MulticastHostname);
+ set->RR_HINFO.DependentOn = &set->RR_A;
+ mDNSPlatformMemCopy(&m->HIHardware, p, 1 + (mDNSu32)m->HIHardware.c[0]);
+ p += 1 + (int)p[0];
+ mDNSPlatformMemCopy(&m->HISoftware, p, 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_Close 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; }
+ if (SameDomainName(&m->MulticastHostname, &newmname)) { LogMsg("mDNS_SetFQDN - hostname unchanged"); return; }
+
+ mDNS_Lock(m);
+ 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 SRV records (and the like) that reference our
+ // host name in their rdata get updated to reference this new host name
+ for (rr = m->ResourceRecords; rr; rr=rr->next) if (rr->HostTarget) SetTargetToHostName(m, rr);
+ for (rr = m->DuplicateRecords; rr; rr=rr->next) if (rr->HostTarget) SetTargetToHostName(m, rr);
+
+ mDNS_Unlock(m);
+ }
+
+mDNSexport 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->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback
+ m->MainCallback(m, result);
+ m->mDNS_reentrancy--; // Decrement to block mDNS API calls again
+ }
+ }
+ 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->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback
+ m->MainCallback(m, mStatus_NameConflict);
+ m->mDNS_reentrancy--; // Decrement to block mDNS API calls again
+ }
+
+ // 2. If the client callback didn't do it, add (or increment) an index ourselves
+ 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 %ld 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;
+ }
+ }
+
+mDNSexport mStatus mDNS_RegisterInterface(mDNS *const m, NetworkInterfaceInfo *set, mDNSBool flapping)
+ {
+ mDNSBool FirstOfType = mDNStrue;
+ NetworkInterfaceInfo **p = &m->HostInterfaces;
+
+ if (!set->InterfaceID)
+ { LogMsg("Error! Tried to register a NetworkInterfaceInfo %#a with zero InterfaceID", &set->ip); return(mStatus_Invalid); }
+
+ if (!mDNSAddressIsValidNonZero(&set->mask))
+ { LogMsg("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 = (set->ip.type == mDNSAddrType_IPv4 && set->McastTxRx);
+ set->IPv6Available = (set->ip.type == mDNSAddrType_IPv6 && set->McastTxRx);
+
+ // Scan list to see if this InterfaceID is already represented
+ while (*p)
+ {
+ if (*p == set)
+ {
+ LogMsg("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);
+
+ LogOperation("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");
+
+ // 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 && ((m->KnownBugs & mDNS_KnownBug_PhantomInterfaces) || FirstOfType || set->InterfaceActive))
+ {
+ DNSQuestion *q;
+ AuthRecord *rr;
+ // If flapping, delay between first and second queries is eight seconds instead of one
+ mDNSs32 delay = flapping ? mDNSPlatformOneSecond * 5 : 0;
+ mDNSu8 announce = 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.
+ if (!m->SuppressSending) m->SuppressSending = m->timenow + (mDNSs32)mDNSRandom((mDNSu32)InitialQuestionInterval);
+
+ if (flapping)
+ {
+ LogMsg("Note: Frequent transitions for interface %s (%#a); network traffic reduction measures in effect", set->ifname, &set->ip);
+ if (!m->SuppressProbes ||
+ m->SuppressProbes - (m->timenow + delay) < 0)
+ m->SuppressProbes = (m->timenow + delay);
+ }
+
+ for (q = m->Questions; q; q=q->next) // Scan our list of questions
+ if (!q->InterfaceID || q->InterfaceID == set->InterfaceID) // If non-specific Q, or Q on this specific interface,
+ { // then reactivate this question
+ mDNSs32 initial = (flapping && q->FlappingInterface != set->InterfaceID) ? InitialQuestionInterval * 8 : InitialQuestionInterval;
+ mDNSs32 qdelay = (flapping && q->FlappingInterface != set->InterfaceID) ? mDNSPlatformOneSecond * 5 : 0;
+ if (flapping && q->FlappingInterface == set->InterfaceID)
+ LogOperation("No cache records for %##s (%s) expired; no need for immediate question", 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
+ }
+ if (q->LastQTime - (m->timenow - q->ThisQInterval + qdelay) > 0)
+ 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 (!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 < announce) rr->AnnounceCount = announce;
+ rr->ThisAPInterval = DefaultAPIntervalForRecordType(rr->resrec.RecordType);
+ InitializeLastAPTime(m, rr);
+ }
+ }
+
+ 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;
+ // If this platform has the "phantom interfaces" known bug (e.g. Jaguar), we have to revalidate records every
+ // time an interface goes away. Otherwise, when you disconnect the Ethernet cable, the system reports that it
+ // still has an IPv6 address, and if we don't revalidate those records don't get deleted in a timely fashion.
+ if (m->KnownBugs & mDNS_KnownBug_PhantomInterfaces) revalidate = mDNStrue;
+
+ 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; }
+
+ // 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;
+ for (intf = m->HostInterfaces; intf; intf = intf->next)
+ if (intf->InterfaceID == set->InterfaceID)
+ break;
+ if (intf)
+ {
+ LogOperation("mDNS_DeregisterInterface: Another representative of InterfaceID %p %s (%#a) exists;"
+ " making it active", set->InterfaceID, set->ifname, &set->ip);
+ intf->InterfaceActive = mDNStrue;
+ UpdateInterfaceProtocols(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;
+ LogOperation("mDNS_DeregisterInterface: Last representative of InterfaceID %p %s (%#a) deregistered;"
+ " marking questions etc. dormant", set->InterfaceID, set->ifname, &set->ip);
+
+ if (flapping)
+ LogMsg("Note: Frequent transitions for interface %s (%#a); network traffic reduction measures in effect",
+ 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->FlappingInterface = set->InterfaceID;
+ }
+
+ // 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 (!flapping) PurgeCacheResourceRecord(m, rr);
+ else mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForFlappingInterface);
+ }
+ }
+ }
+
+ // 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->mDNS_shutdown)
+ {
+ mDNSu32 slot;
+ CacheGroup *cg;
+ CacheRecord *rr;
+ m->NextCacheCheck = m->timenow;
+ FORALL_CACHERECORDS(slot, cg, rr)
+ if (rr->resrec.InterfaceID == set->InterfaceID)
+ mDNS_Reconfirm_internal(m, rr, kDefaultReconfirmTimeForFlappingInterface);
+ }
+
+ 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 (%ld)", 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 PTR 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;
+ if (sr->RR_PTR.resrec.RecordType != kDNSRecordTypeUnregistered) return;
+ for (i=0; i<sr->NumSubTypes; i++) if (sr->SubTypes[i].resrec.RecordType != kDNSRecordTypeUnregistered) return;
+
+ // 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;
+ }
+
+ // 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
+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,
+ const mDNSInterfaceID InterfaceID, mDNSServiceCallback Callback, void *Context)
+ {
+ mStatus err;
+ mDNSu32 i;
+
+ sr->ServiceCallback = Callback;
+ sr->ServiceContext = Context;
+ sr->Extras = mDNSNULL;
+ sr->NumSubTypes = NumSubTypes;
+ sr->SubTypes = SubTypes;
+ sr->Conflict = mDNSfalse;
+ if (host && host->c[0]) sr->Host = *host;
+ else sr->Host.c[0] = 0;
+
+ // If port number is zero, that means the client is really trying to do a RegisterNoSuchService
+ if (!port.NotAnInteger)
+ return(mDNS_RegisterNoSuchService(m, &sr->RR_SRV, name, type, domain, mDNSNULL, mDNSInterface_Any, NSSCallback, sr));
+
+ // Initialize the AuthRecord objects to sane values
+ mDNS_SetupResourceRecord(&sr->RR_ADV, mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeAdvisory, ServiceCallback, sr);
+ mDNS_SetupResourceRecord(&sr->RR_PTR, mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeShared, ServiceCallback, sr);
+ mDNS_SetupResourceRecord(&sr->RR_SRV, mDNSNULL, InterfaceID, kDNSType_SRV, kHostNameTTL, kDNSRecordTypeUnique, ServiceCallback, sr);
+ mDNS_SetupResourceRecord(&sr->RR_TXT, mDNSNULL, InterfaceID, kDNSType_TXT, kStandardTTL, kDNSRecordTypeUnique, ServiceCallback, sr);
+
+ // 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.resrec.name, (domainlabel*)"\x09_services", (domainname*)"\x07_dns-sd\x04_udp", domain) == mDNSNULL)
+ return(mStatus_BadParamErr);
+ if (ConstructServiceName(sr->RR_PTR.resrec.name, mDNSNULL, type, domain) == mDNSNULL) return(mStatus_BadParamErr);
+ if (ConstructServiceName(sr->RR_SRV.resrec.name, name, type, domain) == mDNSNULL) return(mStatus_BadParamErr);
+ AssignDomainName(sr->RR_TXT.resrec.name, 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
+ 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, ServiceCallback, sr);
+ if (ConstructServiceName(sr->SubTypes[i].resrec.name, mDNSNULL, &st, domain) == mDNSNULL) return(mStatus_BadParamErr);
+ AssignDomainName(&sr->SubTypes[i].resrec.rdata->u.name, sr->RR_SRV.resrec.name);
+ 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 HostTarget tells DNS that the target of this SRV is to be automatically kept in sync with our host name
+ if (sr->Host.c[0]) AssignDomainName(&sr->RR_SRV.resrec.rdata->u.srv.target, &sr->Host);
+ else { sr->RR_SRV.HostTarget = mDNStrue; 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
+ 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(txtinfo, sr->RR_TXT.resrec.rdata->u.txt.c, txtlen);
+ }
+ sr->RR_TXT.DependentOn = &sr->RR_SRV;
+
+#ifndef UNICAST_DISABLED
+ // If the client has specified an explicit InterfaceID,
+ // then we do a multicast registration on that interface, even for unicast domains.
+ if (!(InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(sr->RR_SRV.resrec.name)))
+ {
+ mStatus status;
+ mDNS_Lock(m);
+ // 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.
+ // (We have to duplicate this check here because uDNS_RegisterService() bypasses the usual mDNS_Register_internal() bottleneck)
+ if (!sr->RR_TXT.resrec.rdlength) { sr->RR_TXT.resrec.rdlength = 1; sr->RR_TXT.resrec.rdata->u.txt.c[0] = 0; }
+ status = uDNS_RegisterService(m, sr);
+ mDNS_Unlock(m);
+ return(status);
+ }
+#endif
+ mDNS_Lock(m);
+ err = mDNS_Register_internal(m, &sr->RR_SRV);
+ 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_Close, 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)
+ {
+ ExtraResourceRecord **e;
+ mStatus status;
+
+ extra->next = mDNSNULL;
+ mDNS_SetupResourceRecord(&extra->r, rdata, sr->RR_PTR.resrec.InterfaceID,
+ extra->r.resrec.rrtype, ttl, kDNSRecordTypeUnique, ServiceCallback, sr);
+ AssignDomainName(extra->r.resrec.name, sr->RR_SRV.resrec.name);
+
+#ifndef UNICAST_DISABLED
+ if (!(sr->RR_SRV.resrec.InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(sr->RR_SRV.resrec.name)))
+ {
+ mDNS_Lock(m);
+ // 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.
+ // (We have to duplicate this check here because uDNS_AddRecordToService() bypasses the usual mDNS_Register_internal() bottleneck)
+ if (extra->r.resrec.rrtype == kDNSType_TXT && extra->r.resrec.rdlength == 0)
+ { extra->r.resrec.rdlength = 1; extra->r.resrec.rdata->u.txt.c[0] = 0; }
+ status = uDNS_AddRecordToService(m, sr, extra);
+ mDNS_Unlock(m);
+ return status;
+ }
+#endif
+
+ 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", extra->r.resrec.name->c);
+
+ 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;
+#ifndef UNICAST_DISABLED
+ if (!(sr->RR_SRV.resrec.InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(sr->RR_SRV.resrec.name)))
+ status = uDNS_DeregisterRecord(m, &extra->r);
+ else
+#endif
+ 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;
+ domainname *host = mDNSNULL;
+ 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))
+ LogMsg("%##s service renamed from \"%#s\" to \"%#s\"", type.c, name1.c, newname->c);
+ else LogMsg("%##s service (domain %##s) renamed from \"%#s\" to \"%#s\"",type.c, domain.c, name1.c, newname->c);
+
+ if (sr->RR_SRV.HostTarget == mDNSfalse && sr->Host.c[0]) host = &sr->Host;
+
+ 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);
+
+ // 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);
+ }
+
+ 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(mDNS *const m, ServiceRecordSet *sr)
+ {
+ // If port number is zero, that means this was actually registered using mDNS_RegisterNoSuchService()
+ if (!sr->RR_SRV.resrec.rdata->u.srv.port.NotAnInteger) return(mDNS_DeregisterNoSuchService(m, &sr->RR_SRV));
+
+#ifndef UNICAST_DISABLED
+ if (!(sr->RR_SRV.resrec.InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(sr->RR_SRV.resrec.name)))
+ {
+ mStatus status;
+ mDNS_Lock(m);
+ status = uDNS_DeregisterService(m, sr);
+ mDNS_Unlock(m);
+ return(status);
+ }
+#endif
+ 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)
+ {
+ debugf("Service set for %##s already in the process of deregistering", sr->RR_SRV.resrec.name->c);
+ 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, mDNS_Dereg_normal);
+
+ // 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], mDNS_Dereg_normal);
+
+ // Be sure to deregister the PTR last!
+ // Deregistering this record is what triggers the mStatus_MemFree callback to ServiceCallback,
+ // which in turn passes on the mStatus_MemFree (or mStatus_NameConflict) 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.
+ status = mDNS_Deregister_internal(m, &sr->RR_PTR, mDNS_Dereg_normal);
+ 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)
+ {
+ mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceID, kDNSType_SRV, kHostNameTTL, kDNSRecordTypeUnique, Callback, Context);
+ if (ConstructServiceName(rr->resrec.name, 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->HostTarget = mDNStrue;
+ return(mDNS_Register(m, rr));
+ }
+
+mDNSexport mStatus mDNS_AdvertiseDomains(mDNS *const m, AuthRecord *rr,
+ mDNS_DomainType DomainType, const mDNSInterfaceID InterfaceID, char *domname)
+ {
+ mDNS_SetupResourceRecord(rr, mDNSNULL, InterfaceID, kDNSType_PTR, kStandardTTL, kDNSRecordTypeShared, mDNSNULL, mDNSNULL);
+ if (!MakeDomainNameFromDNSNameString(rr->resrec.name, mDNS_DomainTypeNames[DomainType])) return(mStatus_BadParamErr);
+ if (!MakeDomainNameFromDNSNameString(&rr->resrec.rdata->u.name, domname)) return(mStatus_BadParamErr);
+ return(mDNS_Register(m, rr));
+ }
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#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->mDNSPlatformStatus = mStatus_Waiting;
+ m->UnicastPort4 = zeroIPPort;
+ m->UnicastPort6 = zeroIPPort;
+ 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->mDNS_shutdown = mDNSfalse;
+ 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->ExpectUnicastResponse = timenow + 0x78000000;
+ m->RandomQueryDelay = 0;
+ m->RandomReconfirmDelay = 0;
+ m->PktNum = 0;
+ m->SendDeregistrations = mDNSfalse;
+ m->SendImmediateAnswers = mDNSfalse;
+ m->SleepState = mDNSfalse;
+
+ // These fields only required for mDNS Searcher...
+ m->Questions = mDNSNULL;
+ m->NewQuestions = mDNSNULL;
+ m->CurrentQuestion = mDNSNULL;
+ m->LocalOnlyQuestions = mDNSNULL;
+ m->NewLocalOnlyQuestions = 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;
+
+ mDNS_GrowCache_internal(m, rrcachestorage, rrcachesize);
+
+ // 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->CurrentRecord = mDNSNULL;
+ m->HostInterfaces = mDNSNULL;
+ m->ProbeFailTime = 0;
+ m->NumFailedProbes = 0;
+ m->SuppressProbes = 0;
+
+#ifndef UNICAST_DISABLED
+ uDNS_Init(m);
+ m->SuppressStdPort53Queries = 0;
+#endif
+ result = mDNSPlatformInit(m);
+
+ return(result);
+ }
+
+mDNSexport void mDNSCoreInitComplete(mDNS *const m, mStatus result)
+ {
+ m->mDNSPlatformStatus = result;
+ if (m->MainCallback)
+ {
+ mDNS_Lock(m);
+ m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback
+ m->MainCallback(m, mStatus_NoError);
+ m->mDNS_reentrancy--; // Decrement to block mDNS API calls again
+ mDNS_Unlock(m);
+ }
+ }
+
+mDNSexport void mDNS_Close(mDNS *const m)
+ {
+ mDNSu32 rrcache_active = 0;
+ mDNSu32 rrcache_totalused = 0;
+ mDNSu32 slot;
+ NetworkInterfaceInfo *intf;
+ mDNS_Lock(m);
+
+ m->mDNS_shutdown = mDNStrue;
+
+#ifndef UNICAST_DISABLED
+ uDNS_Close(m);
+#endif
+ 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 *rr = cg->members;
+ cg->members = cg->members->next;
+ if (rr->CRActiveQuestion) rrcache_active++;
+ ReleaseCacheRecord(m, rr);
+ }
+ cg->rrcache_tail = &cg->members;
+ ReleaseCacheGroup(m, &m->rrcache_hash[slot]);
+ }
+ }
+ debugf("mDNS_Close: 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 (intf = m->HostInterfaces; intf; intf = intf->next)
+ if (intf->Advertise)
+ DeadvertiseInterface(m, intf);
+
+ // Make sure there are nothing but deregistering records remaining in the list
+ if (m->CurrentRecord) LogMsg("mDNS_Close ERROR m->CurrentRecord already set");
+ m->CurrentRecord = m->ResourceRecords;
+ while (m->CurrentRecord)
+ {
+ AuthRecord *rr = m->CurrentRecord;
+ if (rr->resrec.RecordType != kDNSRecordTypeDeregistering)
+ {
+ debugf("mDNS_Close: Record type %X still in ResourceRecords list %##s", rr->resrec.RecordType, rr->resrec.name->c);
+ mDNS_Deregister_internal(m, rr, mDNS_Dereg_normal);
+ }
+ else
+ m->CurrentRecord = rr->next;
+ }
+
+ if (m->ResourceRecords) debugf("mDNS_Close: Sending final packets for deregistering records");
+ else debugf("mDNS_Close: No deregistering records remain");
+
+ // If any deregistering records remain, send their deregistration announcements before we exit
+ if (m->mDNSPlatformStatus != mStatus_NoError) DiscardDeregistrations(m);
+ else if (m->ResourceRecords) SendResponses(m);
+ if (m->ResourceRecords) LogMsg("mDNS_Close failed to send goodbye for: %s", ARDisplayString(m, m->ResourceRecords));
+
+ mDNS_Unlock(m);
+ debugf("mDNS_Close: mDNSPlatformClose");
+ mDNSPlatformClose(m);
+ debugf("mDNS_Close: done");
+ }
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSDebug.c b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSDebug.c
new file mode 100644
index 0000000000..656b36b774
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSDebug.c
@@ -0,0 +1,152 @@
+/* -*- 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
+
+ Change History (most recent first):
+
+$Log: mDNSDebug.c,v $
+Revision 1.7 2006/08/14 23:24:56 cheshire
+Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
+
+Revision 1.6 2005/01/27 22:57:56 cheshire
+Fix compile errors on gcc4
+
+Revision 1.5 2004/09/17 01:08:55 cheshire
+Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h
+ The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces
+ declared in that file are ONLY appropriate to single-address-space embedded applications.
+ For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used.
+
+Revision 1.4 2004/06/11 22:36:51 cheshire
+Fixes for compatibility with Windows
+
+Revision 1.3 2004/01/28 21:14:23 cheshire
+Reconcile debug_mode and gDebugLogging into a single flag (mDNS_DebugMode)
+
+Revision 1.2 2003/12/09 01:30:40 rpantos
+Fix usage of ARGS... macros to build properly on Windows.
+
+Revision 1.1 2003/12/08 21:11:42; rpantos
+Changes necessary to support mDNSResponder on Linux.
+
+*/
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "mDNSDebug.h"
+
+#include <stdio.h>
+
+#if defined(WIN32)
+// Need to add Windows syslog support here
+#define LOG_PID 0x01
+#define LOG_CONS 0x02
+#define LOG_PERROR 0x20
+#define openlog(A,B,C) (void)(A); (void)(B)
+#define syslog(A,B,C)
+#define closelog()
+#else
+#include <syslog.h>
+#endif
+
+#include "mDNSEmbeddedAPI.h"
+
+#if MDNS_DEBUGMSGS
+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
+mDNSexport void debugf_(const char *format, ...)
+ {
+ unsigned char buffer[512];
+ va_list ptr;
+ va_start(ptr,format);
+ buffer[mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr)] = 0;
+ va_end(ptr);
+ fprintf(stderr,"%s\n", buffer);
+ fflush(stderr);
+ }
+#endif
+
+#if MDNS_DEBUGMSGS > 1
+mDNSexport void verbosedebugf_(const char *format, ...)
+ {
+ unsigned char buffer[512];
+ va_list ptr;
+ va_start(ptr,format);
+ buffer[mDNS_vsnprintf((char *)buffer, sizeof(buffer), format, ptr)] = 0;
+ va_end(ptr);
+ fprintf(stderr,"%s\n", buffer);
+ fflush(stderr);
+ }
+#endif
+
+mDNSlocal void WriteLogMsg(const char *ident, const char *buffer, int logoptflags, int logpriority)
+ {
+ if (mDNS_DebugMode) // In debug mode we write to stderr
+ {
+ fprintf(stderr,"%s\n", buffer);
+ fflush(stderr);
+ }
+ else // else, in production mode, we write to syslog
+ {
+ openlog(ident, LOG_PERROR | logoptflags, LOG_DAEMON);
+ syslog(logpriority, "%s", buffer);
+ closelog();
+ }
+ }
+
+// Log message with default "mDNSResponder" ident string at the start
+mDNSexport void LogMsg(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);
+ WriteLogMsg("mDNSResponder", buffer, 0, LOG_ERR);
+ }
+
+// Log message with specified ident string at the start
+mDNSexport void LogMsgIdent(const char *ident, 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);
+ WriteLogMsg(ident, buffer, ident && *ident ? LOG_PID : 0, LOG_INFO);
+ }
+
+// Log message with no ident string at the start
+mDNSexport void LogMsgNoIdent(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);
+ WriteLogMsg("", buffer, 0, LOG_INFO);
+ }
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSDebug.h b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSDebug.h
new file mode 100644
index 0000000000..18be95a2b5
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSDebug.h
@@ -0,0 +1,192 @@
+/* -*- 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.
+
+ Change History (most recent first):
+
+$Log: mDNSDebug.h,v $
+Revision 1.26.2.1 2006/08/29 06:24:22 cheshire
+Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
+
+Revision 1.26 2005/07/04 22:40:26 cheshire
+Additional debugging code to help catch memory corruption
+
+Revision 1.25 2004/12/14 21:34:16 cheshire
+Add "#define ANSWER_REMOTE_HOSTNAME_QUERIES 0" and comment
+
+Revision 1.24 2004/09/16 01:58:21 cheshire
+Fix compiler warnings
+
+Revision 1.23 2004/05/18 23:51:25 cheshire
+Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers
+
+Revision 1.22 2004/04/22 04:27:42 cheshire
+Spacing tidyup
+
+Revision 1.21 2004/04/14 23:21:41 ksekar
+Removed accidental checkin of MALLOC_DEBUGING flag in 1.20
+
+Revision 1.20 2004/04/14 23:09:28 ksekar
+Support for TSIG signed dynamic updates.
+
+Revision 1.19 2004/03/15 18:57:59 cheshire
+Undo last checkin that accidentally made verbose debugging the default for all targets
+
+Revision 1.18 2004/03/13 01:57:33 ksekar
+<rdar://problem/3192546>: DynDNS: Dynamic update of service records
+
+Revision 1.17 2004/01/28 21:14:23 cheshire
+Reconcile debug_mode and gDebugLogging into a single flag (mDNS_DebugMode)
+
+Revision 1.16 2003/12/09 01:30:06 rpantos
+Fix usage of ARGS... macros to build properly on Windows.
+
+Revision 1.15 2003/12/08 20:55:26 rpantos
+Move some definitions here from mDNSMacOSX.h.
+
+Revision 1.14 2003/08/12 19:56:24 cheshire
+Update to APSL 2.0
+
+Revision 1.13 2003/07/02 21:19:46 cheshire
+<rdar://problem/3313413> Update copyright notices, etc., in source code comments
+
+Revision 1.12 2003/05/26 03:01:27 cheshire
+<rdar://problem/3268904> sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead
+
+Revision 1.11 2003/05/21 17:48:10 cheshire
+Add macro to enable GCC's printf format string checking
+
+Revision 1.10 2003/04/26 02:32:57 cheshire
+Add extern void LogMsg(const char *format, ...);
+
+Revision 1.9 2002/09/21 20:44:49 zarzycki
+Added APSL info
+
+Revision 1.8 2002/09/19 04:20:43 cheshire
+Remove high-ascii characters that confuse some systems
+
+Revision 1.7 2002/09/16 18:41:42 cheshire
+Merge in license terms from Quinn's copy, in preparation for Darwin release
+
+*/
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#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
+#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
+
+#if MDNS_DEBUGMSGS
+#define debugf debugf_
+extern void debugf_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2);
+#else // If debug breaks are off, use a preprocessor trick to optimize those calls out of the code
+ #if (defined(__GNUC__))
+ #define debugf( ARGS... ) ((void)0)
+ #elif (defined(__MWERKS__))
+ #define debugf( ... )
+ #else
+ #define debugf 1 ? ((void)0) : (void)
+ #endif
+#endif
+
+#if MDNS_DEBUGMSGS > 1
+#define verbosedebugf verbosedebugf_
+extern void verbosedebugf_(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2);
+#else
+ #if (defined(__GNUC__))
+ #define verbosedebugf( ARGS... ) ((void)0)
+ #elif (defined(__MWERKS__))
+ #define verbosedebugf( ... )
+ #else
+ #define verbosedebugf 1 ? ((void)0) : (void)
+ #endif
+#endif
+
+// LogMsg is used even in shipping code, to write truly serious error messages to syslog (or equivalent)
+extern int mDNS_DebugMode; // If non-zero, LogMsg() writes to stderr instead of syslog
+extern void LogMsg(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2);
+extern void LogMsgIdent(const char *ident, const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(2,3);
+extern void LogMsgNoIdent(const char *format, ...) IS_A_PRINTF_STYLE_FUNCTION(1,2);
+
+// 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 0
+
+#if 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);
+#else
+#define mallocL(X,Y) malloc(Y)
+#define freeL(X,Y) free(Y)
+#endif
+
+#if MACOSX_MDNS_MALLOC_DEBUGGING >= 2
+#define LogMalloc LogMsg
+#else
+ #if (defined( __GNUC__ ))
+ #define LogMalloc(ARGS...) ((void)0)
+ #elif (defined( __MWERKS__ ))
+ #define LogMalloc( ... )
+ #else
+ #define LogMalloc 1 ? ((void)0) : (void)
+ #endif
+#endif
+
+#define LogAllOperations 0
+
+#if LogAllOperations
+#define LogOperation LogMsg
+#else
+#define LogOperation debugf
+#endif
+
+#define ForceAlerts 0
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSEmbeddedAPI.h b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSEmbeddedAPI.h
new file mode 100644
index 0000000000..5ed2c92240
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSEmbeddedAPI.h
@@ -0,0 +1,2850 @@
+/* -*- 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.
+
+
+ Change History (most recent first):
+
+$Log: mDNSEmbeddedAPI.h,v $
+Revision 1.296.2.1 2006/08/29 06:24:22 cheshire
+Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
+
+Revision 1.296 2006/06/29 05:28:01 cheshire
+Added comment about mDNSlocal and mDNSexport
+
+Revision 1.295 2006/06/29 03:02:43 cheshire
+<rdar://problem/4607042> mDNSResponder NXDOMAIN and CNAME support
+
+Revision 1.294 2006/06/28 06:50:08 cheshire
+In future we may want to change definition of mDNSs32 from "signed long" to "signed int"
+I doubt anyone is building mDNSResponder on systems where int is 16-bits,
+but lets add a compile-time assertion to make sure.
+
+Revision 1.293 2006/06/12 18:00:43 cheshire
+To make code a little more defensive, check _ILP64 before _LP64,
+in case both are set by mistake on some platforms
+
+Revision 1.292 2006/03/19 17:00:57 cheshire
+Define symbol MaxMsg instead of using hard-coded constant value '80'
+
+Revision 1.291 2006/03/19 02:00:07 cheshire
+<rdar://problem/4073825> Improve logic for delaying packets after repeated interface transitions
+
+Revision 1.290 2006/03/08 22:42:23 cheshire
+Fix spelling mistake: LocalReverseMapomain -> LocalReverseMapDomain
+
+Revision 1.289 2006/02/26 00:54:41 cheshire
+Fixes to avoid code generation warning/error on FreeBSD 7
+
+Revision 1.288 2005/12/21 03:24:58 cheshire
+<rdar://problem/4388858> Code changes required to compile on EFI
+
+Revision 1.287 2005/10/20 00:10:33 cheshire
+<rdar://problem/4290265> Add check to avoid crashing NAT gateways that have buggy DNS relay code
+
+Revision 1.286 2005/09/24 01:09:40 cheshire
+Fix comment typos
+
+Revision 1.285 2005/09/16 20:57:47 cheshire
+Add macro mDNS_TimeNow_NoLock(m) to get properly adjusted time without also acquiring lock
+
+Revision 1.284 2005/07/29 18:04:22 ksekar
+<rdar://problem/4137930> Hostname registration should register IPv6 AAAA record with DNS Update
+
+Revision 1.283 2005/05/13 20:45:09 ksekar
+<rdar://problem/4074400> Rapid wide-area txt record updates don't work
+
+Revision 1.282 2005/03/16 00:42:32 ksekar
+<rdar://problem/4012279> Long-lived queries not working on Windows
+
+Revision 1.281 2005/02/25 17:47:44 ksekar
+<rdar://problem/4021868> SendServiceRegistration fails on wake from sleep
+
+Revision 1.280 2005/02/25 04:21:00 cheshire
+<rdar://problem/4015377> mDNS -F returns the same domain multiple times with different casing
+
+Revision 1.279 2005/02/17 01:56:14 cheshire
+Increase ifname field to 64 bytes
+
+Revision 1.278 2005/02/09 23:38:51 ksekar
+<rdar://problem/3993508> Reregister hostname when DNS server changes but IP address does not
+
+Revision 1.277 2005/02/09 23:31:12 ksekar
+<rdar://problem/3984374> NAT-PMP response callback should return a boolean indicating if the packet matched the request
+
+Revision 1.276 2005/02/01 19:33:29 ksekar
+<rdar://problem/3985239> Keychain format too restrictive
+
+Revision 1.275 2005/01/27 22:57:55 cheshire
+Fix compile errors on gcc4
+
+Revision 1.274 2005/01/19 21:01:54 ksekar
+<rdar://problem/3955355> uDNS needs to support subtype registration and browsing
+
+Revision 1.273 2005/01/19 19:15:31 ksekar
+Refinement to <rdar://problem/3954575> - Simplify mDNS_PurgeResultsForDomain logic and move into daemon layer
+
+Revision 1.272 2005/01/18 18:10:55 ksekar
+<rdar://problem/3954575> Use 10.4 resolver API to get search domains
+
+Revision 1.271 2005/01/15 00:56:41 ksekar
+<rdar://problem/3954575> Unicast services don't disappear when logging
+out of VPN
+
+Revision 1.270 2005/01/14 18:34:22 ksekar
+<rdar://problem/3954571> Services registered outside of firewall don't succeed after location change
+
+Revision 1.269 2005/01/11 22:50:52 ksekar
+Fixed constant naming (was using kLLQ_DefLease for update leases)
+
+Revision 1.268 2004/12/22 22:25:47 ksekar
+<rdar://problem/3734265> NATPMP: handle location changes
+
+Revision 1.267 2004/12/22 00:13:49 ksekar
+<rdar://problem/3873993> Change version, port, and polling interval for LLQ
+
+Revision 1.266 2004/12/18 03:13:45 cheshire
+<rdar://problem/3751638> kDNSServiceInterfaceIndexLocalOnly should return all local records
+
+Revision 1.265 2004/12/17 23:37:45 cheshire
+<rdar://problem/3485365> Guard against repeating wireless dissociation/re-association
+(and other repetitive configuration changes)
+
+Revision 1.264 2004/12/17 05:25:46 cheshire
+<rdar://problem/3925163> Shorten DNS-SD queries to avoid NAT bugs
+
+Revision 1.263 2004/12/16 20:40:25 cheshire
+Fix compile warnings
+
+Revision 1.262 2004/12/16 20:13:00 cheshire
+<rdar://problem/3324626> Cache memory management improvements
+
+Revision 1.261 2004/12/14 21:21:20 ksekar
+<rdar://problem/3825979> NAT-PMP: Update response format to contain "Seconds Since Boot"
+
+Revision 1.260 2004/12/12 23:51:42 ksekar
+<rdar://problem/3845683> Wide-area registrations should fallback to using DHCP hostname as target
+
+Revision 1.259 2004/12/11 20:55:29 ksekar
+<rdar://problem/3916479> Clean up registration state machines
+
+Revision 1.258 2004/12/10 20:48:32 cheshire
+<rdar://problem/3705229> Need to pick final EDNS numbers for LLQ and GC
+
+Revision 1.257 2004/12/10 02:09:23 cheshire
+<rdar://problem/3898376> Modify default TTLs
+
+Revision 1.256 2004/12/09 03:15:40 ksekar
+<rdar://problem/3806610> use _legacy instead of _default to find "empty string" browse domains
+
+Revision 1.255 2004/12/07 22:48:37 cheshire
+Tidying
+
+Revision 1.254 2004/12/07 21:26:04 ksekar
+<rdar://problem/3908336> DNSServiceRegisterRecord() can crash on deregistration
+
+Revision 1.253 2004/12/07 20:42:33 cheshire
+Add explicit context parameter to mDNS_RemoveRecordFromService()
+
+Revision 1.252 2004/12/07 03:02:12 ksekar
+Fixed comments, grouped unicast-specific routines together
+
+Revision 1.251 2004/12/06 21:15:22 ksekar
+<rdar://problem/3884386> mDNSResponder crashed in CheckServiceRegistrations
+
+Revision 1.250 2004/12/04 02:12:45 cheshire
+<rdar://problem/3517236> mDNSResponder puts LargeCacheRecord on the stack
+
+Revision 1.249 2004/12/03 05:18:33 ksekar
+<rdar://problem/3810596> mDNSResponder needs to return more specific TSIG errors
+
+Revision 1.248 2004/12/02 20:03:48 ksekar
+<rdar://problem/3889647> Still publishes wide-area domains even after switching to a local subnet
+
+Revision 1.247 2004/12/01 20:57:19 ksekar
+<rdar://problem/3873921> Wide Area Service Discovery must be split-DNS aware
+
+Revision 1.246 2004/11/29 23:26:32 cheshire
+Added NonZeroTime() function, which usually returns the value given, with the exception
+that if the value given is zero, it returns one instead. For timer values where zero is
+used to mean "not set", this can be used to ensure that setting them to the result of an
+interval computation (e.g. "now+interval") does not inadvertently result in a zero value.
+
+Revision 1.245 2004/11/25 01:28:09 cheshire
+<rdar://problem/3557050> Need to implement random delay for 'QU' unicast replies (and set cache flush bit too)
+
+Revision 1.244 2004/11/24 22:00:59 cheshire
+Move definition of mDNSAddressIsAllDNSLinkGroup() from mDNSMacOSX.c to mDNSEmbeddedAPI.h
+
+Revision 1.243 2004/11/23 22:43:53 cheshire
+Tidy up code alignment
+
+Revision 1.242 2004/11/23 03:39:46 cheshire
+Let interface name/index mapping capability live directly in JNISupport.c,
+instead of having to call through to the daemon via IPC to get this information.
+
+Revision 1.241 2004/11/22 17:16:19 ksekar
+<rdar://problem/3854298> Unicast services don't disappear when you disable all networking
+
+Revision 1.240 2004/11/19 02:32:43 ksekar
+Wide-Area Security: Add LLQ-ID to events
+
+Revision 1.239 2004/11/15 20:09:23 ksekar
+<rdar://problem/3719050> Wide Area support for Add/Remove record
+
+Revision 1.238 2004/11/12 03:16:48 rpantos
+rdar://problem/3809541 Add mDNSPlatformGetInterfaceByName, mDNSPlatformGetInterfaceName
+
+Revision 1.237 2004/11/10 20:40:53 ksekar
+<rdar://problem/3868216> LLQ mobility fragile on non-primary interface
+
+Revision 1.236 2004/11/01 20:36:11 ksekar
+<rdar://problem/3802395> mDNSResponder should not receive Keychain Notifications
+
+Revision 1.235 2004/11/01 17:48:14 cheshire
+Changed SOA serial number back to signed. RFC 1035 may describe it as "unsigned", but
+it's wrong. The SOA serial is a modular counter, as explained in "DNS & BIND", page
+137. Since C doesn't have a modular type, we used signed, C's closest approximation.
+
+Revision 1.234 2004/10/29 21:59:02 ksekar
+SOA serial should be a unsigned integer, as per RFC 1035
+
+Revision 1.233 2004/10/28 03:24:41 cheshire
+Rename m->CanReceiveUnicastOn as m->CanReceiveUnicastOn5353
+
+Revision 1.232 2004/10/26 06:20:23 cheshire
+Add mDNSAddressIsValidNonZero() macro
+
+Revision 1.231 2004/10/26 06:11:41 cheshire
+Add improved logging to aid in diagnosis of <rdar://problem/3842714> mDNSResponder crashed
+
+Revision 1.230 2004/10/26 03:52:02 cheshire
+Update checkin comments
+
+Revision 1.229 2004/10/25 19:30:52 ksekar
+<rdar://problem/3827956> Simplify dynamic host name structures
+
+Revision 1.228 2004/10/23 01:16:00 cheshire
+<rdar://problem/3851677> uDNS operations not always reliable on multi-homed hosts
+
+Revision 1.227 2004/10/22 20:52:07 ksekar
+<rdar://problem/3799260> Create NAT port mappings for Long Lived Queries
+
+Revision 1.226 2004/10/20 01:50:40 cheshire
+<rdar://problem/3844991> Cannot resolve non-local registrations using the mach API
+Implemented ForceMCast mode for AuthRecords as well as for Questions
+
+Revision 1.225 2004/10/19 21:33:17 cheshire
+<rdar://problem/3844991> Cannot resolve non-local registrations using the mach API
+Added flag 'kDNSServiceFlagsForceMulticast'. Passing through an interface id for a unicast name
+doesn't force multicast unless you set this flag to indicate explicitly that this is what you want
+
+Revision 1.224 2004/10/16 00:16:59 cheshire
+<rdar://problem/3770558> Replace IP TTL 255 check with local subnet source address check
+
+Revision 1.223 2004/10/15 23:00:17 ksekar
+<rdar://problem/3799242> Need to update LLQs on location changes
+
+Revision 1.222 2004/10/12 02:49:20 ksekar
+<rdar://problem/3831228> Clean up LLQ sleep/wake, error handling
+
+Revision 1.221 2004/10/10 06:57:15 cheshire
+Change definition of "localdomain" to make code compile a little smaller
+
+Revision 1.220 2004/10/06 01:44:19 cheshire
+<rdar://problem/3813936> Resolving too quickly sometimes returns stale TXT record
+
+Revision 1.219 2004/10/03 23:18:58 cheshire
+Move address comparison macros from DNSCommon.h to mDNSEmbeddedAPI.h
+
+Revision 1.218 2004/10/03 23:14:12 cheshire
+Add "mDNSEthAddr" type and "zeroEthAddr" constant
+
+Revision 1.217 2004/09/30 00:24:56 ksekar
+<rdar://problem/3695802> Dynamically update default registration domains on config change
+
+Revision 1.216 2004/09/27 23:24:32 cheshire
+Fix typo: SOA refresh interval is supposed to be unsigned
+
+Revision 1.215 2004/09/26 23:20:35 ksekar
+<rdar://problem/3813108> Allow default registrations in multiple wide-area domains
+
+Revision 1.214 2004/09/25 02:41:39 cheshire
+<rdar://problem/3637266> Deliver near-pending "remove" events before new "add" events
+
+Revision 1.213 2004/09/25 02:24:27 cheshire
+Removed unused rr->UseCount
+
+Revision 1.212 2004/09/24 20:57:39 cheshire
+<rdar://problem/3680902> Eliminate inappropriate casts that cause misaligned-address errors
+
+Revision 1.211 2004/09/24 20:33:22 cheshire
+Remove unused DNSDigest_MD5 declaration
+
+Revision 1.210 2004/09/23 20:21:07 cheshire
+<rdar://problem/3426876> Refine "immediate answer burst; restarting exponential backoff sequence" logic
+Associate a unique sequence number with each received packet, and only increment the count of recent answer
+packets if the packet sequence number for this answer record is not one we've already seen and counted.
+
+Revision 1.209 2004/09/23 20:14:39 cheshire
+Rename "question->RecentAnswers" to "question->RecentAnswerPkts"
+
+Revision 1.208 2004/09/23 00:50:53 cheshire
+<rdar://problem/3419452> Don't send a (DE) if a service is unregistered after wake from sleep
+
+Revision 1.207 2004/09/22 02:34:46 cheshire
+Move definitions of default TTL times from mDNS.c to mDNSEmbeddedAPI.h
+
+Revision 1.206 2004/09/22 00:41:59 cheshire
+Move tcp connection status codes into the legal range allocated for mDNS use
+
+Revision 1.205 2004/09/21 23:40:11 ksekar
+<rdar://problem/3810349> mDNSResponder to return errors on NAT traversal failure
+
+Revision 1.204 2004/09/21 23:29:50 cheshire
+<rdar://problem/3680045> DNSServiceResolve should delay sending packets
+
+Revision 1.203 2004/09/21 20:58:22 cheshire
+Add ifname field to NetworkInterfaceInfo_struct
+
+Revision 1.202 2004/09/17 00:46:34 cheshire
+mDNS_TimeNow should take const mDNS parameter
+
+Revision 1.201 2004/09/17 00:31:51 cheshire
+For consistency with ipv6, renamed rdata field 'ip' to 'ipv4'
+
+Revision 1.200 2004/09/17 00:19:10 cheshire
+For consistency with AllDNSLinkGroupv6, rename AllDNSLinkGroup to AllDNSLinkGroupv4
+
+Revision 1.199 2004/09/16 21:59:16 cheshire
+For consistency with zerov6Addr, rename zeroIPAddr to zerov4Addr
+
+Revision 1.198 2004/09/16 21:36:36 cheshire
+<rdar://problem/3803162> Fix unsafe use of mDNSPlatformTimeNow()
+Changes to add necessary locking calls around unicast DNS operations
+
+Revision 1.197 2004/09/16 00:24:48 cheshire
+<rdar://problem/3803162> Fix unsafe use of mDNSPlatformTimeNow()
+
+Revision 1.196 2004/09/14 23:42:35 cheshire
+<rdar://problem/3801296> Need to seed random number generator from platform-layer data
+
+Revision 1.195 2004/09/14 23:27:46 cheshire
+Fix compile errors
+
+Revision 1.194 2004/09/10 00:49:57 cheshire
+<rdar://problem/3787644> Add error code kDNSServiceErr_Firewall, for future use
+
+Revision 1.193 2004/09/03 19:23:05 ksekar
+<rdar://problem/3788460>: Need retransmission mechanism for wide-area service registrations
+
+Revision 1.192 2004/09/02 03:48:47 cheshire
+<rdar://problem/3709039> Disable targeted unicast query support by default
+1. New flag kDNSServiceFlagsAllowRemoteQuery to indicate we want to allow remote queries for this record
+2. New field AllowRemoteQuery in AuthRecord structure
+3. uds_daemon.c sets AllowRemoteQuery if kDNSServiceFlagsAllowRemoteQuery is set
+4. mDNS.c only answers remote queries if AllowRemoteQuery is set
+
+Revision 1.191 2004/08/25 00:37:27 ksekar
+<rdar://problem/3774635>: Cleanup DynDNS hostname registration code
+
+Revision 1.190 2004/08/18 17:35:41 ksekar
+<rdar://problem/3651443>: Feature #9586: Need support for Legacy NAT gateways
+
+Revision 1.189 2004/08/14 03:22:41 cheshire
+<rdar://problem/3762579> Dynamic DNS UI <-> mDNSResponder glue
+Add GetUserSpecifiedDDNSName() routine
+Convert ServiceRegDomain to domainname instead of C string
+Replace mDNS_GenerateFQDN/mDNS_GenerateGlobalFQDN with mDNS_SetFQDNs
+
+Revision 1.188 2004/08/13 23:46:58 cheshire
+"asyncronous" -> "asynchronous"
+
+Revision 1.187 2004/08/13 23:37:02 cheshire
+Now that we do both uDNS and mDNS, global replace "uDNS_info.hostname" with
+"uDNS_info.UnicastHostname" for clarity
+
+Revision 1.186 2004/08/13 23:25:00 cheshire
+Now that we do both uDNS and mDNS, global replace "m->hostname" with
+"m->MulticastHostname" for clarity
+
+Revision 1.185 2004/08/12 00:32:36 ksekar
+<rdar://problem/3759567>: LLQ Refreshes never terminate if unanswered
+
+Revision 1.184 2004/08/11 17:09:31 cheshire
+Add comment clarifying the applicability of these APIs
+
+Revision 1.183 2004/08/10 23:19:14 ksekar
+<rdar://problem/3722542>: DNS Extension daemon for Wide Area Service Discovery
+Moved routines/constants to allow extern access for garbage collection daemon
+
+Revision 1.182 2004/07/30 17:40:06 ksekar
+<rdar://problem/3739115>: TXT Record updates not available for wide-area services
+
+Revision 1.181 2004/07/29 19:27:15 ksekar
+NATPMP Support - minor fixes and cleanup
+
+Revision 1.180 2004/07/29 02:03:35 ksekar
+Delete unused #define and structure field
+
+Revision 1.179 2004/07/26 22:49:30 ksekar
+<rdar://problem/3651409>: Feature #9516: Need support for NATPMP in client
+
+Revision 1.178 2004/07/13 21:24:24 rpantos
+Fix for <rdar://problem/3701120>.
+
+Revision 1.177 2004/06/05 00:04:26 cheshire
+<rdar://problem/3668639>: wide-area domains should be returned in reg. domain enumeration
+
+Revision 1.176 2004/06/04 08:58:29 ksekar
+<rdar://problem/3668624>: Keychain integration for secure dynamic update
+
+Revision 1.175 2004/06/04 00:15:06 cheshire
+Move misplaced brackets
+
+Revision 1.174 2004/06/03 23:30:16 cheshire
+Remove extraneous blank lines and white space
+
+Revision 1.173 2004/06/03 03:09:58 ksekar
+<rdar://problem/3668626>: Garbage Collection for Dynamic Updates
+
+Revision 1.172 2004/06/01 23:46:50 ksekar
+<rdar://problem/3675149>: DynDNS: dynamically look up LLQ/Update ports
+
+Revision 1.171 2004/05/28 23:42:37 ksekar
+<rdar://problem/3258021>: Feature: DNS server->client notification on record changes (#7805)
+
+Revision 1.170 2004/05/18 23:51:25 cheshire
+Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers
+
+Revision 1.169 2004/05/13 04:54:20 ksekar
+Unified list copy/free code. Added symetric list for
+
+Revision 1.168 2004/05/12 22:03:09 ksekar
+Made GetSearchDomainList a true platform-layer call (declaration moved
+from mDNSMacOSX.h to mDNSEmbeddedAPI.h), implemented to return "local"
+only on non-OSX platforms. Changed call to return a copy of the list
+to avoid shared memory issues. Added a routine to free the list.
+
+Revision 1.167 2004/04/22 04:07:01 cheshire
+Fix from Bob Bradley: Don't try to do inline functions on compilers that don't support it
+
+Revision 1.166 2004/04/22 03:15:56 cheshire
+Fix use of "struct __attribute__((__packed__))" so it only applies on GCC >= 2.9
+
+Revision 1.165 2004/04/22 03:05:28 cheshire
+kDNSClass_ANY should be kDNSQClass_ANY
+
+Revision 1.164 2004/04/21 02:55:03 cheshire
+Update comments describing 'InterfaceActive' field
+
+Revision 1.163 2004/04/21 02:49:11 cheshire
+To reduce future confusion, renamed 'TxAndRx' to 'McastTxRx'
+
+Revision 1.162 2004/04/15 00:51:28 bradley
+Minor tweaks for Windows and C++ builds. Added casts for signed/unsigned integers and 64-bit pointers.
+Prefix some functions with mDNS to avoid conflicts. Disable benign warnings on Microsoft compilers.
+
+Revision 1.161 2004/04/14 23:09:28 ksekar
+Support for TSIG signed dynamic updates.
+
+Revision 1.160 2004/04/09 17:40:26 cheshire
+Remove unnecessary "Multicast" field -- it duplicates the semantics of the existing McastTxRx field
+
+Revision 1.159 2004/04/09 16:37:15 cheshire
+Suggestion from Bob Bradley:
+Move NumCacheRecordsForInterfaceID() to DNSCommon.c so it's available to all platform layers
+
+Revision 1.158 2004/04/02 19:38:33 cheshire
+Update comment about typical RR TTLs
+
+Revision 1.157 2004/04/02 19:35:53 cheshire
+Add clarifying comments about legal mDNSInterfaceID values
+
+Revision 1.156 2004/04/02 19:19:48 cheshire
+Add code to do optional logging of multi-packet KA list time intervals
+
+Revision 1.155 2004/03/24 00:29:45 ksekar
+Make it safe to call StopQuery in a unicast question callback
+
+Revision 1.154 2004/03/20 01:05:49 cheshire
+Test __LP64__ and __ILP64__ to compile properly on a wider range of 64-bit architectures
+
+Revision 1.153 2004/03/13 01:57:33 ksekar
+<rdar://problem/3192546>: DynDNS: Dynamic update of service records
+
+Revision 1.152 2004/03/09 02:27:16 cheshire
+Remove erroneous underscore in 'packed_struct' (makes no difference now, but might in future)
+
+Revision 1.151 2004/03/02 03:21:56 cheshire
+<rdar://problem/3549576> Properly support "_services._dns-sd._udp" meta-queries
+
+Revision 1.150 2004/02/21 02:06:24 cheshire
+Can't use anonymous unions -- they're non-standard and don't work on all compilers
+
+Revision 1.149 2004/02/06 23:04:19 ksekar
+Basic Dynamic Update support via mDNS_Register (dissabled via
+UNICAST_REGISTRATION #define)
+
+Revision 1.148 2004/02/03 19:47:36 ksekar
+Added an asynchronous state machine mechanism to uDNS.c, including
+calls to find the parent zone for a domain name. Changes include code
+in repository previously dissabled via "#if 0 incomplete". Codepath
+is currently unused, and will be called to create update records, etc.
+
+Revision 1.147 2004/02/03 18:57:35 cheshire
+Update comment for "IsLocalDomain()"
+
+Revision 1.146 2004/01/30 02:20:24 bradley
+Map inline to __inline when building with Microsoft C compilers since they do not support C99 inline.
+
+Revision 1.145 2004/01/29 02:59:17 ksekar
+Unicast DNS: Changed from a resource record oriented question/response
+matching to packet based matching. New callback architecture allows
+collections of records in a response to be processed differently
+depending on the nature of the request, and allows the same structure
+to be used for internal and client-driven queries with different processing needs.
+
+Revision 1.144 2004/01/28 20:20:45 ksekar
+Unified ActiveQueries and ActiveInternalQueries lists, using a flag to
+demux them. Check-in includes work-in-progress code, #ifdef'd out.
+
+Revision 1.143 2004/01/28 03:41:00 cheshire
+<rdar://problem/3541946>: Need ability to do targeted queries as well as multicast queries
+
+Revision 1.142 2004/01/28 02:30:07 ksekar
+Added default Search Domains to unicast browsing, controlled via
+Networking sharing prefs pane. Stopped sending unicast messages on
+every interface. Fixed unicast resolving via mach-port API.
+
+Revision 1.141 2004/01/27 20:15:22 cheshire
+<rdar://problem/3541288>: Time to prune obsolete code for listening on port 53
+
+Revision 1.140 2004/01/24 23:37:08 cheshire
+At Kiren's suggestion, made functions to convert mDNSOpaque16s to/from integer values
+
+Revision 1.139 2004/01/24 08:46:26 bradley
+Added InterfaceID<->Index platform interfaces since they are now used by all platforms for the DNS-SD APIs.
+
+Revision 1.138 2004/01/24 04:59:15 cheshire
+Fixes so that Posix/Linux, OS9, Windows, and VxWorks targets build again
+
+Revision 1.137 2004/01/24 03:40:56 cheshire
+Move mDNSAddrIsDNSMulticast() from DNSCommon.h to mDNSEmbeddedAPI.h so clients can use it
+
+Revision 1.136 2004/01/24 03:38:27 cheshire
+Fix minor syntactic error: Headers should use "extern" declarations, not "mDNSexport"
+
+Revision 1.135 2004/01/23 23:23:15 ksekar
+Added TCP support for truncated unicast messages.
+
+Revision 1.134 2004/01/22 03:54:11 cheshire
+Create special meta-interface 'mDNSInterface_ForceMCast' (-2),
+which means "do this query via multicast, even if it's apparently a unicast domain"
+
+Revision 1.133 2004/01/22 03:48:41 cheshire
+Make sure uDNS client doesn't accidentally use query ID zero
+
+Revision 1.132 2004/01/22 03:43:08 cheshire
+Export constants like mDNSInterface_LocalOnly so that the client layers can use them
+
+Revision 1.131 2004/01/21 21:53:18 cheshire
+<rdar://problem/3448144>: Don't try to receive unicast responses if we're not the first to bind to the UDP port
+
+Revision 1.130 2003/12/14 05:05:29 cheshire
+Add comments explaining mDNS_Init_NoCache and mDNS_Init_ZeroCacheSize
+
+Revision 1.129 2003/12/13 03:05:27 ksekar
+<rdar://problem/3192548>: DynDNS: Unicast query of service records
+
+Revision 1.128 2003/12/01 21:44:23 cheshire
+Add mStatus_BadInterfaceErr = -65552 for consistency with dns_sd.h
+
+Revision 1.127 2003/12/01 18:26:37 cheshire
+Also pack the OpaqueXX union types. Otherwise, on some systems, mDNSOpaque16 is four bytes!
+
+Revision 1.126 2003/12/01 18:23:48 cheshire
+<rdar://problem/3464646>: Scalar size problem in mDNS code on some 64-bit architectures
+
+Revision 1.125 2003/11/22 00:18:27 cheshire
+Add compile-time asserts to verify correct sizes of mDNSu32, mDNSOpaque16, etc.
+
+Revision 1.124 2003/11/20 22:59:54 cheshire
+Changed runtime checks in mDNS.c to be compile-time checks in mDNSEmbeddedAPI.h
+Thanks to Bob Bradley for suggesting the ingenious compiler trick to make this work.
+
+Revision 1.123 2003/11/20 22:53:01 cheshire
+Add comment about MAX_ESCAPED_DOMAIN_LABEL
+
+Revision 1.122 2003/11/20 20:49:53 cheshire
+Another fix from HP: Use packedstruct macro to ensure proper packing for on-the-wire packet structures
+
+Revision 1.121 2003/11/20 05:01:38 cheshire
+Update comments; add explanation of Advertise/DontAdvertiseLocalAddresses
+
+Revision 1.120 2003/11/14 20:59:08 cheshire
+Clients can't use AssignDomainName macro because mDNSPlatformMemCopy is defined in mDNSPlatformFunctions.h.
+Best solution is just to combine mDNSEmbeddedAPI.h and mDNSPlatformFunctions.h into a single file.
+
+Revision 1.119 2003/11/14 19:47:52 cheshire
+Define symbol MAX_ESCAPED_DOMAIN_NAME to indicate recommended buffer size for ConvertDomainNameToCString
+
+Revision 1.118 2003/11/14 19:18:34 cheshire
+Move AssignDomainName macro to mDNSEmbeddedAPI.h to that client layers can use it too
+
+Revision 1.117 2003/11/08 23:32:24 cheshire
+Gave name to anonymous struct, to avoid errors on certain compilers.
+(Thanks to ramaprasad.kr@hp.com for reporting this.)
+
+Revision 1.116 2003/11/07 03:32:56 cheshire
+<rdar://problem/3472153> mDNSResponder delivers answers in inconsistent order
+This is the real fix. Checkin 1.312 was overly simplistic; Calling GetFreeCacheRR() can sometimes
+purge records from the cache, causing tail pointer *rp to be stale on return. The correct fix is
+to maintain a system-wide tail pointer for each cache slot, and then if neccesary GetFreeCacheRR()
+can update this pointer, so that mDNSCoreReceiveResponse() appends records in the right place.
+
+Revision 1.115 2003/09/23 00:53:54 cheshire
+NumFailedProbes should be unsigned
+
+Revision 1.114 2003/08/29 19:44:15 cheshire
+<rdar://problem/3400967> Traffic reduction: Eliminate synchronized QUs when a new service appears
+1. Use m->RandomQueryDelay to impose a random delay in the range 0-500ms on queries
+ that already have at least one unique answer in the cache
+2. For these queries, go straight to QM, skipping QU
+
+Revision 1.113 2003/08/21 19:31:58 cheshire
+Cosmetic: Swap order of fields
+
+Revision 1.112 2003/08/21 19:27:36 cheshire
+<rdar://problem/3387878> Traffic reduction: No need to announce record for longer than TTL
+
+Revision 1.111 2003/08/21 02:21:50 cheshire
+<rdar://problem/3386473> Efficiency: Reduce repeated queries
+
+Revision 1.110 2003/08/20 23:39:31 cheshire
+<rdar://problem/3344098> Review syslog messages, and remove as appropriate
+
+Revision 1.109 2003/08/19 22:24:10 cheshire
+Comment change
+
+Revision 1.108 2003/08/19 22:20:00 cheshire
+<rdar://problem/3376721> Don't use IPv6 on interfaces that have a routable IPv4 address configured
+More minor refinements
+
+Revision 1.107 2003/08/19 06:48:25 cheshire
+<rdar://problem/3376552> Guard against excessive record updates
+Each record starts with 10 UpdateCredits.
+Every update consumes one UpdateCredit.
+UpdateCredits are replenished at a rate of one one per minute, up to a maximum of 10.
+As the number of UpdateCredits declines, the number of announcements is similarly scaled back.
+When fewer than 5 UpdateCredits remain, the first announcement is also delayed by an increasing amount.
+
+Revision 1.106 2003/08/19 04:49:28 cheshire
+<rdar://problem/3368159> Interaction between v4, v6 and dual-stack hosts not working quite right
+1. A dual-stack host should only suppress its own query if it sees the same query from other hosts on BOTH IPv4 and IPv6.
+2. When we see the first v4 (or first v6) member of a group, we re-trigger questions and probes on that interface.
+3. When we see the last v4 (or v6) member of a group go away, we revalidate all the records received on that interface.
+
+Revision 1.105 2003/08/19 02:33:37 cheshire
+Update comments
+
+Revision 1.104 2003/08/19 02:31:11 cheshire
+<rdar://problem/3378386> mDNSResponder overenthusiastic with final expiration queries
+Final expiration queries now only mark the question for sending on the particular interface
+pertaining to the record that's expiring.
+
+Revision 1.103 2003/08/18 19:05:44 cheshire
+<rdar://problem/3382423> UpdateRecord not working right
+Added "newrdlength" field to hold new length of updated rdata
+
+Revision 1.102 2003/08/16 03:39:00 cheshire
+<rdar://problem/3338440> InterfaceID -1 indicates "local only"
+
+Revision 1.101 2003/08/15 20:16:02 cheshire
+<rdar://problem/3366590> mDNSResponder takes too much RPRVT
+We want to avoid touching the rdata pages, so we don't page them in.
+1. RDLength was stored with the rdata, which meant touching the page just to find the length.
+ Moved this from the RData to the ResourceRecord object.
+2. To avoid unnecessarily touching the rdata just to compare it,
+ compute a hash of the rdata and store the hash in the ResourceRecord object.
+
+Revision 1.100 2003/08/14 19:29:04 cheshire
+<rdar://problem/3378473> Include cache records in SIGINFO output
+Moved declarations of DNSTypeName() and GetRRDisplayString to mDNSEmbeddedAPI.h so daemon.c can use them
+
+Revision 1.99 2003/08/14 02:17:05 cheshire
+<rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord
+
+Revision 1.98 2003/08/12 19:56:23 cheshire
+Update to APSL 2.0
+
+Revision 1.97 2003/08/12 14:59:27 cheshire
+<rdar://problem/3374490> Rate-limiting blocks some legitimate responses
+When setting LastMCTime also record LastMCInterface. When checking LastMCTime to determine
+whether to suppress the response, also check LastMCInterface to see if it matches.
+
+Revision 1.96 2003/08/12 13:57:04 cheshire
+<rdar://problem/3323817> Improve cache performance
+Changed the number of hash table slots from 37 to 499
+
+Revision 1.95 2003/08/09 00:55:02 cheshire
+<rdar://problem/3366553> mDNSResponder is taking 20-30% of the CPU
+Don't scan the whole cache after every packet.
+
+Revision 1.94 2003/08/09 00:35:29 cheshire
+
+Revision 1.93 2003/08/08 18:55:48 cheshire
+<rdar://problem/3370365> Guard against time going backwards
+
+Revision 1.92 2003/08/08 18:36:04 cheshire
+<rdar://problem/3344154> Only need to revalidate on interface removal on platforms that have the PhantomInterfaces bug
+
+Revision 1.91 2003/08/06 21:33:39 cheshire
+Fix compiler warnings on PocketPC 2003 (Windows CE)
+
+Revision 1.90 2003/08/06 20:30:17 cheshire
+Add structure definition for rdataMX (not currently used, but good to have it for completeness)
+
+Revision 1.89 2003/08/06 18:58:19 cheshire
+Update comments
+
+Revision 1.88 2003/07/24 23:45:44 cheshire
+To eliminate compiler warnings, changed definition of mDNSBool from
+"unsigned char" to "int", since "int" is in fact truly the type that C uses
+for the result of comparison operators (a<b) and logical operators (a||b)
+
+Revision 1.87 2003/07/22 23:57:20 cheshire
+Move platform-layer function prototypes from mDNSEmbeddedAPI.h to mDNSPlatformFunctions.h where they belong
+
+Revision 1.86 2003/07/20 03:52:02 ksekar
+<rdar://problem/3320722>: Feature: New DNS-SD APIs (#7875) (mDNSResponder component)
+Added error type for incompatibility between daemon and client versions
+
+Revision 1.85 2003/07/19 03:23:13 cheshire
+<rdar://problem/2986147> mDNSResponder needs to receive and cache larger records
+
+Revision 1.84 2003/07/18 23:52:12 cheshire
+To improve consistency of field naming, global search-and-replace:
+NextProbeTime -> NextScheduledProbe
+NextResponseTime -> NextScheduledResponse
+
+Revision 1.83 2003/07/18 00:29:59 cheshire
+<rdar://problem/3268878> Remove mDNSResponder version from packet header and use HINFO record instead
+
+Revision 1.82 2003/07/17 17:35:04 cheshire
+<rdar://problem/3325583> Rate-limit responses, to guard against packet flooding
+
+Revision 1.81 2003/07/16 05:01:36 cheshire
+Add fields 'LargeAnswers' and 'ExpectUnicastResponse' in preparation for
+<rdar://problem/3315761> Need to implement "unicast response" request, using top bit of qclass
+
+Revision 1.80 2003/07/15 01:55:12 cheshire
+<rdar://problem/3315777> Need to implement service registration with subtypes
+
+Revision 1.79 2003/07/13 02:28:00 cheshire
+<rdar://problem/3325166> SendResponses didn't all its responses
+Delete all references to RRInterfaceActive -- it's now superfluous
+
+Revision 1.78 2003/07/13 01:47:53 cheshire
+Fix one error and one warning in the Windows build
+
+Revision 1.77 2003/07/11 01:32:38 cheshire
+Syntactic cleanup (no change to funcationality): Now that we only have one host name,
+rename field "hostname1" to "hostname", and field "RR_A1" to "RR_A".
+
+Revision 1.76 2003/07/11 01:28:00 cheshire
+<rdar://problem/3161289> No more local.arpa
+
+Revision 1.75 2003/07/02 21:19:45 cheshire
+<rdar://problem/3313413> Update copyright notices, etc., in source code comments
+
+Revision 1.74 2003/07/02 02:41:23 cheshire
+<rdar://problem/2986146> mDNSResponder needs to start with a smaller cache and then grow it as needed
+
+Revision 1.73 2003/06/10 04:24:39 cheshire
+<rdar://problem/3283637> React when we observe other people query unsuccessfully for a record that's in our cache
+Some additional refinements:
+Don't try to do this for unicast-response queries
+better tracking of Qs and KAs in multi-packet KA lists
+
+Revision 1.72 2003/06/10 01:46:27 cheshire
+Add better comments explaining how these data structures are intended to be used from the client layer
+
+Revision 1.71 2003/06/07 06:45:05 cheshire
+<rdar://problem/3283666> No need for multiple machines to all be sending the same queries
+
+Revision 1.70 2003/06/07 04:50:53 cheshire
+<rdar://problem/3283637> React when we observe other people query unsuccessfully for a record that's in our cache
+
+Revision 1.69 2003/06/07 04:22:17 cheshire
+Add MsgBuffer for error log and debug messages
+
+Revision 1.68 2003/06/07 01:46:38 cheshire
+<rdar://problem/3283540> When query produces zero results, call mDNS_Reconfirm() on any antecedent records
+
+Revision 1.67 2003/06/07 01:22:14 cheshire
+<rdar://problem/3283516> mDNSResponder needs an mDNS_Reconfirm() function
+
+Revision 1.66 2003/06/07 00:59:43 cheshire
+<rdar://problem/3283454> Need some randomness to spread queries on the network
+
+Revision 1.65 2003/06/06 21:41:11 cheshire
+For consistency, mDNS_StopQuery() should return an mStatus result, just like all the other mDNSCore routines
+
+Revision 1.64 2003/06/06 21:38:55 cheshire
+Renamed 'NewData' as 'FreshData' (The data may not be new data, just a refresh of data that we
+already had in our cache. This refreshes our TTL on the data, but the data itself stays the same.)
+
+Revision 1.63 2003/06/06 17:20:14 cheshire
+For clarity, rename question fields name/rrtype/rrclass as qname/qtype/qclass
+(Global search-and-replace; no functional change to code execution.)
+
+Revision 1.62 2003/06/04 01:25:33 cheshire
+<rdar://problem/3274950> Cannot perform multi-packet known-answer suppression messages
+Display time interval between first and subsequent queries
+
+Revision 1.61 2003/06/03 05:02:16 cheshire
+<rdar://problem/3277080> Duplicate registrations not handled as efficiently as they should be
+
+Revision 1.60 2003/05/31 00:09:49 cheshire
+<rdar://problem/3274862> Add ability to discover what services are on a network
+
+Revision 1.59 2003/05/29 06:11:35 cheshire
+<rdar://problem/3272214>: Report if there appear to be too many "Resolve" callbacks
+
+Revision 1.58 2003/05/29 05:48:06 cheshire
+Minor fix for when generating printf warnings: mDNS_snprintf arguments are now 3,4
+
+Revision 1.57 2003/05/26 03:21:27 cheshire
+Tidy up address structure naming:
+mDNSIPAddr => mDNSv4Addr (for consistency with mDNSv6Addr)
+mDNSAddr.addr.ipv4 => mDNSAddr.ip.v4
+mDNSAddr.addr.ipv6 => mDNSAddr.ip.v6
+
+Revision 1.56 2003/05/26 03:01:27 cheshire
+<rdar://problem/3268904> sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead
+
+Revision 1.55 2003/05/26 00:47:30 cheshire
+Comment clarification
+
+Revision 1.54 2003/05/24 16:39:48 cheshire
+<rdar://problem/3268631> SendResponses also needs to handle multihoming better
+
+Revision 1.53 2003/05/23 02:15:37 cheshire
+Fixed misleading use of the term "duplicate suppression" where it should have
+said "known answer suppression". (Duplicate answer suppression is something
+different, and duplicate question suppression is yet another thing, so the use
+of the completely vague term "duplicate suppression" was particularly bad.)
+
+Revision 1.52 2003/05/22 02:29:22 cheshire
+<rdar://problem/2984918> SendQueries needs to handle multihoming better
+Complete rewrite of SendQueries. Works much better now :-)
+
+Revision 1.51 2003/05/21 20:14:55 cheshire
+Fix comments and warnings
+
+Revision 1.50 2003/05/14 07:08:36 cheshire
+<rdar://problem/3159272> mDNSResponder should be smarter about reconfigurations
+Previously, when there was any network configuration change, mDNSResponder
+would tear down the entire list of active interfaces and start again.
+That was very disruptive, and caused the entire cache to be flushed,
+and caused lots of extra network traffic. Now it only removes interfaces
+that have really gone, and only adds new ones that weren't there before.
+
+Revision 1.49 2003/05/07 01:49:36 cheshire
+Remove "const" in ConstructServiceName prototype
+
+Revision 1.48 2003/05/07 00:18:44 cheshire
+Fix typo: "kDNSQClass_Mask" should be "kDNSClass_Mask"
+
+Revision 1.47 2003/05/06 00:00:46 cheshire
+<rdar://problem/3248914> Rationalize naming of domainname manipulation functions
+
+Revision 1.46 2003/04/30 20:39:09 cheshire
+Add comment
+
+Revision 1.45 2003/04/29 00:40:50 cheshire
+Fix compiler warnings
+
+Revision 1.44 2003/04/26 02:41:56 cheshire
+<rdar://problem/3241281> Change timenow from a local variable to a structure member
+
+Revision 1.43 2003/04/25 01:45:56 cheshire
+<rdar://problem/3240002> mDNS_RegisterNoSuchService needs to include a host name
+
+Revision 1.42 2003/04/15 20:58:31 jgraessl
+
+<rdar://problem/3229014> Added a hash to lookup records in the cache.
+
+Revision 1.41 2003/04/15 18:09:13 jgraessl
+
+<rdar://problem/3228892>
+Reviewed by: Stuart Cheshire
+Added code to keep track of when the next cache item will expire so we can
+call TidyRRCache only when necessary.
+
+Revision 1.40 2003/03/29 01:55:19 cheshire
+<rdar://problem/3212360> mDNSResponder sometimes suffers false self-conflicts when it sees its own packets
+Solution: Major cleanup of packet timing and conflict handling rules
+
+Revision 1.39 2003/03/27 03:30:55 cheshire
+<rdar://problem/3210018> Name conflicts not handled properly, resulting in memory corruption, and eventual crash
+Problem was that HostNameCallback() was calling mDNS_DeregisterInterface(), which is not safe in a callback
+Fixes:
+1. Make mDNS_DeregisterInterface() safe to call from a callback
+2. Make HostNameCallback() use mDNS_DeadvertiseInterface() instead
+ (it never really needed to deregister the interface at all)
+
+Revision 1.38 2003/03/15 04:40:36 cheshire
+Change type called "mDNSOpaqueID" to the more descriptive name "mDNSInterfaceID"
+
+Revision 1.37 2003/03/14 21:34:11 cheshire
+<rdar://problem/3176950> Can't setup and print to Lexmark PS printers via Airport Extreme
+Increase size of cache rdata from 512 to 768
+
+Revision 1.36 2003/03/05 03:38:35 cheshire
+<rdar://problem/3185731> Bogus error message in console: died or deallocated, but no record of client can be found!
+Fixed by leaving client in list after conflict, until client explicitly deallocates
+
+Revision 1.35 2003/02/21 02:47:54 cheshire
+<rdar://problem/3099194> mDNSResponder needs performance improvements
+Several places in the code were calling CacheRRActive(), which searched the entire
+question list every time, to see if this cache resource record answers any question.
+Instead, we now have a field "CRActiveQuestion" in the resource record structure
+
+Revision 1.34 2003/02/21 01:54:08 cheshire
+<rdar://problem/3099194> mDNSResponder needs performance improvements
+Switched to using new "mDNS_Execute" model (see "Implementer Notes.txt")
+
+Revision 1.33 2003/02/20 06:48:32 cheshire
+<rdar://problem/3169535> Xserve RAID needs to do interface-specific registrations
+Reviewed by: Josh Graessley, Bob Bradley
+
+Revision 1.32 2003/01/31 03:35:59 cheshire
+<rdar://problem/3147097> mDNSResponder sometimes fails to find the correct results
+When there were *two* active questions in the list, they were incorrectly
+finding *each other* and *both* being marked as duplicates of another question
+
+Revision 1.31 2003/01/29 02:46:37 cheshire
+Fix for IPv6:
+A physical interface is identified solely by its InterfaceID (not by IP and type).
+On a given InterfaceID, mDNSCore may send both v4 and v6 multicasts.
+In cases where the requested outbound protocol (v4 or v6) is not supported on
+that InterfaceID, the platform support layer should simply discard that packet.
+
+Revision 1.30 2003/01/29 01:47:08 cheshire
+Rename 'Active' to 'CRActive' or 'InterfaceActive' for improved clarity
+
+Revision 1.29 2003/01/28 05:23:43 cheshire
+<rdar://problem/3147097> mDNSResponder sometimes fails to find the correct results
+Add 'Active' flag for interfaces
+
+Revision 1.28 2003/01/28 01:35:56 cheshire
+Revise comment about ThisQInterval to reflect new semantics
+
+Revision 1.27 2003/01/13 23:49:42 jgraessl
+Merged changes for the following fixes in to top of tree:
+<rdar://problem/3086540> computer name changes not handled properly
+<rdar://problem/3124348> service name changes are not properly handled
+<rdar://problem/3124352> announcements sent in pairs, failing chattiness test
+
+Revision 1.26 2002/12/23 22:13:28 jgraessl
+
+Reviewed by: Stuart Cheshire
+Initial IPv6 support for mDNSResponder.
+
+Revision 1.25 2002/09/21 20:44:49 zarzycki
+Added APSL info
+
+Revision 1.24 2002/09/19 23:47:35 cheshire
+Added mDNS_RegisterNoSuchService() function for assertion of non-existence
+of a particular named service
+
+Revision 1.23 2002/09/19 21:25:34 cheshire
+mDNS_snprintf() doesn't need to be in a separate file
+
+Revision 1.22 2002/09/19 04:20:43 cheshire
+Remove high-ascii characters that confuse some systems
+
+Revision 1.21 2002/09/17 01:06:35 cheshire
+Change mDNS_AdvertiseLocalAddresses to be a parameter to mDNS_Init()
+
+Revision 1.20 2002/09/16 18:41:41 cheshire
+Merge in license terms from Quinn's copy, in preparation for Darwin release
+
+*/
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifndef __mDNSClientAPI_h
+#define __mDNSClientAPI_h
+
+#if defined(EFI32) || defined(EFI64)
+// EFI doesn't have stdarg.h
+#include "Tiano.h"
+#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)
+#else
+#include <stdarg.h> // stdarg.h is required for for va_list support for the mDNS_vsnprintf declaration
+#endif
+
+#include "mDNSDebug.h"
+
+#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_AAAA = 28, // 28 IPv6 address
+ kDNSType_SRV = 33, // 33 Service record
+ kDNSType_OPT = 41, // EDNS0 OPT record
+ kDNSType_TSIG = 250, // 250 Transaction Signature
+
+ kDNSQType_ANY = 255 // Not a DNS type, but a DNS query type, meaning "all types"
+ } DNS_TypeValues;
+
+// ***************************************************************************
+#if 0
+#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.
+typedef packedunion { mDNSu8 b[ 2]; mDNSu16 NotAnInteger; } mDNSOpaque16;
+typedef packedunion { mDNSu8 b[ 4]; mDNSu32 NotAnInteger; } mDNSOpaque32;
+typedef packedunion { mDNSu8 b[ 6]; mDNSu16 w[3]; mDNSu32 l[1]; } mDNSOpaque48;
+typedef packedunion { mDNSu8 b[16]; mDNSu16 w[8]; mDNSu32 l[4]; } mDNSOpaque128;
+
+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)
+
+enum
+ {
+ mDNSAddrType_None = 0,
+ mDNSAddrType_IPv4 = 4,
+ mDNSAddrType_IPv6 = 6,
+ mDNSAddrType_Unknown = ~0 // Special marker value used in known answer list recording
+ };
+
+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
+ // -65563 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 specify that a domain name, including length bytes, data bytes, and terminating zero, may be up to 255 bytes long
+#define MAX_DOMAIN_NAME 255
+typedef struct { mDNSu8 c[256]; } domainname; // Up to 255 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 1005 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 255 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, 61 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 + 61*4+1 + 1 = 1005.
+// 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 1005
+
+// 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.
+// Wide-area service discovery records have a very short TTL to avoid poluting intermediate caches with
+// dynamic records. When discovered via Long Lived Queries (with change notifications), resource record
+// TTLs can be safely ignored.
+
+#define kStandardTTL (3600UL * 100 / 80)
+#define kHostNameTTL 120UL
+#define kWideAreaTTL 3
+
+#define DefaultTTLforRRType(X) (((X) == kDNSType_A || (X) == kDNSType_AAAA || (X) == kDNSType_SRV) ? kHostNameTTL : kStandardTTL)
+
+// ***************************************************************************
+#if 0
+#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;
+
+// ***************************************************************************
+#if 0
+#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 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),
+ kDNSRecordTypeActiveMask = (kDNSRecordTypeAdvisory | kDNSRecordTypeShared | kDNSRecordTypeVerified | kDNSRecordTypeKnownUnique),
+
+ 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
+
+ kDNSRecordTypePacketAnsMask = 0x40, // True for PacketAns and PacketAnsUnique
+ kDNSRecordTypePacketUniqueMask = 0x10 // True for PacketAddUnique, PacketAnsUnique, PacketAuthUnique
+ };
+
+typedef packedstruct { mDNSu16 priority; mDNSu16 weight; mDNSIPPort port; domainname target; } rdataSRV;
+typedef packedstruct { mDNSu16 preference; domainname exchange; } rdataMX;
+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;
+
+typedef packedstruct
+ {
+ mDNSu16 vers;
+ mDNSu16 llqOp;
+ mDNSu16 err;
+ mDNSu8 id[8];
+ mDNSu32 lease;
+ } LLQOptData;
+
+#define LLQ_OPTLEN ((3 * sizeof(mDNSu16)) + 8 + sizeof(mDNSu32))
+// Windows adds pad bytes to sizeof(LLQOptData). Use this macro when setting length fields or validating option rdata from
+// off the wire. Use sizeof(LLQOptData) when dealing with structures (e.g. memcpy). Never memcpy between on-the-wire
+// representation and a structure
+
+// 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 lease; } OptData;
+ } rdataOpt;
+
+// StandardAuthRDSize is 264 (256+8), which is large enough to hold a maximum-sized SRV record
+// 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
+
+#define InlineCacheGroupNameSize 144
+
+typedef union
+ {
+ mDNSu8 data[StandardAuthRDSize];
+ mDNSv4Addr ipv4; // For 'A' record
+ mDNSv6Addr ipv6; // For 'AAAA' record
+ domainname name; // For PTR, NS, and CNAME records
+ UTF8str255 txt; // For TXT record
+ rdataSRV srv; // For SRV record
+ rdataMX mx; // For MX record
+ rdataSOA soa; // For SOA record
+ rdataOpt opt; // For eDNS0 opt record
+ } RDataBody;
+
+typedef struct
+ {
+ mDNSu16 MaxRDLength; // Amount of storage allocated for rdata (usually sizeof(RDataBody))
+ RDataBody u;
+ } RData;
+#define sizeofRDataHeader (sizeof(RData) - sizeof(RDataBody))
+
+typedef struct AuthRecord_struct AuthRecord;
+typedef struct CacheRecord_struct CacheRecord;
+typedef struct CacheGroup_struct CacheGroup;
+typedef struct DNSQuestion_struct DNSQuestion;
+typedef struct mDNS_struct mDNS;
+typedef struct mDNS_PlatformSupport_struct mDNS_PlatformSupport;
+typedef struct NATTraversalInfo_struct NATTraversalInfo;
+
+// Note: Within an mDNSRecordCallback mDNS all API calls are legal except mDNS_Init(), mDNS_Close(), 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);
+
+typedef struct
+ {
+ mDNSu8 RecordType; // See enum above
+ 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)
+ domainname *name;
+ mDNSu16 rrtype;
+ mDNSu16 rrclass;
+ mDNSu32 rroriginalttl; // In seconds
+ mDNSu16 rdlength; // Size of the raw rdata, in bytes
+ mDNSu16 rdestimate; // Upper bound on 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.
+ RData *rdata; // Pointer to storage for this rdata
+ } ResourceRecord;
+
+// Unless otherwise noted, states may apply to either independent record registrations or service registrations
+typedef enum
+ {
+ regState_FetchingZoneData = 1, // getting info - update not sent
+ regState_Pending = 2, // update sent, reply not received
+ regState_Registered = 3, // update sent, reply received
+ regState_DeregPending = 4, // dereg sent, reply not received
+ regState_DeregDeferred = 5, // dereg requested while in Pending state - send dereg AFTER registration is confirmed
+ regState_Cancelled = 6, // update not sent, reg. cancelled by client
+ regState_Unregistered = 8, // not in any list
+ regState_Refresh = 9, // outstanding refresh (or target change) message
+ regState_NATMap = 10, // establishing NAT port mapping or learning public address
+ regState_UpdatePending = 11, // update in flight as result of mDNS_Update call
+ regState_NoTarget = 12, // service registration pending registration of hostname (ServiceRegistrations only)
+ regState_ExtraQueued = 13, // extra record to be registered upon completion of service registration (RecordRegistrations only)
+ regState_NATError = 14 // unable to complete NAT traversal
+ } regState_t;
+
+// context for both ServiceRecordSet and individual AuthRec structs
+typedef struct
+ {
+ // registration/lease state
+ regState_t state;
+ mDNSBool lease; // dynamic update contains (should contain) lease option
+ mDNSs32 expire; // expiration of lease (-1 for static)
+ mDNSBool TestForSelfConflict; // on name conflict, check if we're just seeing our own orphaned records
+
+ // identifier to match update request and response
+ mDNSOpaque16 id;
+
+ // server info
+ domainname zone; // the zone that is updated
+ mDNSAddr ns; // primary name server for the record's zone !!!KRS not technically correct to cache longer than TTL
+ mDNSIPPort port; // port on which server accepts dynamic updates
+
+ // NAT traversal context
+ NATTraversalInfo *NATinfo; // may be NULL
+
+ // state for deferred operations
+ mDNSBool ClientCallbackDeferred; // invoke client callback on completion of pending operation(s)
+ mStatus DeferredStatus; // status to deliver when above flag is set
+ mDNSBool SRVUpdateDeferred; // do we need to change target or port once current operation completes?
+ mDNSBool SRVChanged; // temporarily deregistered service because its SRV target or port changed
+
+ // uDNS_UpdateRecord support fields
+ RData *OrigRData; mDNSu16 OrigRDLen; // previously registered, being deleted
+ RData *InFlightRData; mDNSu16 InFlightRDLen; // currently being registered
+ RData *QueuedRData; mDNSu16 QueuedRDLen; // if the client call Update while an update is in flight, we must finish the
+ // pending operation (re-transmitting if necessary) THEN register the queued update
+ mDNSRecordUpdateCallback *UpdateRDCallback; // client callback to free old rdata
+ } uDNS_RegInfo;
+
+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;
+ uDNS_RegInfo uDNS_info;
+
+ // Field Group 2: Persistent metadata for Authoritative Records
+ AuthRecord *Additional1; // Recommended additional record to include in response
+ AuthRecord *Additional2; // Another additional
+ 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 HostTarget; // 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
+
+ // 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 LocalAnswer; // Set if this RR has been delivered to LocalOnly questions
+ mDNSu8 IncludeInProbe; // Set if this RR is being put into a probe right now
+ mDNSInterfaceID ImmedAnswer; // Someone on this interface issued a query we need to answer (all-ones for all interfaces)
+ mDNSu8 ImmedUnicast; // Set if we may send our response directly via unicast to the requester
+#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 AnnounceUntil; // In platform time units: Creation time + TTL
+ 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
+
+ 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
+ };
+
+// 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 struct in field 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
+ mDNSu8 namestorage[InlineCacheGroupNameSize];
+ };
+
+struct CacheRecord_struct
+ {
+ CacheRecord *next; // Next in list; first element of structure for efficiency reasons
+ ResourceRecord resrec;
+
+ // 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
+ 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
+ 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
+ CacheRecord *NextInCFList; // Set if this is in the list of records we just received with the cache flush bit set
+
+ struct { mDNSu16 MaxRDLength; mDNSu8 data[InlineCacheRDSize]; } rdatastorage; // Storage for small records is right here
+ };
+
+// Storage sufficient to hold either a CacheGroup header or a CacheRecord
+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 uDNS_HostnameInfo
+ {
+ struct uDNS_HostnameInfo *next;
+ 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
+ } uDNS_HostnameInfo;
+
+enum
+ {
+ DNSServer_Untested = 0,
+ DNSServer_Failed = 1,
+ DNSServer_Passed = 2
+ };
+
+typedef struct DNSServer
+ {
+ struct DNSServer *next;
+ mDNSAddr addr;
+ mDNSBool del; // Set when we're planning to delete this from the list
+ mDNSu32 teststate; // Have we sent bug-detection query to this server?
+ domainname domain; // name->server matching for "split dns"
+ } DNSServer;
+
+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;
+
+ mDNSBool InterfaceActive; // Set if interface is sending & receiving packets (see comment above)
+ mDNSBool IPv4Available; // If InterfaceActive, set if v4 available on this InterfaceID
+ mDNSBool IPv6Available; // If InterfaceActive, set if v6 available on this InterfaceID
+
+ // 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;
+ char ifname[64]; // Windows uses a GUID string for the interface name, which doesn't fit in 16 bytes
+ mDNSBool Advertise; // False if you are only searching on this interface
+ mDNSBool McastTxRx; // Send/Receive multicast on this { InterfaceID, address family } ?
+ };
+
+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_Close(), mDNS_Execute()
+typedef struct ServiceRecordSet_struct ServiceRecordSet;
+typedef void mDNSServiceCallback(mDNS *const m, ServiceRecordSet *const sr, mStatus result);
+struct ServiceRecordSet_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_RegisterService();
+ // all required data is passed as parameters to that function.
+ ServiceRecordSet *next;
+ uDNS_RegInfo uDNS_info;
+ mDNSServiceCallback *ServiceCallback;
+ void *ServiceContext;
+ ExtraResourceRecord *Extras; // Optional list of extra AuthRecords attached to this service registration
+ mDNSu32 NumSubTypes;
+ AuthRecord *SubTypes;
+ mDNSBool Conflict; // Set if this record set was forcibly deregistered because of a conflict
+ domainname Host; // Set if this service record does not use the standard target host name
+ 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 - 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
+ {
+ // Setup states
+ LLQ_UnInit = 0,
+ LLQ_GetZoneInfo = 1,
+ LLQ_InitialRequest = 2,
+ LLQ_SecondaryRequest = 3,
+ LLQ_Refresh = 4,
+ LLQ_Retry = 5,
+ LLQ_Established = 6,
+ LLQ_Suspended = 7,
+ LLQ_SuspendDeferred = 8, // suspend once we get zone info
+ LLQ_SuspendedPoll = 9, // suspended from polling state
+ LLQ_NatMapWait = 10,
+
+ // Established/error states
+ LLQ_Static = 16,
+ LLQ_Poll = 17,
+ LLQ_Error = 18,
+ LLQ_Cancelled = 19
+ } LLQ_State;
+
+typedef struct
+ {
+ LLQ_State state;
+ mDNSAddr servAddr;
+ mDNSIPPort servPort;
+ DNSQuestion *question;
+ mDNSu32 origLease; // seconds (relative)
+ mDNSs32 retry; // ticks (absolute)
+ mDNSs32 expire; // ticks (absolute)
+ mDNSs16 ntries;
+ mDNSu8 id[8];
+ mDNSBool deriveRemovesOnResume;
+ mDNSBool NATMap; // does this LLQ use the global LLQ NAT mapping?
+ } LLQ_Info;
+
+// LLQ constants
+#define kDNSOpt_LLQ 1
+#define kDNSOpt_Lease 2
+#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
+#define kLLQ_DEF_RETRY 1800 // retry a failed operation after 30 minutes
+// LLQ Operation Codes
+#define kLLQOp_Setup 1
+#define kLLQOp_Refresh 2
+#define kLLQOp_Event 3
+
+#define LLQ_OPT_RDLEN ((2 * sizeof(mDNSu16)) + LLQ_OPTLEN)
+#define LEASE_OPT_RDLEN (2 * sizeof(mDNSu16)) + sizeof(mDNSs32)
+
+// 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
+ };
+
+typedef void (*InternalResponseHndlr)(mDNS *const m, DNSMessage *msg, const mDNSu8 *end, DNSQuestion *question, void *internalContext);
+typedef struct
+ {
+ mDNSOpaque16 id;
+ mDNSBool internal;
+ InternalResponseHndlr responseCallback; // NULL if internal field is false
+ LLQ_Info *llq; // NULL for 1-shot queries
+ mDNSBool Answered; // have we received an answer (including NXDOMAIN) for this question?
+ CacheRecord *knownAnswers;
+ mDNSs32 RestartTime; // Mark when we restart a suspended query
+ void *context;
+ } uDNS_QuestionInfo;
+
+// Note: Within an mDNSQuestionCallback mDNS all API calls are legal except mDNS_Init(), mDNS_Close(), mDNS_Execute()
+typedef void mDNSQuestionCallback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord);
+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 that's been removed from the list
+ 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 FlappingInterface;// Set when an interface goes away, to flag if removes are delivered for this Q
+ 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
+ uDNS_QuestionInfo uDNS_info;
+
+ // 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 ReturnCNAME; // Set by client to request callbacks for intermediate CNAME records
+ 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_Close(), 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;
+ };
+
+// ***************************************************************************
+#if 0
+#pragma mark - NAT Traversal structures and constants
+#endif
+
+#define NATMAP_INIT_RETRY (mDNSPlatformOneSecond / 4) // start at 250ms w/ exponential decay
+#define NATMAP_MAX_RETRY mDNSPlatformOneSecond // back off to once per second
+#define NATMAP_MAX_TRIES 3 // for max 3 tries
+#define NATMAP_DEFAULT_LEASE (60 * 60) // lease life in seconds
+#define NATMAP_VERS 0
+#define NATMAP_RESPONSE_MASK 0x80
+
+typedef enum
+ {
+ NATOp_AddrRequest = 0,
+ NATOp_MapUDP = 1,
+ NATOp_MapTCP = 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 enum
+ {
+ NATState_Init = 0,
+ NATState_Request = 1,
+ NATState_Established = 2,
+ NATState_Legacy = 3,
+ NATState_Error = 4,
+ NATState_Refresh = 5,
+ NATState_Deleted = 6
+ } NATState_t;
+// Note: we have no explicit "cancelled" state, where a service/interface is deregistered while we
+ // have an outstanding NAT request. This is conveyed by the "reg" pointer being set to NULL
+
+typedef packedstruct
+ {
+ mDNSu8 vers;
+ mDNSu8 opcode;
+ } NATAddrRequest;
+
+typedef packedstruct
+ {
+ mDNSu8 vers;
+ mDNSu8 opcode;
+ mDNSOpaque16 err;
+ mDNSOpaque32 uptime;
+ mDNSv4Addr PubAddr;
+ } NATAddrReply;
+
+typedef packedstruct
+ {
+ mDNSu8 vers;
+ mDNSu8 opcode;
+ mDNSOpaque16 unused;
+ mDNSIPPort priv;
+ mDNSIPPort pub;
+ mDNSOpaque32 lease;
+ } NATPortMapRequest;
+
+typedef packedstruct
+ {
+ mDNSu8 vers;
+ mDNSu8 opcode;
+ mDNSOpaque16 err;
+ mDNSOpaque32 uptime;
+ mDNSIPPort priv;
+ mDNSIPPort pub;
+ mDNSOpaque32 lease;
+ } NATPortMapReply;
+
+// Pass NULL for pkt on error (including timeout)
+typedef mDNSBool (*NATResponseHndlr)(NATTraversalInfo *n, mDNS *m, mDNSu8 *pkt, mDNSu16 len);
+
+struct NATTraversalInfo_struct
+ {
+ NATOp_t op;
+ NATResponseHndlr ReceiveResponse;
+ union { AuthRecord *RecordRegistration; ServiceRecordSet *ServiceRegistration; } reg;
+ mDNSAddr Router;
+ mDNSIPPort PublicPort;
+ union { NATAddrRequest AddrReq; NATPortMapRequest PortReq; } request;
+ mDNSs32 retry; // absolute time when we retry
+ mDNSs32 RetryInterval; // delta between time sent and retry
+ int ntries;
+ NATState_t state;
+ NATTraversalInfo *next;
+ };
+
+// ***************************************************************************
+#if 0
+#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
+ {
+ mDNS_KnownBug_PhantomInterfaces = 1
+ };
+
+typedef struct
+ {
+ mDNSs32 nextevent;
+ DNSQuestion *ActiveQueries; //!!!KRS this should be a hashtable (hash on messageID)
+ DNSQuestion *CurrentQuery; // pointer to ActiveQueries list being examined in a loop. Functions that remove
+ // elements from the ActiveQueries list must update this pointer (if non-NULL) as necessary.
+ //!!!KRS do the same for registration lists
+ ServiceRecordSet *ServiceRegistrations;
+ AuthRecord *RecordRegistrations;
+ NATTraversalInfo *NATTraversals;
+ mDNSu16 NextMessageID;
+ DNSServer *Servers; // list of DNS servers
+ mDNSAddr Router;
+ mDNSAddr AdvertisedV4; // IPv4 address pointed to by hostname
+ mDNSAddr MappedV4; // Cache of public address if PrimaryIP is behind a NAT
+ mDNSAddr AdvertisedV6; // IPv6 address pointed to by hostname
+ NATTraversalInfo *LLQNatInfo; // Nat port mapping to receive LLQ events
+ domainname ServiceRegDomain; // (going away w/ multi-user support)
+ struct uDNS_AuthInfo *AuthInfoList; // list of domains requiring authentication for updates.
+ uDNS_HostnameInfo *Hostnames; // List of registered hostnames + hostname metadata
+ DNSQuestion ReverseMap; // Reverse-map query to find static hostname for service target
+ mDNSBool ReverseMapActive; // Is above query active?
+ domainname StaticHostname; // Current answer to reverse-map query (above)
+ mDNSBool DelaySRVUpdate; // Delay SRV target/port update to avoid "flap"
+ mDNSs32 NextSRVUpdate; // Time to perform delayed update
+ } uDNS_GlobalInfo;
+
+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;
+ mStatus mDNSPlatformStatus;
+ mDNSIPPort UnicastPort4;
+ mDNSIPPort UnicastPort6;
+ 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 mDNS_shutdown; // Set when we're shutting down, allows us to skip some unnecessary steps
+ mDNSu8 lock_rrcache; // For debugging: Set at times when these lists may not be modified
+ mDNSu8 lock_Questions;
+ mDNSu8 lock_Records;
+ #define MaxMsg 120
+ 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 SuppressSending; // Don't send *any* 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 ExpectUnicastResponse; // Set when we send a query with the kDNSQClass_UnicastResponse bit set
+ 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
+ mDNSBool SendDeregistrations; // Set if we need to send deregistrations (immediately)
+ mDNSBool SendImmediateAnswers; // Set if we need to send answers (immediately -- or as soon as SuppressSending clears)
+ mDNSBool SleepState; // Set if we're sleeping (send no more packets)
+
+ // 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
+ DNSQuestion *NewLocalOnlyQuestions; // Fresh local-only questions not yet answered
+ 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];
+
+ // 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 *ResourceRecords;
+ AuthRecord *DuplicateRecords; // Records currently 'on hold' because they are duplicates of existing records
+ AuthRecord *NewLocalRecords; // Fresh local-only records not yet delivered to local-only questions
+ AuthRecord *CurrentRecord; // Next AuthRecord about to be examined
+ NetworkInterfaceInfo *HostInterfaces;
+ mDNSs32 ProbeFailTime;
+ mDNSu32 NumFailedProbes;
+ mDNSs32 SuppressProbes;
+
+ // unicast-specific data
+ uDNS_GlobalInfo uDNS_info;
+ mDNSs32 SuppressStdPort53Queries; // Wait before allowing the next standard unicast query to the user's configured DNS server
+
+ // Fixed storage, to avoid creating large objects on the stack
+ DNSMessage 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 - Useful Static Constants
+#endif
+
+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 mDNSAddr zeroAddr;
+
+extern const mDNSInterfaceID mDNSInterface_Any; // Zero
+extern const mDNSInterfaceID mDNSInterface_LocalOnly; // Special value
+
+extern const mDNSIPPort UnicastDNSPort;
+extern const mDNSIPPort NATPMPPort;
+extern const mDNSIPPort DNSEXTPort;
+extern const mDNSIPPort MulticastDNSPort;
+extern const mDNSIPPort LoopbackIPCPort;
+
+extern const mDNSv4Addr AllDNSAdminGroup;
+#define AllDNSLinkGroupv4 (AllDNSLinkGroup_v4.ip.v4)
+#define AllDNSLinkGroupv6 (AllDNSLinkGroup_v6.ip.v6)
+extern const mDNSAddr AllDNSLinkGroup_v4;
+extern const mDNSAddr AllDNSLinkGroup_v6;
+
+extern const mDNSOpaque16 zeroID;
+extern const mDNSOpaque16 QueryFlags;
+extern const mDNSOpaque16 uQueryFlags;
+extern const mDNSOpaque16 ResponseFlags;
+extern const mDNSOpaque16 UpdateReqFlags;
+extern const mDNSOpaque16 UpdateRespFlags;
+
+#define localdomain (*(const domainname *)"\x5" "local")
+#define LocalReverseMapDomain (*(const domainname *)"\x3" "254" "\x3" "169" "\x7" "in-addr" "\x4" "arpa")
+
+// ***************************************************************************
+#if 0
+#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 mDNSu32 mDNSVal32(mDNSOpaque32 x);
+extern mDNSOpaque16 mDNSOpaque16fromIntVal(mDNSu16 v);
+extern mDNSOpaque32 mDNSOpaque32fromIntVal(mDNSu32 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 mDNSu32 mDNSVal32(mDNSOpaque32 x) { return((mDNSu32)((mDNSu32)x.b[0] << 24 | (mDNSu32)x.b[1] << 16 | (mDNSu32)x.b[2] << 8 | (mDNSu32)x.b[3])); }
+
+mDNSinline mDNSOpaque16 mDNSOpaque16fromIntVal(mDNSu16 v)
+ {
+ mDNSOpaque16 x;
+ x.b[0] = (mDNSu8)(v >> 8);
+ x.b[1] = (mDNSu8)(v & 0xFF);
+ return(x);
+ }
+
+mDNSinline mDNSOpaque32 mDNSOpaque32fromIntVal(mDNSu32 v)
+ {
+ mDNSOpaque32 x;
+ x.b[0] = (mDNSu8) (v >> 24) ;
+ x.b[1] = (mDNSu8)((v >> 16) & 0xFF);
+ x.b[2] = (mDNSu8)((v >> 8 ) & 0xFF);
+ x.b[3] = (mDNSu8)((v ) & 0xFF);
+ return x;
+ }
+
+#endif
+
+// ***************************************************************************
+#if 0
+#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.
+//
+// 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_Close to tidy up before exiting
+//
+// 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_GrowCache (mDNS *const m, CacheEntity *storage, mDNSu32 numrecords);
+extern void mDNS_Close (mDNS *const m);
+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_Reconfirm (mDNS *const m, CacheRecord *const cacherr);
+extern mStatus mDNS_ReconfirmByValue(mDNS *const m, ResourceRecord *const rr);
+extern mDNSs32 mDNS_TimeNow(const mDNS *const m);
+
+// ***************************************************************************
+#if 0
+#pragma mark - Platform support functions that are accessible to the client layer too
+#endif
+
+extern mDNSs32 mDNSPlatformOneSecond;
+
+// ***************************************************************************
+#if 0
+#pragma mark - General utility and helper functions
+#endif
+
+// 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, mDNSRecordCallback Callback, void *Context);
+
+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,
+ const mDNSInterfaceID InterfaceID, mDNSServiceCallback Callback, void *Context);
+extern mStatus mDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra, RData *rdata, mDNSu32 ttl);
+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(mDNS *const m, ServiceRecordSet *sr);
+
+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);
+#define mDNS_DeregisterNoSuchService mDNS_Deregister
+
+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_DomainTypeBrowseLegacy = 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
+
+// ***************************************************************************
+#if 0
+#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) mDNSPlatformMemCopy((SRC)->c, (DST)->c, DomainNameLength((SRC)))
+
+// Comparison functions
+extern mDNSBool SameDomainLabel(const mDNSu8 *a, const mDNSu8 *b);
+extern mDNSBool SameDomainName(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
+
+// 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 DomainNameLength(const domainname *const 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 (1005) 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 - 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 *rr, RDataBody *rd, char *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 IsPrivateV4Addr(mDNSAddr *addr); // returns true for RFC1918 private addresses
+
+#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 mDNSIPv4AddressIsZero(A) mDNSSameIPv4Address((A), zerov4Addr)
+#define mDNSIPv6AddressIsZero(A) mDNSSameIPv6Address((A), zerov6Addr)
+
+#define mDNSIPv4AddressIsOnes(A) mDNSSameIPv4Address((A), onesIPv4Addr)
+#define mDNSIPv6AddressIsOnes(A) mDNSSameIPv6Address((A), onesIPv6Addr)
+
+#define mDNSAddressIsAllDNSLinkGroup(X) ( \
+ ((X)->type == mDNSAddrType_IPv4 && mDNSSameIPv4Address((X)->ip.v4, AllDNSLinkGroupv4)) || \
+ ((X)->type == mDNSAddrType_IPv6 && mDNSSameIPv6Address((X)->ip.v6, AllDNSLinkGroupv6)) )
+
+#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)
+
+
+// ***************************************************************************
+#if 0
+#pragma mark - Authentication Support
+#endif
+
+#define HMAC_LEN 64
+#define HMAC_IPAD 0x36
+#define HMAC_OPAD 0x5c
+#define MD5_LEN 16
+
+// padded keys for inned/outer hash rounds
+typedef struct
+ {
+ mDNSu8 ipad[HMAC_LEN];
+ mDNSu8 opad[HMAC_LEN];
+ } HMAC_Key;
+
+// Internal data structure to maintain authentication information for an update domain
+typedef struct uDNS_AuthInfo
+ {
+ domainname zone;
+ domainname keyname;
+ HMAC_Key key;
+ struct uDNS_AuthInfo *next;
+ } uDNS_AuthInfo;
+
+// Unicast DNS and Dynamic Update specific Client Calls
+//
+// mDNS_SetSecretForZone 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 dissable authentication for the zone.
+
+extern mStatus mDNS_SetSecretForZone(mDNS *m, const domainname *zone, const domainname *key, const char *sharedSecret);
+
+// 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.
+
+// When routable V4 interfaces are added or removed, mDNS_UpdateLLQs should be called to re-estabish LLQs in case the
+// destination address for events (i.e. the route) has changed. For performance reasons, the caller is responsible for
+// batching changes, e.g. calling the routine only once if multiple interfaces are simultanously removed or added.
+
+// DNS servers used to resolve unicast queries are specified by mDNS_AddDNSServer, and may later be removed via mDNS_DeleteDNSServers.
+// 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 void mDNS_UpdateLLQs(mDNS *m);
+extern void mDNS_AddDNSServer(mDNS *const m, const mDNSAddr *dnsAddr, const domainname *domain);
+extern void mDNS_DeleteDNSServers(mDNS *const m);
+
+// Routines called by the core, exported by DNSDigest.c
+
+// Convert a base64 encoded key into a binary byte stream
+extern mDNSs32 DNSDigest_Base64ToBin(const char *src, mDNSu8 *target, mDNSu32 targsize);
+
+// Convert an arbitrary binary key (of any length) into an HMAC key (stored in AuthInfo struct)
+extern void DNSDigest_ConstructHMACKey(uDNS_AuthInfo *info, const mDNSu8 *key, mDNSu32 len);
+
+// sign a DNS message. The message must be compete, 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 mDNSu8 *DNSDigest_SignMessage(DNSMessage *msg, mDNSu8 **end, mDNSu16 *numAdditionals, uDNS_AuthInfo *info);
+
+// ***************************************************************************
+#if 0
+#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 64. 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, const mDNSAddr *dst, mDNSIPPort dstport);
+
+extern void mDNSPlatformLock (const mDNS *const m);
+extern void mDNSPlatformUnlock (const mDNS *const m);
+
+extern void mDNSPlatformStrCopy (const void *src, void *dst);
+extern mDNSu32 mDNSPlatformStrLen (const void *src);
+extern void mDNSPlatformMemCopy (const void *src, void *dst, mDNSu32 len);
+extern mDNSBool mDNSPlatformMemSame (const void *src, const void *dst, mDNSu32 len);
+extern void mDNSPlatformMemZero ( void *dst, mDNSu32 len);
+extern void * mDNSPlatformMemAllocate (mDNSu32 len);
+extern void mDNSPlatformMemFree (void *mem);
+extern mDNSu32 mDNSPlatformRandomSeed (void);
+extern mStatus mDNSPlatformTimeInit (void);
+extern mDNSs32 mDNSPlatformRawTime (void);
+extern mDNSs32 mDNSPlatformUTC (void);
+#define mDNS_TimeNow_NoLock(m) (mDNSPlatformRawTime() + m->timenow_adjust)
+
+// 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 index);
+extern mDNSu32 mDNSPlatformInterfaceIndexfromInterfaceID(mDNS *const m, mDNSInterfaceID id);
+
+// 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.
+// 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 void (*TCPConnectionCallback)(int sd, void *context, mDNSBool ConnectionEstablished);
+extern mStatus mDNSPlatformTCPConnect(const mDNSAddr *dst, mDNSOpaque16 dstport, mDNSInterfaceID InterfaceID,
+ TCPConnectionCallback callback, void *context, int *descriptor);
+extern void mDNSPlatformTCPCloseConnection(int sd);
+extern int mDNSPlatformReadTCP(int sd, void *buf, int buflen);
+extern int mDNSPlatformWriteTCP(int sd, const char *msg, int len);
+
+// 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.
+// The Get() functions must return a linked list of DNameListElem structs, allocated via mDNSPlatformMemAllocate.
+// Platforms may implement the Get() calls via the mDNS_CopyDNameList() helper routine.
+// Callers should free lists obtained via the Get() calls with th mDNS_FreeDNameList routine, provided by the core.
+
+typedef struct DNameListElem
+ {
+ domainname name;
+ struct DNameListElem *next;
+ } DNameListElem;
+
+extern DNameListElem *mDNSPlatformGetSearchDomainList(void);
+extern DNameListElem *mDNSPlatformGetRegDomainList(void);
+
+// Helper functions provided by the core
+extern DNameListElem *mDNS_CopyDNameList(const DNameListElem *orig);
+extern void mDNS_FreeDNameList(DNameListElem *list);
+
+#ifdef _LEGACY_NAT_TRAVERSAL_
+// Support for legacy NAT traversal protocols, implemented by the platform layer and callable by the core.
+
+#define DYN_PORT_MIN 49152 // ephemeral port range
+#define DYN_PORT_MAX 65535
+#define LEGACY_NATMAP_MAX_TRIES 4 // if our desired mapping is taken, how many times we try mapping to a random port
+
+extern mStatus LNT_GetPublicIP(mDNSOpaque32 *ip);
+extern mStatus LNT_MapPort(mDNSIPPort priv, mDNSIPPort pub, mDNSBool tcp);
+extern mStatus LNT_UnmapPort(mDNSIPPort PubPort, mDNSBool tcp);
+#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 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 *const dstaddr, const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID);
+extern void mDNSCoreMachineSleep(mDNS *const m, mDNSBool wake);
+
+extern mDNSBool mDNSAddrIsDNSMulticast(const mDNSAddr *ip);
+
+// ***************************************************************************
+#if 0
+#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 mDNS_CompileTimeAssertionChecks
+ {
+ // 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];
+ };
+
+// ***************************************************************************
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSPosix.c b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSPosix.c
new file mode 100644
index 0000000000..179d84027f
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSPosix.c
@@ -0,0 +1,1771 @@
+/* -*- 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.)
+
+ Change History (most recent first):
+
+$Log: mDNSPosix.c,v $
+Revision 1.78.2.1 2006/08/29 06:24:34 cheshire
+Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
+
+Revision 1.78 2006/06/28 09:12:22 cheshire
+Added debugging message
+
+Revision 1.77 2006/03/19 02:00:11 cheshire
+<rdar://problem/4073825> Improve logic for delaying packets after repeated interface transitions
+
+Revision 1.76 2006/01/09 19:29:16 cheshire
+<rdar://problem/4403128> Cap number of "sendto failed" messages we allow mDNSResponder to log
+
+Revision 1.75 2006/01/05 22:04:57 cheshire
+<rdar://problem/4399479> Log error message when send fails with "operation not permitted"
+
+Revision 1.74 2006/01/05 21:45:27 cheshire
+<rdar://problem/4400118> Fix uninitialized structure member in IPv6 code
+
+Revision 1.73 2005/10/11 21:31:46 cheshire
+<rdar://problem/4296177> Don't depend on IP_RECVTTL succeeding (not available on all platforms)
+
+Revision 1.72 2005/09/08 20:45:26 cheshire
+Default dot-local host name should be "Computer" not "Macintosh",
+since the machine this is running on is most likely NOT a Mac.
+
+Revision 1.71 2005/02/26 01:29:12 cheshire
+Ignore multicasts accidentally delivered to our unicast receiving socket
+
+Revision 1.70 2005/02/04 00:39:59 cheshire
+Move ParseDNSServers() from PosixDaemon.c to mDNSPosix.c so all Posix client layers can use it
+
+Revision 1.69 2004/12/18 02:03:28 cheshire
+Need to #include "dns_sd.h"
+
+Revision 1.68 2004/12/18 00:51:52 cheshire
+Use symbolic constant kDNSServiceInterfaceIndexLocalOnly instead of (mDNSu32) ~0
+
+Revision 1.67 2004/12/17 23:37:48 cheshire
+<rdar://problem/3485365> Guard against repeating wireless dissociation/re-association
+(and other repetitive configuration changes)
+
+Revision 1.66 2004/12/01 04:27:28 cheshire
+<rdar://problem/3872803> Darwin patches for Solaris and Suse
+Don't use uint32_t, etc. -- they require stdint.h, which doesn't exist on FreeBSD 4.x, Solaris, etc.
+
+Revision 1.65 2004/11/30 22:37:01 cheshire
+Update copyright dates and add "Mode: C; tab-width: 4" headers
+
+Revision 1.64 2004/11/23 03:39:47 cheshire
+Let interface name/index mapping capability live directly in JNISupport.c,
+instead of having to call through to the daemon via IPC to get this information.
+
+Revision 1.63 2004/11/12 03:16:43 rpantos
+rdar://problem/3809541 Add mDNSPlatformGetInterfaceByName, mDNSPlatformGetInterfaceName
+
+Revision 1.62 2004/10/28 03:24:42 cheshire
+Rename m->CanReceiveUnicastOn as m->CanReceiveUnicastOn5353
+
+Revision 1.61 2004/10/16 00:17:01 cheshire
+<rdar://problem/3770558> Replace IP TTL 255 check with local subnet source address check
+
+Revision 1.60 2004/09/26 23:20:36 ksekar
+<rdar://problem/3813108> Allow default registrations in multiple wide-area domains
+
+Revision 1.59 2004/09/21 21:02:55 cheshire
+Set up ifname before calling mDNS_RegisterInterface()
+
+Revision 1.58 2004/09/17 01:08:54 cheshire
+Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h
+ The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces
+ declared in that file are ONLY appropriate to single-address-space embedded applications.
+ For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used.
+
+Revision 1.57 2004/09/17 00:19:11 cheshire
+For consistency with AllDNSLinkGroupv6, rename AllDNSLinkGroup to AllDNSLinkGroupv4
+
+Revision 1.56 2004/09/17 00:15:56 cheshire
+Rename mDNSPlatformInit_ReceiveUnicast to mDNSPlatformInit_CanReceiveUnicast
+
+Revision 1.55 2004/09/16 00:24:49 cheshire
+<rdar://problem/3803162> Fix unsafe use of mDNSPlatformTimeNow()
+
+Revision 1.54 2004/09/15 23:55:00 ksekar
+<rdar://problem/3800597> mDNSPosix should #include stdint.h
+
+Revision 1.53 2004/09/14 23:42:36 cheshire
+<rdar://problem/3801296> Need to seed random number generator from platform-layer data
+
+Revision 1.52 2004/08/25 16:42:13 ksekar
+Fix Posix build - change mDNS_SetFQDNs to mDNS_SetFQDN, remove unicast
+hostname parameter.
+
+Revision 1.51 2004/08/14 03:22:42 cheshire
+<rdar://problem/3762579> Dynamic DNS UI <-> mDNSResponder glue
+Add GetUserSpecifiedDDNSName() routine
+Convert ServiceRegDomain to domainname instead of C string
+Replace mDNS_GenerateFQDN/mDNS_GenerateGlobalFQDN with mDNS_SetFQDNs
+
+Revision 1.50 2004/08/11 01:20:20 cheshire
+Declare private local functions using "mDNSlocal"
+
+Revision 1.49 2004/07/26 22:49:31 ksekar
+<rdar://problem/3651409>: Feature #9516: Need support for NATPMP in client
+
+Revision 1.48 2004/07/20 01:47:36 rpantos
+NOT_HAVE_SA_LEN applies to v6, too. And use more-portable s6_addr.
+
+Revision 1.47 2004/06/25 00:26:27 rpantos
+Changes to fix the Posix build on Solaris.
+
+Revision 1.46 2004/05/13 04:54:20 ksekar
+Unified list copy/free code. Added symetric list for
+
+Revision 1.45 2004/05/12 22:03:09 ksekar
+Made GetSearchDomainList a true platform-layer call (declaration moved
+from mDNSMacOSX.h to mDNSEmbeddedAPI.h), impelemted to return "local"
+only on non-OSX platforms. Changed call to return a copy of the list
+to avoid shared memory issues. Added a routine to free the list.
+
+Revision 1.44 2004/04/21 02:49:11 cheshire
+To reduce future confusion, renamed 'TxAndRx' to 'McastTxRx'
+
+Revision 1.43 2004/04/14 23:09:29 ksekar
+Support for TSIG signed dynamic updates.
+
+Revision 1.42 2004/04/09 17:43:04 cheshire
+Make sure to set the McastTxRx field so that duplicate suppression works correctly
+
+Revision 1.41 2004/02/06 01:19:51 cheshire
+Conditionally exclude IPv6 code unless HAVE_IPV6 is set
+
+Revision 1.40 2004/02/05 01:00:01 rpantos
+Fix some issues that turned up when building for FreeBSD.
+
+Revision 1.39 2004/01/28 21:12:15 cheshire
+Reconcile mDNSIPv6Support & HAVE_IPV6 into a single flag (HAVE_IPV6)
+
+Revision 1.38 2004/01/27 20:15:23 cheshire
+<rdar://problem/3541288>: Time to prune obsolete code for listening on port 53
+
+Revision 1.37 2004/01/24 05:12:03 cheshire
+<rdar://problem/3534352>: Need separate socket for issuing unicast queries
+
+Revision 1.36 2004/01/24 04:59:16 cheshire
+Fixes so that Posix/Linux, OS9, Windows, and VxWorks targets build again
+
+Revision 1.35 2004/01/23 21:37:08 cheshire
+For consistency, rename multicastSocket to multicastSocket4, and multicastSocketv6 to multicastSocket6
+
+Revision 1.34 2004/01/22 03:43:09 cheshire
+Export constants like mDNSInterface_LocalOnly so that the client layers can use them
+
+Revision 1.33 2004/01/21 21:54:20 cheshire
+<rdar://problem/3448144>: Don't try to receive unicast responses if we're not the first to bind to the UDP port
+
+Revision 1.32 2004/01/20 01:49:28 rpantos
+Tweak error handling of last checkin a bit.
+
+Revision 1.31 2004/01/20 01:39:27 rpantos
+Respond to If changes by rebuilding interface list.
+
+Revision 1.30 2003/12/11 19:40:36 cheshire
+Fix 'destAddr.type == senderAddr.type;' that should have said 'destAddr.type = senderAddr.type;'
+
+Revision 1.29 2003/12/11 18:53:22 cheshire
+Fix compiler warning reported by Paul Guyot
+
+Revision 1.28 2003/12/11 03:03:51 rpantos
+Clean up mDNSPosix so that it builds on OS X again.
+
+Revision 1.27 2003/12/08 20:47:02 rpantos
+Add support for mDNSResponder on Linux.
+
+Revision 1.26 2003/11/14 20:59:09 cheshire
+Clients can't use AssignDomainName macro because mDNSPlatformMemCopy is defined in mDNSPlatformFunctions.h.
+Best solution is just to combine mDNSEmbeddedAPI.h and mDNSPlatformFunctions.h into a single file.
+
+Revision 1.25 2003/10/30 19:25:49 cheshire
+Fix signed/unsigned warning on certain compilers
+
+Revision 1.24 2003/08/18 23:12:23 cheshire
+<rdar://problem/3382647> mDNSResponder divide by zero in mDNSPlatformRawTime()
+
+Revision 1.23 2003/08/12 19:56:26 cheshire
+Update to APSL 2.0
+
+Revision 1.22 2003/08/06 18:46:15 cheshire
+LogMsg() errors are serious -- always report them to stderr, regardless of debugging level
+
+Revision 1.21 2003/08/06 18:20:51 cheshire
+Makefile cleanup
+
+Revision 1.20 2003/08/05 23:56:26 cheshire
+Update code to compile with the new mDNSCoreReceive() function that requires a TTL
+(Right now mDNSPosix.c just reports 255 -- we should fix this)
+
+Revision 1.19 2003/07/19 03:15:16 cheshire
+Add generic MemAllocate/MemFree prototypes to mDNSPlatformFunctions.h,
+and add the obvious trivial implementations to each platform support layer
+
+Revision 1.18 2003/07/14 18:11:54 cheshire
+Fix stricter compiler warnings
+
+Revision 1.17 2003/07/13 01:08:38 cheshire
+There's not much point running mDNS over a point-to-point link; exclude those
+
+Revision 1.16 2003/07/02 21:19:59 cheshire
+<rdar://problem/3313413> Update copyright notices, etc., in source code comments
+
+Revision 1.15 2003/06/18 05:48:41 cheshire
+Fix warnings
+
+Revision 1.14 2003/05/26 03:21:30 cheshire
+Tidy up address structure naming:
+mDNSIPAddr => mDNSv4Addr (for consistency with mDNSv6Addr)
+mDNSAddr.addr.ipv4 => mDNSAddr.ip.v4
+mDNSAddr.addr.ipv6 => mDNSAddr.ip.v6
+
+Revision 1.13 2003/05/26 03:01:28 cheshire
+<rdar://problem/3268904> sprintf/vsprintf-style functions are unsafe; use snprintf/vsnprintf instead
+
+Revision 1.12 2003/05/21 03:49:18 cheshire
+Fix warning
+
+Revision 1.11 2003/05/06 00:00:50 cheshire
+<rdar://problem/3248914> Rationalize naming of domainname manipulation functions
+
+Revision 1.10 2003/04/25 01:45:57 cheshire
+<rdar://problem/3240002> mDNS_RegisterNoSuchService needs to include a host name
+
+Revision 1.9 2003/03/20 21:10:31 cheshire
+Fixes done at IETF 56 to make mDNSProxyResponderPosix run on Solaris
+
+Revision 1.8 2003/03/15 04:40:38 cheshire
+Change type called "mDNSOpaqueID" to the more descriptive name "mDNSInterfaceID"
+
+Revision 1.7 2003/03/13 03:46:21 cheshire
+Fixes to make the code build on Linux
+
+Revision 1.6 2003/03/08 00:35:56 cheshire
+Switched to using new "mDNS_Execute" model (see "mDNSCore/Implementer Notes.txt")
+
+Revision 1.5 2002/12/23 22:13:31 jgraessl
+Reviewed by: Stuart Cheshire
+Initial IPv6 support for mDNSResponder.
+
+Revision 1.4 2002/09/27 01:47:45 cheshire
+Workaround for Linux 2.0 systems that don't have IP_PKTINFO
+
+Revision 1.3 2002/09/21 20:44:53 zarzycki
+Added APSL info
+
+Revision 1.2 2002/09/19 21:25:36 cheshire
+mDNS_snprintf() doesn't need to be in a separate file
+
+Revision 1.1 2002/09/17 06:24:34 cheshire
+First checkin
+*/
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "mDNSEmbeddedAPI.h" // Defines the interface provided to the client layer above
+#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>
+#include <syslog.h>
+#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"
+
+// ***************************************************************************
+// 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, const mDNSAddr *dst, mDNSIPPort dstPort)
+ {
+ int err = 0;
+ struct sockaddr_storage to;
+ PosixNetworkInterface * thisIntf = (PosixNetworkInterface *)(InterfaceID);
+ int sendingsocket = -1;
+
+ assert(m != NULL);
+ assert(msg != NULL);
+ assert(end != NULL);
+ assert( (((char *) end) - ((char *) msg)) > 0 );
+ assert(dstPort.NotAnInteger != 0);
+
+ 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 = AllDNSLinkGroupv4;
+ else if (senderAddr.type == mDNSAddrType_IPv6) destAddr.ip.v6 = AllDNSLinkGroupv6;
+ }
+ #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 mStatus mDNSPlatformTCPConnect(const mDNSAddr *dst, mDNSOpaque16 dstport, mDNSInterfaceID InterfaceID,
+ TCPConnectionCallback callback, void *context, int *descriptor)
+ {
+ (void)dst; // Unused
+ (void)dstport; // Unused
+ (void)InterfaceID; // Unused
+ (void)callback; // Unused
+ (void)context; // Unused
+ (void)descriptor; // Unused
+ return(mStatus_UnsupportedErr);
+ }
+
+mDNSexport void mDNSPlatformTCPCloseConnection(int sd)
+ {
+ (void)sd; // Unused
+ }
+
+mDNSexport int mDNSPlatformReadTCP(int sd, void *buf, int buflen)
+ {
+ (void)sd; // Unused
+ (void)buf; // Unused
+ (void)buflen; // Unused
+ return(0);
+ }
+
+mDNSexport int mDNSPlatformWriteTCP(int sd, const char *msg, int len)
+ {
+ (void)sd; // Unused
+ (void)msg; // Unused
+ (void)len; // Unused
+ return(0);
+ }
+
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark ***** Get/Free Search Domain List
+#endif
+
+mDNSexport DNameListElem *mDNSPlatformGetSearchDomainList(void)
+ {
+ static DNameListElem tmp;
+ static mDNSBool init = mDNSfalse;
+
+ if (!init)
+ {
+ MakeDomainNameFromDNSNameString(&tmp.name, "local.");
+ tmp.next = NULL;
+ init = mDNStrue;
+ }
+ return mDNS_CopyDNameList(&tmp);
+ }
+
+mDNSexport DNameListElem *mDNSPlatformGetRegDomainList(void)
+ {
+ return NULL;
+ }
+
+#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;
+ gethostname((char *)(&namelabel->c[1]), MAX_DOMAIN_LABEL);
+ 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 (strncmp(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, &DNSAddr, NULL);
+ numOfServers++;
+ }
+ }
+ 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);
+
+ 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)
+ {
+ PosixNetworkInterface *intf;
+
+ assert(m != NULL);
+
+ if (id == mDNSInterface_LocalOnly) return(kDNSServiceInterfaceIndexLocalOnly);
+
+ 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) assert(close(intf->multicastSocket4) == 0);
+#if HAVE_IPV6
+ if (intf->multicastSocket6 != -1) assert(close(intf->multicastSocket6) == 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("socket"); }
+
+ // ... 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 and Solaris
+ 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
+ #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 = AllDNSLinkGroupv4.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_RECVPKTINFO) // Solaris
+ if (err == 0)
+ {
+ err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_RECVPKTINFO, &kOn, sizeof(kOn));
+ if (err < 0) { err = errno; perror("setsockopt - IPV6_PKTINFO"); }
+ }
+
+ #elif defined(IPV6_PKTINFO)
+ if (err == 0)
+ {
+ err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_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_RECVHOPLIMIT) // Solaris
+ if (err == 0)
+ {
+ err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_RECVHOPLIMIT, &kOn, sizeof(kOn));
+ if (err < 0) { err = errno; perror("setsockopt - IPV6_HOPLIMIT"); }
+ }
+
+ #elif defined(IPV6_HOPLIMIT)
+ if (err == 0)
+ {
+ err = setsockopt(*sktPtr, IPPROTO_IPV6, IPV6_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*)&AllDNSLinkGroupv6;
+ 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) { assert(close(*sktPtr) == 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 (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. */
+ bzero( &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( void *context)
+ {
+ IfChangeRec *pChgRec = (IfChangeRec*) context;
+ fd_set readFDs;
+ mDNSu32 changedInterfaces = 0;
+ struct timeval zeroTimeout = { 0, 0 };
+
+ 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
+ if (err == mStatus_NoError) ParseDNSServers(m, uDNS_SERVERS_FILE);
+
+ 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) assert(close(m->p->unicastSocket4) == 0);
+#if HAVE_IPV6
+ if (m->p->unicastSocket6 != -1) assert(close(m->p->unicastSocket6) == 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(const void *src, void *dst)
+ {
+ strcpy((char *)dst, (char *)src);
+ }
+
+// 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(const void *src, void *dst, 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 *src, const void *dst, 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);
+ }
+
+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;
+
+ bzero( &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;
+
+ bzero( &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->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/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSPosix.h b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSPosix.h
new file mode 100644
index 0000000000..bd5286c899
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSPosix.h
@@ -0,0 +1,147 @@
+/* -*- 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.
+
+ Change History (most recent first):
+
+$Log: mDNSPosix.h,v $
+Revision 1.18 2006/08/14 23:24:47 cheshire
+Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
+
+Revision 1.17 2005/02/04 00:39:59 cheshire
+Move ParseDNSServers() from PosixDaemon.c to mDNSPosix.c so all Posix client layers can use it
+
+Revision 1.16 2004/11/30 22:37:01 cheshire
+Update copyright dates and add "Mode: C; tab-width: 4" headers
+
+Revision 1.15 2004/02/06 01:19:51 cheshire
+Conditionally exclude IPv6 code unless HAVE_IPV6 is set
+
+Revision 1.14 2004/01/28 21:12:15 cheshire
+Reconcile mDNSIPv6Support & HAVE_IPV6 into a single flag (HAVE_IPV6)
+
+Revision 1.13 2004/01/24 05:12:03 cheshire
+<rdar://problem/3534352>: Need separate socket for issuing unicast queries
+
+Revision 1.12 2004/01/23 21:37:08 cheshire
+For consistency, rename multicastSocket to multicastSocket4, and multicastSocketv6 to multicastSocket6
+
+Revision 1.11 2003/12/11 03:03:51 rpantos
+Clean up mDNSPosix so that it builds on OS X again.
+
+Revision 1.10 2003/12/08 20:47:02 rpantos
+Add support for mDNSResponder on Linux.
+
+Revision 1.9 2003/10/30 19:25:19 cheshire
+Fix warning on certain compilers
+
+Revision 1.8 2003/08/12 19:56:26 cheshire
+Update to APSL 2.0
+
+Revision 1.7 2003/07/02 21:19:59 cheshire
+<rdar://problem/3313413> Update copyright notices, etc., in source code comments
+
+Revision 1.6 2003/03/13 03:46:21 cheshire
+Fixes to make the code build on Linux
+
+Revision 1.5 2003/03/08 00:35:56 cheshire
+Switched to using new "mDNS_Execute" model (see "mDNSCore/Implementer Notes.txt")
+
+Revision 1.4 2002/12/23 22:13:31 jgraessl
+
+Reviewed by: Stuart Cheshire
+Initial IPv6 support for mDNSResponder.
+
+Revision 1.3 2002/09/21 20:44:53 zarzycki
+Added APSL info
+
+Revision 1.2 2002/09/19 04:20:44 cheshire
+Remove high-ascii characters that confuse some systems
+
+Revision 1.1 2002/09/17 06:24:34 cheshire
+First checkin
+
+*/
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifndef __mDNSPlatformPosix_h
+#define __mDNSPlatformPosix_h
+
+#include <signal.h>
+#include <sys/time.h>
+
+#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)( 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/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSUNP.c b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSUNP.c
new file mode 100644
index 0000000000..15db1b7539
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSUNP.c
@@ -0,0 +1,1102 @@
+/* -*- 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.
+
+ Change History (most recent first):
+
+$Log: mDNSUNP.c,v $
+Revision 1.34 2006/08/14 23:24:47 cheshire
+Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
+
+Revision 1.33 2006/03/13 23:14:21 cheshire
+<rdar://problem/4427969> Compile problems on FreeBSD
+Use <netinet/in_var.h> instead of <netinet6/in6_var.h>
+
+Revision 1.32 2005/12/21 02:56:43 cheshire
+<rdar://problem/4243433> get_ifi_info() should fake ifi_index when SIOCGIFINDEX undefined
+
+Revision 1.31 2005/12/21 02:46:05 cheshire
+<rdar://problem/4243514> mDNSUNP.c needs to include <sys/param.h> on 4.4BSD Lite
+
+Revision 1.30 2005/11/29 20:03:02 mkrochma
+Wrapped sin_len with #ifndef NOT_HAVE_SA_LEN
+
+Revision 1.29 2005/11/12 02:23:10 cheshire
+<rdar://problem/4317680> mDNSUNP.c needs to deal with lame results from SIOCGIFNETMASK, SIOCGIFBRDADDR and SIOCGIFDSTADDR
+
+Revision 1.28 2005/10/31 22:09:45 cheshire
+Buffer "char addr6[33]" was seven bytes too small
+
+Revision 1.27 2005/06/29 15:54:21 cheshire
+<rdar://problem/4113742> mDNSResponder-107.1 does not work on FreeBSD
+Refine last checkin so that it (hopefully) doesn't break get_ifi_info() for every other OS
+
+Revision 1.26 2005/04/08 21:43:59 ksekar
+<rdar://problem/4083426> mDNSPosix (v98) retrieve interface list bug on AMD64 architecture
+Submitted by Andrew de Quincey
+
+Revision 1.25 2005/04/08 21:37:57 ksekar
+<rdar://problem/3792767> get_ifi_info doesn't return IPv6 interfaces on Linux
+
+Revision 1.24 2005/04/08 21:30:16 ksekar
+<rdar://problem/4007457> Compiling problems with mDNSResponder-98 on Solaris/Sparc v9
+Patch submitted by Bernd Kuhls
+
+Revision 1.23 2004/12/01 04:25:05 cheshire
+<rdar://problem/3872803> Darwin patches for Solaris and Suse
+Provide daemon() for platforms that don't have it
+
+Revision 1.22 2004/11/30 22:37:01 cheshire
+Update copyright dates and add "Mode: C; tab-width: 4" headers
+
+Revision 1.21 2004/11/08 22:13:59 rpantos
+Create sockf6 lazily when v6 interface found.
+
+Revision 1.20 2004/10/16 00:17:01 cheshire
+<rdar://problem/3770558> Replace IP TTL 255 check with local subnet source address check
+
+Revision 1.19 2004/07/20 01:47:36 rpantos
+NOT_HAVE_SA_LEN applies to v6, too. And use more-portable s6_addr.
+
+Revision 1.18 2004/07/08 21:30:21 rpantos
+
+Revision 1.17 2004/06/25 00:26:27 rpantos
+Changes to fix the Posix build on Solaris.
+
+Revision 1.16 2004/03/20 05:37:09 cheshire
+Fix contributed by Terry Lambert & Alfred Perlstein:
+Don't use uint8_t -- it requires stdint.h, which doesn't exist on FreeBSD 4.x
+
+Revision 1.15 2004/02/14 01:09:45 rpantos
+Just use HAVE_IPV6 rather than defined(HAVE_IPV6).
+
+Revision 1.14 2003/12/11 18:53:40 cheshire
+Fix compiler warning reported by Paul Guyot
+
+Revision 1.13 2003/12/08 20:47:02 rpantos
+Add support for mDNSResponder on Linux.
+
+Revision 1.12 2003/09/02 20:47:13 cheshire
+Fix signed/unsigned warning
+
+Revision 1.11 2003/08/12 19:56:26 cheshire
+Update to APSL 2.0
+
+Revision 1.10 2003/08/06 18:20:51 cheshire
+Makefile cleanup
+
+Revision 1.9 2003/07/14 18:11:54 cheshire
+Fix stricter compiler warnings
+
+Revision 1.8 2003/07/02 21:19:59 cheshire
+<rdar://problem/3313413> Update copyright notices, etc., in source code comments
+
+Revision 1.7 2003/03/20 21:10:31 cheshire
+Fixes done at IETF 56 to make mDNSProxyResponderPosix run on Solaris
+
+Revision 1.6 2003/03/13 03:46:21 cheshire
+Fixes to make the code build on Linux
+
+Revision 1.5 2003/02/07 03:02:02 cheshire
+Submitted by: Mitsutaka Watanabe
+The code saying "index += 1;" was effectively making up random interface index values.
+The right way to find the correct interface index is if_nametoindex();
+
+Revision 1.4 2002/12/23 22:13:31 jgraessl
+
+Reviewed by: Stuart Cheshire
+Initial IPv6 support for mDNSResponder.
+
+Revision 1.3 2002/09/21 20:44:53 zarzycki
+Added APSL info
+
+Revision 1.2 2002/09/19 04:20:44 cheshire
+Remove high-ascii characters that confuse some systems
+
+Revision 1.1 2002/09/17 06:24:34 cheshire
+First checkin
+
+*/
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "mDNSUNP.h"
+
+#include "mDNSDebug.h"
+
+#include <errno.h>
+#include <assert.h>
+#include <string.h>
+#include <stdlib.h>
+#include <sys/uio.h>
+#include <sys/ioctl.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
+#if !HAVE_SOLARIS
+#include <net/if_var.h>
+#else
+#include <alloca.h>
+#ifdef HAVE_SOLARIS_ZONES
+#include <zone.h>
+#endif /* HAVE_SOLARIS_ZONES */
+#endif /* !HAVE_SOLARIS */
+#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
+
+#if 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;
+ FILE *fp;
+ char addr[8][5];
+ int flags, myflags, index, plen, scope;
+ char ifname[8], 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;
+
+ res0=NULL;
+ ifihead = NULL;
+ ifipnext = &ifihead;
+ lastname[0] = 0;
+
+ if ((fp = fopen(PROC_IFINET6_PATH, "r")) != NULL) {
+ while (fscanf(fp,
+ "%4s%4s%4s%4s%4s%4s%4s%4s %02x %02x %02x %02x %8s\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;
+ }
+ memcpy(lastname, ifname, IFNAMSIZ);
+ ifi = (struct ifi_info*)calloc(1, sizeof(struct ifi_info));
+ if (ifi == NULL) {
+ goto gotError;
+ }
+
+ *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 */
+ memcpy(ifi->ifi_name, ifname, IFI_NAME);
+
+ /* Add interface index */
+ ifi->ifi_index = index;
+
+ /* If interface is in /proc then it is up*/
+ ifi->ifi_flags = IFF_UP;
+
+ freeaddrinfo(res0);
+ res0=NULL;
+ }
+ }
+ goto done;
+
+ gotError:
+ if (ifihead != NULL) {
+ free_ifi_info(ifihead);
+ ifihead = NULL;
+ }
+ if (res0 != NULL) {
+ freeaddrinfo(res0);
+ res0=NULL;
+ }
+ done:
+ return(ifihead); /* pointer to first structure in linked list */
+ }
+
+#endif /* LINUX */
+#endif /* defined(AF_INET6) && HAVE_IPV6 */
+
+#if HAVE_SOLARIS
+
+/*
+ * Converts prefix length to network mask. Assumes
+ * addr points to a zeroed out buffer and prefix <= sizeof(addr)
+ * Unlike plen_to_mask returns netmask in binary form and not
+ * in text form.
+ */
+static void plen_to_netmask(int prefix, unsigned char *addr) {
+ for (; prefix > 8; prefix -= 8)
+ *addr++ = 0xff;
+ for (; prefix > 0; prefix--)
+ *addr = (*addr >> 1) | 0x80;
+}
+
+/*
+ * This function goes through all the IP interfaces associated with a
+ * physical interface and finds the best matched one for use by mDNS.
+ * Returns NULL when none of the IP interfaces associated with a physical
+ * interface are usable. Otherwise returns the best matched interface
+ * information and a pointer to the best matched lifreq.
+ */
+struct ifi_info *
+select_src_ifi_info_solaris(int sockfd, int numifs,
+ struct lifreq *lifrlist, const char *curifname,
+ struct lifreq **best_lifr)
+{
+ struct lifreq *lifr;
+ struct lifreq lifrcopy;
+ struct ifi_info *ifi;
+ char *chptr;
+ char cmpifname[LIFNAMSIZ];
+ int i;
+ uint64_t best_lifrflags;
+ uint64_t ifflags;
+
+ *best_lifr = NULL;
+
+ /*
+ * Check all logical interfaces associated with the physical
+ * interface and figure out which one works best for us.
+ */
+ for (i = numifs, lifr = lifrlist; i > 0; --i, ++lifr) {
+
+ if (strlcpy(cmpifname, lifr->lifr_name, sizeof(cmpifname)) >= sizeof(cmpifname))
+ continue; /* skip interface */
+
+ /* Strip logical interface number before checking ifname */
+ if ((chptr = strchr(cmpifname, ':')) != NULL)
+ *chptr = '\0';
+
+ /*
+ * Check ifname to see if the logical interface is associated
+ * with the physical interface we are interested in.
+ */
+ if (strcmp(cmpifname, curifname) != 0)
+ continue;
+
+#ifdef HAVE_SOLARIS_ZONES
+ /* Check the zone associated with the address */
+ lifrcopy = *lifr;
+ if (ioctl(sockfd, SIOCGLIFZONE, &lifrcopy) < 0) {
+ /* interface removed */
+ if (errno == ENXIO)
+ continue;
+ return(NULL);
+ }
+ if (lifrcopy.lifr_zoneid != getzoneid())
+ continue;
+#endif
+
+ lifrcopy = *lifr;
+ if (ioctl(sockfd, SIOCGLIFFLAGS, &lifrcopy) < 0) {
+ /* interface removed */
+ if (errno == ENXIO)
+ continue;
+ return(NULL);
+ }
+ ifflags = lifrcopy.lifr_flags;
+
+ /* ignore address if not up */
+ if ((ifflags & IFF_UP) == 0)
+ continue;
+ /*
+ * Avoid address if any of the following flags are set:
+ * IFF_NOFAILOVER: IPMP test address for use only by in.mpathd
+ * IFF_NOXMIT: no packets transmitted over interface
+ * IFF_NOLOCAL: no address
+ * IFF_PRIVATE: is not advertised
+ */
+ if (ifflags & (IFF_NOFAILOVER | IFF_NOXMIT
+ | IFF_NOLOCAL | IFF_PRIVATE))
+ continue;
+
+ if (*best_lifr != NULL) {
+ /*
+ * Check if we found a better interface by checking
+ * the flags. If flags are identical we prefer
+ * the new found interface.
+ */
+ uint64_t diff_flags = best_lifrflags ^ ifflags;
+
+ /* If interface has a different set of flags */
+ if (diff_flags != 0) {
+ /* Check flags in increasing order of ones we prefer */
+
+ /* Address temporary? */
+ if ((diff_flags & IFF_TEMPORARY) &&
+ (ifflags & IFF_TEMPORARY))
+ continue;
+ /* Deprecated address? */
+ if ((diff_flags & IFF_DEPRECATED) &&
+ (ifflags & IFF_DEPRECATED))
+ continue;
+ /* Last best-matched interface address has preferred? */
+ if ((diff_flags & IFF_PREFERRED) &&
+ ((ifflags & IFF_PREFERRED) == 0))
+ continue;
+ }
+ }
+
+ /* Set best match interface & flags */
+ *best_lifr = lifr;
+ best_lifrflags = ifflags;
+ }
+
+ if (*best_lifr == NULL)
+ return(NULL);
+
+ /* Found a match: return the interface information */
+ ifi = calloc(1, sizeof(struct ifi_info));
+ if (ifi == NULL)
+ return(NULL);
+
+ ifi->ifi_flags = best_lifrflags;
+ ifi->ifi_index = if_nametoindex((*best_lifr)->lifr_name);
+ if (strlcpy(ifi->ifi_name, (*best_lifr)->lifr_name, sizeof(ifi->ifi_name)) >= sizeof(ifi->ifi_name)) {
+ free(ifi);
+ return(NULL);
+ }
+ return(ifi);
+}
+
+/*
+ * Returns a list of IP interface information on Solaris. The function
+ * returns all IP interfaces on the system with IPv4 address assigned
+ * when passed AF_INET and returns IP interfaces with IPv6 address assigned
+ * when AF_INET6 is passed.
+ */
+struct ifi_info *get_ifi_info_solaris(int family)
+{
+ struct ifi_info *ifi, *ifihead, **ifipnext;
+ int sockfd;
+ int len;
+ char *buf;
+ char *cptr;
+ char ifname[LIFNAMSIZ], cmpifname[LIFNAMSIZ];
+ struct sockaddr_in *sinptr;
+ struct lifnum lifn;
+ struct lifconf lifc;
+ struct lifreq *lifrp, *best_lifr;
+ struct lifreq lifrcopy;
+ int numifs, nlifr, n;
+#if defined(AF_INET6) && HAVE_IPV6
+ struct sockaddr_in6 *sinptr6;
+#endif
+
+ ifihead = NULL;
+
+ sockfd = socket(family, SOCK_DGRAM, 0);
+ if (sockfd < 0)
+ goto gotError;
+
+again:
+ lifn.lifn_family = family;
+ lifn.lifn_flags = 0;
+ if (ioctl(sockfd, SIOCGLIFNUM, &lifn) < 0)
+ goto gotError;
+ /*
+ * Pad interface count to detect & retrieve any
+ * additional interfaces between IFNUM & IFCONF calls.
+ */
+ lifn.lifn_count += 4;
+ numifs = lifn.lifn_count;
+ len = numifs * sizeof (struct lifreq);
+ buf = alloca(len);
+
+ lifc.lifc_family = family;
+ lifc.lifc_len = len;
+ lifc.lifc_buf = buf;
+ lifc.lifc_flags = 0;
+
+ if (ioctl(sockfd, SIOCGLIFCONF, &lifc) < 0)
+ goto gotError;
+
+ nlifr = lifc.lifc_len / sizeof(struct lifreq);
+ if (nlifr >= numifs)
+ goto again;
+
+ lifrp = lifc.lifc_req;
+ ifipnext = &ifihead;
+
+ for (n = nlifr; n > 0; n--, lifrp++) {
+
+ if (lifrp->lifr_addr.ss_family != family)
+ continue;
+
+ /*
+ * See if we have already processed the interface
+ * by checking the interface names.
+ */
+ if (strlcpy(ifname, lifrp->lifr_name, sizeof(ifname)) >= sizeof(ifname))
+ goto gotError;
+ if ((cptr = strchr(ifname, ':')) != NULL)
+ *cptr = '\0';
+
+ /*
+ * If any of the interfaces found so far share the physical
+ * interface name then we have already processed the interface.
+ */
+ for (ifi = ifihead; ifi != NULL; ifi = ifi->ifi_next) {
+
+ /* Retrieve physical interface name */
+ (void) strlcpy(cmpifname, ifi->ifi_name, sizeof(cmpifname));
+
+ /* Strip logical interface number before checking ifname */
+ if ((cptr = strchr(cmpifname, ':')) != NULL)
+ *cptr = '\0';
+
+ if (strcmp(cmpifname, ifname) == 0)
+ break;
+ }
+ if (ifi != NULL)
+ continue; /* already processed */
+
+ /*
+ * New interface, find the one with the preferred source
+ * address for our use in Multicast DNS.
+ */
+ if ((ifi = select_src_ifi_info_solaris(sockfd, nlifr,
+ lifc.lifc_req, ifname, &best_lifr)) == NULL)
+ continue;
+
+ assert(best_lifr != NULL);
+ assert((best_lifr->lifr_addr.ss_family == AF_INET6) ||
+ (best_lifr->lifr_addr.ss_family == AF_INET));
+
+ switch (best_lifr->lifr_addr.ss_family) {
+
+#if defined(AF_INET6) && HAVE_IPV6
+ case AF_INET6:
+ sinptr6 = (struct sockaddr_in6 *) &best_lifr->lifr_addr;
+ ifi->ifi_addr = malloc(sizeof(struct sockaddr_in6));
+ if (ifi->ifi_addr == NULL)
+ goto gotError;
+ memcpy(ifi->ifi_addr, sinptr6, sizeof(struct sockaddr_in6));
+
+ ifi->ifi_netmask = calloc(1, sizeof(struct sockaddr_in6));
+ if (ifi->ifi_netmask == NULL)
+ goto gotError;
+ sinptr6 = (struct sockaddr_in6 *)(ifi->ifi_netmask);
+ sinptr6->sin6_family = AF_INET6;
+ plen_to_netmask(best_lifr->lifr_addrlen,
+ (unsigned char *) &(sinptr6->sin6_addr));
+ break;
+#endif
+
+ case AF_INET:
+ sinptr = (struct sockaddr_in *) &best_lifr->lifr_addr;
+ ifi->ifi_addr = malloc(sizeof(struct sockaddr_in));
+ if (ifi->ifi_addr == NULL)
+ goto gotError;
+
+ memcpy(ifi->ifi_addr, sinptr, sizeof(struct sockaddr_in));
+
+ lifrcopy = *best_lifr;
+ if (ioctl(sockfd, SIOCGLIFNETMASK, &lifrcopy) < 0) {
+ /* interface removed */
+ if (errno == ENXIO) {
+ free(ifi->ifi_addr);
+ free(ifi);
+ continue;
+ }
+ goto gotError;
+ }
+
+ ifi->ifi_netmask = malloc(sizeof(struct sockaddr_in));
+ if (ifi->ifi_netmask == NULL)
+ goto gotError;
+ sinptr = (struct sockaddr_in *) &lifrcopy.lifr_addr;
+ sinptr->sin_family = AF_INET;
+ memcpy(ifi->ifi_netmask, sinptr, sizeof(struct sockaddr_in));
+ break;
+
+ default:
+ /* never reached */
+ break;
+ }
+
+ *ifipnext = ifi; /* prev points to this new one */
+ ifipnext = &ifi->ifi_next; /* pointer to next one goes here */
+ }
+
+ (void) close(sockfd);
+ return(ifihead); /* pointer to first structure in linked list */
+
+gotError:
+ if (sockfd != -1)
+ (void) close(sockfd);
+ if (ifihead != NULL)
+ free_ifi_info(ifihead);
+ return(NULL);
+}
+
+#endif /* HAVE_SOLARIS */
+
+struct ifi_info *get_ifi_info(int family, int doaliases)
+{
+ int junk;
+ struct ifi_info *ifi, *ifihead, **ifipnext;
+ 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);
+#elif HAVE_SOLARIS
+ return get_ifi_info_solaris(family);
+#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;
+ }
+ *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) 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);
+ bzero(&ifr6, 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) 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_brdaddr != NULL)
+ free(ifi->ifi_brdaddr);
+ if (ifi->ifi_dstaddr != NULL)
+ free(ifi->ifi_dstaddr);
+ if (ifi->ifi_netmask != NULL)
+ free(ifi->ifi_netmask);
+ 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];
+ pad64_t align8; /* ensure structure is 8-byte aligned on sparc */
+ } control_un;
+
+ *ttl = 255; // If kernel fails to provide TTL data then assume the TTL was 255 as it should be
+
+ msg.msg_control = (void *) 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
+ (void) memcpy(&pktp->ipi_ifindex, CMSG_DATA(cmptr), sizeof(uint_t));
+ 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_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_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 <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/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSUNP.h b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSUNP.h
new file mode 100644
index 0000000000..68594d219d
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/mDNSUNP.h
@@ -0,0 +1,190 @@
+/* -*- 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.
+
+ Change History (most recent first):
+
+$Log: mDNSUNP.h,v $
+Revision 1.19 2006/08/14 23:24:47 cheshire
+Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
+
+Revision 1.18 2005/04/08 21:37:57 ksekar
+<rdar://problem/3792767> get_ifi_info doesn't return IPv6 interfaces on Linux
+
+Revision 1.17 2004/12/17 19:32:43 cheshire
+Add missing semicolon
+
+Revision 1.16 2004/12/01 04:25:05 cheshire
+<rdar://problem/3872803> Darwin patches for Solaris and Suse
+Provide daemon() for platforms that don't have it
+
+Revision 1.15 2004/11/30 22:37:01 cheshire
+Update copyright dates and add "Mode: C; tab-width: 4" headers
+
+Revision 1.14 2004/10/16 00:17:01 cheshire
+<rdar://problem/3770558> Replace IP TTL 255 check with local subnet source address check
+
+Revision 1.13 2004/03/20 05:37:09 cheshire
+Fix contributed by Terry Lambert & Alfred Perlstein:
+Don't use uint8_t -- it requires stdint.h, which doesn't exist on FreeBSD 4.x
+
+Revision 1.12 2004/01/28 21:12:15 cheshire
+Reconcile mDNSIPv6Support & HAVE_IPV6 into a single flag (HAVE_IPV6)
+
+Revision 1.11 2003/12/13 05:43:09 bradley
+Fixed non-sa_len and non-IPv6 version of GET_SA_LEN macro to cast as sockaddr to access
+sa_family so it works with any sockaddr-compatible address structure (e.g. sockaddr_storage).
+
+Revision 1.10 2003/12/11 03:03:51 rpantos
+Clean up mDNSPosix so that it builds on OS X again.
+
+Revision 1.9 2003/12/08 20:47:02 rpantos
+Add support for mDNSResponder on Linux.
+
+Revision 1.8 2003/08/12 19:56:26 cheshire
+Update to APSL 2.0
+
+Revision 1.7 2003/08/06 18:20:51 cheshire
+Makefile cleanup
+
+Revision 1.6 2003/07/02 21:19:59 cheshire
+<rdar://problem/3313413> Update copyright notices, etc., in source code comments
+
+Revision 1.5 2003/03/13 03:46:21 cheshire
+Fixes to make the code build on Linux
+
+Revision 1.4 2002/12/23 22:13:32 jgraessl
+
+Reviewed by: Stuart Cheshire
+Initial IPv6 support for mDNSResponder.
+
+Revision 1.3 2002/09/21 20:44:53 zarzycki
+Added APSL info
+
+Revision 1.2 2002/09/19 04:20:44 cheshire
+Remove high-ascii characters that confuse some systems
+
+Revision 1.1 2002/09/17 06:24:35 cheshire
+First checkin
+
+*/
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifndef __mDNSUNP_h
+#define __mDNSUNP_h
+
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <net/if.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+
+#ifdef HAVE_LINUX
+#include <linux/socket.h>
+#endif
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+#ifdef NOT_HAVE_SOCKLEN_T
+ typedef unsigned int socklen_t;
+#endif
+
+#if !defined(_SS_MAXSIZE)
+#if HAVE_IPV6
+#define sockaddr_storage sockaddr_in6
+#else
+#define sockaddr_storage sockaddr
+#endif // HAVE_IPV6
+#endif // !defined(_SS_MAXSIZE)
+
+#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/usr/src/cmd/cmd-inet/usr.lib/mdnsd/multicast.xml b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/multicast.xml
new file mode 100644
index 0000000000..4ef679eaaa
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/multicast.xml
@@ -0,0 +1,144 @@
+<?xml version="1.0"?>
+<!DOCTYPE service_bundle SYSTEM "/usr/share/lib/xml/dtd/service_bundle.dtd.1">
+<!--
+ Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ Use is subject to license terms.
+
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License (the "License").
+ You may not use this file except in compliance with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+ NOTE: This service manifest is not editable; its contents will
+ be overwritten by package or patch operations, including
+ operating system upgrade. Make customizations in a different
+ file.
+
+#ident "%Z%%M% %I% %E% SMI"
+-->
+
+<service_bundle type='manifest' name='SUNWdsdr:multicast'>
+
+<service
+ name='network/dns/multicast'
+ type='service'
+ version='1'>
+
+ <dependency
+ name='loopback'
+ grouping='require_all'
+ restart_on='none'
+ type='service'>
+ <service_fmri value='svc:/network/loopback' />
+ </dependency>
+
+ <dependency name='net-physical'
+ grouping='require_all'
+ restart_on='none'
+ type='service'>
+ <service_fmri value='svc:/network/physical' />
+ </dependency>
+
+ <dependency
+ name='identity'
+ grouping='optional_all'
+ restart_on='refresh'
+ type='service'>
+ <service_fmri value='svc:/system/identity:node' />
+ </dependency>
+
+ <dependency
+ name='system-log'
+ grouping='optional_all'
+ restart_on='none'
+ type='service'>
+ <service_fmri value='svc:/system/system-log' />
+ </dependency>
+
+ <instance name='default' enabled='false' >
+
+ <exec_method
+ type='method'
+ name='start'
+ exec='/usr/lib/inet/mdnsd'
+ timeout_seconds='60'>
+ <method_context>
+ <method_credential user='root' group='root' />
+ </method_context>
+ </exec_method>
+
+ <exec_method
+ type='method'
+ name='stop'
+ exec=':kill'
+ timeout_seconds='60' />
+
+ <!-- to start stop mdns service -->
+ <property_group name='general' type='framework'>
+ <propval name='action_authorization' type='astring'
+ value='solaris.smf.manage.mdns' />
+ <propval name='value_authorization' type='astring'
+ value='solaris.smf.manage.mdns' />
+ </property_group>
+
+ <!-- Properties in this group are used by nss_mdns module -->
+ <property_group name='nss_mdns_config' type='application'>
+ <stability value='Unstable' />
+
+ <!-- mDNS domain search property list. Users must
+ explicitly add mDNS search option values and
+ none are provided here by default.
+ <property name='search' type='astring'>
+ <astring_list>
+ <value_node value='local'/>
+ </astring_list>
+ </property>
+ -->
+ <property name='domain' type='astring'>
+ <astring_list>
+ <value_node value='local'/>
+ <value_node value='254.169.in-addr.arpa'/>
+ <value_node value='8.e.f.ip6.arpa'/>
+ <value_node value='9.e.f.ip6.arpa'/>
+ <value_node value='a.e.f.ip6.arpa'/>
+ <value_node value='b.e.f.ip6.arpa'/>
+ </astring_list>
+ </property>
+ <propval name='value_authorization' type='astring'
+ value='solaris.smf.value.mdns' />
+ </property_group>
+
+ </instance>
+
+ <stability value='Unstable' />
+
+ <template>
+
+ <common_name>
+ <loctext xml:lang='C'>DNS Service Discovery and Multicast DNS
+ </loctext>
+ </common_name>
+ <documentation>
+ <manpage title='mdnsd' section='1M' />
+ <doc_link name='Service Discovery OpenSolaris Project Page'
+ uri="http://opensolaris.org/os/project/nwam/service-discovery/"/>
+ </documentation>
+
+ </template>
+</service>
+
+</service_bundle>
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/uDNS.c b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/uDNS.c
new file mode 100644
index 0000000000..ec2bf87479
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/uDNS.c
@@ -0,0 +1,5253 @@
+/* -*- 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.
+
+ Change History (most recent first):
+
+$Log: uDNS.c,v $
+Revision 1.230.2.1 2006/08/29 06:24:23 cheshire
+Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
+
+Revision 1.230 2006/06/29 03:02:44 cheshire
+<rdar://problem/4607042> mDNSResponder NXDOMAIN and CNAME support
+
+Revision 1.229 2006/03/02 22:03:41 cheshire
+<rdar://problem/4395331> Spurious warning "GetLargeResourceRecord: m->rec appears to be already in use"
+Refinement: m->rec.r.resrec.RecordType needs to be cleared *every* time around for loop, not just once at the end
+
+Revision 1.228 2006/02/26 00:54:42 cheshire
+Fixes to avoid code generation warning/error on FreeBSD 7
+
+Revision 1.227 2006/01/09 20:47:05 cheshire
+<rdar://problem/4395331> Spurious warning "GetLargeResourceRecord: m->rec appears to be already in use"
+
+Revision 1.226 2005/12/20 02:46:33 cheshire
+<rdar://problem/4175520> mDNSPosix wide-area registration broken
+Check too strict -- we can still do wide-area registration (without NAT-PMP)
+without having to know our gateway address
+
+Revision 1.225 2005/10/21 22:51:17 cheshire
+<rdar://problem/4290265> Add check to avoid crashing NAT gateways that have buggy DNS relay code
+Refinement: Shorten "check-for-broken-dns-relay" to just "dnsbugtest"
+to avoid crashing NAT gateways that have a different DNS relay bug
+
+Revision 1.224 2005/10/20 00:10:33 cheshire
+<rdar://problem/4290265> Add check to avoid crashing NAT gateways that have buggy DNS relay code
+
+Revision 1.223 2005/10/17 18:52:42 cheshire
+<rdar://problem/4271183> mDNSResponder crashed in CheckRecordRegistrations
+Move code to unregister the service's extra records from uDNS_DeregisterService() to unlinkSRS().
+
+Revision 1.222 2005/10/05 23:04:10 cheshire
+Add more information to unlinkAR and startLLQHandshakeCallback error messages
+
+Revision 1.221 2005/10/05 17:27:48 herscher
+<rdar://problem/4272516> Change 200ms delay to 10ms
+
+Revision 1.220 2005/09/24 01:10:09 cheshire
+Fix comment typos
+
+Revision 1.219 2005/09/22 07:28:25 herscher
+Double the delay to 200000 usec after sending out a DNS query
+
+Revision 1.218 2005/09/13 01:06:14 herscher
+<rdar://problem/4248878> Add 100ms delay in sendQuery.
+
+Revision 1.217 2005/08/04 18:08:24 cheshire
+Update comments
+
+Revision 1.216 2005/07/29 23:05:22 ksekar
+<rdar://problem/4137930> Hostname registration should register IPv6 AAAA record with DNS Update
+Services should point to IPv6 address if IPv4 NAT mapping fails
+
+Revision 1.215 2005/07/29 21:01:51 ksekar
+<rdar://problem/4137930> Hostname registration should register IPv6 AAAA record with DNS Update
+correction to original checkin - misplaced return in HostnameCallback and logic error determining v6 changes
+
+Revision 1.214 2005/07/29 19:46:10 ksekar
+<rdar://problem/4191860> reduce polling period on failed LLQs to 15 minutes
+
+Revision 1.213 2005/07/29 18:04:22 ksekar
+<rdar://problem/4137930> Hostname registration should register IPv6 AAAA record with DNS Update
+
+Revision 1.212 2005/07/22 19:35:50 ksekar
+<rdar://problem/4188821> SUTiger: LLQ event acknowledgments are not formated correctly
+
+Revision 1.211 2005/07/21 18:51:04 ksekar
+<rdar://problem/4103136> mDNSResponder times out when mapping ports after sleep
+
+Revision 1.210 2005/07/21 18:47:31 ksekar
+<rdar://problem/4137283> NAT-PMP refresh Requested Public Port should contain actual mapped port
+
+Revision 1.209 2005/07/04 21:16:37 cheshire
+Minor code tidying -- initialize variables where they are declared
+
+Revision 1.208 2005/06/28 00:24:28 ksekar
+<rdar://problem/4157823> memory smasher in conQueryCallback
+
+Revision 1.207 2005/05/13 20:45:10 ksekar
+<rdar://problem/4074400> Rapid wide-area txt record updates don't work
+
+Revision 1.206 2005/03/31 02:19:55 cheshire
+<rdar://problem/4021486> Fix build warnings
+Reviewed by: Scott Herscher
+
+Revision 1.205 2005/03/21 00:33:51 shersche
+<rdar://problem/4021486> Fix build warnings on Win32 platform
+
+Revision 1.204 2005/03/16 00:42:32 ksekar
+<rdar://problem/4012279> Long-lived queries not working on Windows
+
+Revision 1.203 2005/03/04 03:00:03 ksekar
+<rdar://problem/4026546> Retransmissions happen too early, causing registrations to conflict with themselves
+
+Revision 1.202 2005/03/01 19:29:17 ksekar
+changed LogMsgs to debugfs
+
+Revision 1.201 2005/02/26 03:04:13 cheshire
+<rdar://problem/4017292> Should not indicate successful dynamic update if no network connection
+Don't try to do updates to root name server. This ensures status dot turns red if user
+enters a bad host name such as just "fred" instead of a properly fully-qualified name.
+
+Revision 1.200 2005/02/25 17:47:45 ksekar
+<rdar://problem/4021868> SendServiceRegistration fails on wake from sleep
+
+Revision 1.199 2005/02/25 04:21:00 cheshire
+<rdar://problem/4015377> mDNS -F returns the same domain multiple times with different casing
+
+Revision 1.198 2005/02/25 02:35:22 cheshire
+<rdar://problem/4017292> Should not indicate successful dynamic update if no network connection
+If we get NXDomain error looking for the _dns-update._udp record,
+update status from 1 (in progress) to mStatus_NoSuchNameErr (failed)
+
+Revision 1.197 2005/02/24 21:56:59 ksekar
+Change LogMsgs to debugfs
+
+Revision 1.196 2005/02/24 21:52:28 ksekar
+<rdar://problem/3922768> Remove "deferred deregistration" logic for hostnames
+
+Revision 1.195 2005/02/22 17:53:08 ksekar
+Changed successful NAT Traversals from LogMsg to LogOperation
+
+Revision 1.194 2005/02/15 18:38:03 ksekar
+<rdar://problem/3967876> change expected/redundant log messages to debugfs.
+
+Revision 1.193 2005/02/15 01:17:48 ksekar
+Fixed build failure.
+
+Revision 1.192 2005/02/14 23:01:28 ksekar
+Refinement to previous checkin - don't log bad LLQ opcode if we had to send the request more than once.
+
+Revision 1.191 2005/02/14 18:26:51 ksekar
+<rdar://problem/4005569> mDNSResponder complains about bad LLQ Opcode 2
+
+Revision 1.190 2005/02/11 19:44:06 shersche
+Remove extra semicolon at end of line
+
+Revision 1.189 2005/02/10 21:07:02 ksekar
+Don't goto error in ReceiveNATAddrResponse if we receive a malformatted response
+
+Revision 1.188 2005/02/10 02:02:44 ksekar
+Remove double semi-colon
+
+Revision 1.187 2005/02/09 23:28:01 ksekar
+<rdar://problem/3984374> NAT-PMP response callback should return a
+boolean indicating if the packet matched the request
+
+Revision 1.186 2005/02/04 21:56:29 ksekar
+<rdar://problem/3984374> Simultaneous port map requests sometimes fail
+- Refinement to previous checkin.
+
+Revision 1.185 2005/02/03 23:48:22 ksekar
+<rdar://problem/3984374> Simultaneous port map requests sometimes fail
+
+Revision 1.184 2005/02/01 19:33:29 ksekar
+<rdar://problem/3985239> Keychain format too restrictive
+
+Revision 1.183 2005/01/27 22:57:55 cheshire
+Fix compile errors on gcc4
+
+Revision 1.182 2005/01/25 18:55:05 ksekar
+Shortened log message
+
+Revision 1.181 2005/01/25 02:17:32 cheshire
+<rdar://problem/3971263> Don't use query ID zero in uDNS queries
+
+Revision 1.180 2005/01/19 21:01:54 ksekar
+<rdar://problem/3955355> uDNS needs to support subtype registration and browsing
+
+Revision 1.179 2005/01/19 19:15:35 ksekar
+Refinement to <rdar://problem/3954575> - Simplify mDNS_PurgeResultsForDomain logic and move into daemon layer
+
+Revision 1.178 2005/01/17 23:47:58 cheshire
+<rdar://problem/3904954> Wide-area services not found on little-endian
+
+Revision 1.177 2005/01/17 23:41:26 cheshire
+Fix compile errors
+
+Revision 1.176 2005/01/17 21:03:04 cheshire
+<rdar://problem/3904954> Wide-area services not found on little-endian
+
+Revision 1.175 2005/01/15 00:56:41 ksekar
+<rdar://problem/3954575> Unicast services don't disappear when logging
+out of VPN
+
+Revision 1.174 2005/01/14 18:44:28 ksekar
+<rdar://problem/3954609> mDNSResponder is crashing when changing domains
+
+Revision 1.173 2005/01/14 18:34:22 ksekar
+<rdar://problem/3954571> Services registered outside of firewall don't succeed after location change
+
+Revision 1.172 2005/01/11 22:50:52 ksekar
+Fixed constant naming (was using kLLQ_DefLease for update leases)
+
+Revision 1.171 2005/01/10 04:52:49 ksekar
+Changed LogMsg to debugf
+
+Revision 1.170 2005/01/08 00:50:05 ksekar
+Fixed spelling mistake in log msg
+
+Revision 1.169 2005/01/08 00:42:18 ksekar
+<rdar://problem/3922758> Clean up syslog messages
+
+Revision 1.168 2004/12/23 23:22:47 ksekar
+<rdar://problem/3933606> Unicast known answers "name" pointers point to garbage stack memory
+
+Revision 1.167 2004/12/22 22:25:47 ksekar
+<rdar://problem/3734265> NATPMP: handle location changes
+
+Revision 1.166 2004/12/22 00:04:12 ksekar
+<rdar://problem/3930324> mDNSResponder crashing in ReceivePortMapReply
+
+Revision 1.165 2004/12/18 03:14:22 cheshire
+DblNAT -> DoubleNAT
+
+Revision 1.164 2004/12/17 03:55:40 ksekar
+Don't use -1 as special meaning for expiration timer (it is a valid
+value, and is redundant with our state variables)
+
+Revision 1.163 2004/12/17 03:51:53 ksekar
+<rdar://problem/3920991> Don't update TXT record if service registration fails
+
+Revision 1.162 2004/12/17 01:29:11 ksekar
+<rdar://problem/3920598> Questions can go deaf on location changes
+
+Revision 1.161 2004/12/16 20:42:02 cheshire
+Fix compiler warnings
+
+Revision 1.160 2004/12/16 20:13:00 cheshire
+<rdar://problem/3324626> Cache memory management improvements
+
+Revision 1.159 2004/12/15 02:11:22 ksekar
+<rdar://problem/3917317> Don't check for Dynamic DNS hostname uniqueness
+
+Revision 1.158 2004/12/15 02:04:28 ksekar
+Refinement to previous checkin - we should still return NatTraversal error when the port mapping fails
+
+Revision 1.157 2004/12/15 01:39:21 ksekar
+Refinement to previous checkin - we should still return NatTraversal error when the port mapping fails
+
+Revision 1.156 2004/12/15 01:18:57 ksekar
+<rdar://problem/3825979> Call DeregisterService on nat port map failure
+
+Revision 1.155 2004/12/14 21:21:20 ksekar
+<rdar://problem/3825979> NAT-PMP: Update response format to contain "Seconds Since Boot"
+
+Revision 1.154 2004/12/14 20:52:27 cheshire
+Add question->qnamehash and cr->resrec.namehash to log message
+
+Revision 1.153 2004/12/14 20:45:02 cheshire
+Improved error logging in "unexpected answer" message
+
+Revision 1.152 2004/12/14 03:02:10 ksekar
+<rdar://problem/3919016> Rare race condition can cause crash
+
+Revision 1.151 2004/12/13 21:45:08 ksekar
+uDNS_DeregisterService should return NoError if called twice (to follow mDNS behavior expected by daemon layer)
+
+Revision 1.150 2004/12/13 20:42:41 ksekar
+Fixed LogMsg
+
+Revision 1.149 2004/12/13 18:10:03 ksekar
+Fixed LogMsg
+
+Revision 1.148 2004/12/13 01:18:04 ksekar
+Fixed unused variable warning for non-debug builds
+
+Revision 1.147 2004/12/12 23:51:42 ksekar
+<rdar://problem/3845683> Wide-area registrations should fallback to using DHCP hostname as target
+
+Revision 1.146 2004/12/12 23:30:40 ksekar
+<rdar://problem/3916987> Extra RRs not properly unlinked when parent service registration fails
+
+Revision 1.145 2004/12/12 22:56:29 ksekar
+<rdar://problem/3668508> Need to properly handle duplicate long-lived queries
+
+Revision 1.144 2004/12/11 20:55:29 ksekar
+<rdar://problem/3916479> Clean up registration state machines
+
+Revision 1.143 2004/12/10 01:21:27 cheshire
+<rdar://problem/3914089> Get rid of "LLQ Responses over TCP not currently supported" message
+
+Revision 1.142 2004/12/08 02:03:31 ksekar
+<rdar://problem/3865124> Looping on NAT Traversal error - check for
+NULL RR on error
+
+Revision 1.141 2004/12/07 01:39:28 cheshire
+Don't fail if the same server is responsible for more than one domain
+(e.g. the same DNS server may be responsible for both apple.com. and 17.in-addr.arpa.)
+
+Revision 1.140 2004/12/06 21:15:22 ksekar
+<rdar://problem/3884386> mDNSResponder crashed in CheckServiceRegistrations
+
+Revision 1.139 2004/12/06 19:08:03 cheshire
+Add clarifying comment -- CountLabels() excludes the final root label.
+
+Revision 1.138 2004/12/06 01:45:54 ksekar
+Correct wording in LogMsg
+
+Revision 1.137 2004/12/03 20:40:35 ksekar
+<rdar://problem/3865124> Looping on NAT Traversal error
+
+Revision 1.136 2004/12/03 07:20:50 ksekar
+<rdar://problem/3674208> Wide-Area: Registration of large TXT record fails
+
+Revision 1.135 2004/12/03 05:18:33 ksekar
+<rdar://problem/3810596> mDNSResponder needs to return more specific TSIG errors
+
+Revision 1.134 2004/12/02 20:03:49 ksekar
+<rdar://problem/3889647> Still publishes wide-area domains even after switching to a local subnet
+
+Revision 1.133 2004/12/02 18:37:52 ksekar
+<rdar://problem/3758233> Registering with port number zero should not create a port mapping
+
+Revision 1.132 2004/12/01 20:57:19 ksekar
+<rdar://problem/3873921> Wide Area Service Discovery must be split-DNS aware
+
+Revision 1.131 2004/12/01 19:59:27 cheshire
+<rdar://problem/3882643> Crash in mDNSPlatformTCPConnect
+If a TCP response has the TC bit set, don't respond by just trying another TCP connection
+
+Revision 1.130 2004/12/01 02:43:23 cheshire
+Don't call StatusCallback if function pointer is null
+
+Revision 1.129 2004/11/30 23:51:06 cheshire
+Remove double semicolons
+
+Revision 1.128 2004/11/25 01:48:30 ksekar
+<rdar://problem/3878991> Logging into VPN does not trigger registration of address record
+
+Revision 1.127 2004/11/25 01:41:36 ksekar
+Changed unnecessary LogMsgs to debugfs
+
+Revision 1.126 2004/11/23 23:54:17 ksekar
+<rdar://problem/3890318> Wide-Area DNSServiceRegisterRecord() failures
+can crash mDNSResponder
+
+Revision 1.125 2004/11/23 04:16:48 cheshire
+Removed receiveMsg() routine.
+
+Revision 1.124 2004/11/23 04:06:51 cheshire
+Get rid of floating point constant -- in a small embedded device, bringing in all
+the floating point libraries just to halve an integer value is a bit too heavyweight.
+
+Revision 1.123 2004/11/22 17:16:20 ksekar
+<rdar://problem/3854298> Unicast services don't disappear when you disable all networking
+
+Revision 1.122 2004/11/19 18:00:34 ksekar
+<rdar://problem/3682646> Security: use random ID for one-shot unicast queries
+
+Revision 1.121 2004/11/19 04:24:08 ksekar
+<rdar://problem/3682609> Security: Enforce a "window" on one-shot wide-area queries
+
+Revision 1.120 2004/11/19 02:32:43 ksekar
+<rdar://problem/3682608> Wide-Area Security: Add LLQ-ID to events
+
+Revision 1.119 2004/11/18 23:21:24 ksekar
+<rdar://problem/3764544> LLQ Security: Need to verify src port/address for LLQ handshake
+
+Revision 1.118 2004/11/18 22:58:37 ksekar
+Removed old comment.
+
+Revision 1.117 2004/11/18 18:04:21 ksekar
+Restore checkins lost due to repository disk failure: Update comments & <rdar://problem/3880688>
+
+Revision 1.xxx 2004/11/17 06:17:57 cheshire
+Update comments to show correct SRV names: _dns-update._udp.<zone>. and _dns-llq._udp.<zone>.
+
+Revision 1.xxx 2004/11/17 00:45:28 ksekar
+<rdar://problem/3880688> Result of putUpdateLease not error-checked
+
+Revision 1.116 2004/11/16 01:41:47 ksekar
+Fixed typo in debugf
+
+Revision 1.115 2004/11/15 20:09:24 ksekar
+<rdar://problem/3719050> Wide Area support for Add/Remove record
+
+Revision 1.114 2004/11/13 02:32:47 ksekar
+<rdar://problem/3868216> LLQ mobility fragile on non-primary interface
+- fixed incorrect state comparison in CheckQueries
+
+Revision 1.113 2004/11/13 02:29:52 ksekar
+<rdar://problem/3878386> LLQ refreshes not reliable
+
+Revision 1.112 2004/11/11 20:45:14 ksekar
+<rdar://problem/3876052> self-conflict test not compatible with some BIND servers
+
+Revision 1.111 2004/11/11 20:14:55 ksekar
+<rdar://problem/3719574> Wide-Area registrations not deregistered on sleep
+
+Revision 1.110 2004/11/10 23:53:53 ksekar
+Remove no longer relevant comment
+
+Revision 1.109 2004/11/10 20:40:53 ksekar
+<rdar://problem/3868216> LLQ mobility fragile on non-primary interface
+
+Revision 1.108 2004/11/01 20:36:16 ksekar
+<rdar://problem/3802395> mDNSResponder should not receive Keychain Notifications
+
+Revision 1.107 2004/10/26 06:11:41 cheshire
+Add improved logging to aid in diagnosis of <rdar://problem/3842714> mDNSResponder crashed
+
+Revision 1.106 2004/10/26 03:52:03 cheshire
+Update checkin comments
+
+Revision 1.105 2004/10/26 01:15:06 cheshire
+Use "#if 0" instead of commenting out code
+
+Revision 1.104 2004/10/25 21:41:38 ksekar
+<rdar://problem/3852958> wide-area name conflicts can cause crash
+
+Revision 1.103 2004/10/25 19:30:52 ksekar
+<rdar://problem/3827956> Simplify dynamic host name structures
+
+Revision 1.102 2004/10/23 01:16:00 cheshire
+<rdar://problem/3851677> uDNS operations not always reliable on multi-homed hosts
+
+Revision 1.101 2004/10/22 20:52:07 ksekar
+<rdar://problem/3799260> Create NAT port mappings for Long Lived Queries
+
+Revision 1.100 2004/10/20 02:16:41 cheshire
+Improve "could not confirm existence of NS record" error message
+Don't call newRR->RecordCallback if it is NULL
+
+Revision 1.99 2004/10/19 21:33:18 cheshire
+<rdar://problem/3844991> Cannot resolve non-local registrations using the mach API
+Added flag 'kDNSServiceFlagsForceMulticast'. Passing through an interface id for a unicast name
+doesn't force multicast unless you set this flag to indicate explicitly that this is what you want
+
+Revision 1.98 2004/10/16 00:16:59 cheshire
+<rdar://problem/3770558> Replace IP TTL 255 check with local subnet source address check
+
+Revision 1.97 2004/10/15 23:00:18 ksekar
+<rdar://problem/3799242> Need to update LLQs on location changes
+
+Revision 1.96 2004/10/12 23:30:44 ksekar
+<rdar://problem/3609944> mDNSResponder needs to follow CNAME referrals
+
+Revision 1.95 2004/10/12 03:15:09 ksekar
+<rdar://problem/3835612> mDNS_StartQuery shouldn't return transient no-server error
+
+Revision 1.94 2004/10/12 02:49:20 ksekar
+<rdar://problem/3831228> Clean up LLQ sleep/wake, error handling
+
+Revision 1.93 2004/10/08 04:17:25 ksekar
+<rdar://problem/3831819> Don't use DNS extensions if the server does not advertise required SRV record
+
+Revision 1.92 2004/10/08 03:54:35 ksekar
+<rdar://problem/3831802> Refine unicast polling intervals
+
+Revision 1.91 2004/09/30 17:45:34 ksekar
+<rdar://problem/3821119> lots of log messages: mDNS_SetPrimaryIP: IP address unchanged
+
+Revision 1.90 2004/09/25 00:22:13 ksekar
+<rdar://problem/3815534> Crash in uDNS_RegisterService
+
+Revision 1.89 2004/09/24 19:14:53 cheshire
+Remove unused "extern mDNS mDNSStorage"
+
+Revision 1.88 2004/09/23 20:48:15 ksekar
+Clarify retransmission debugf messages.
+
+Revision 1.87 2004/09/22 00:41:59 cheshire
+Move tcp connection status codes into the legal range allocated for mDNS use
+
+Revision 1.86 2004/09/21 23:40:11 ksekar
+<rdar://problem/3810349> mDNSResponder to return errors on NAT traversal failure
+
+Revision 1.85 2004/09/21 22:38:27 ksekar
+<rdar://problem/3810286> PrimaryIP type uninitialized
+
+Revision 1.84 2004/09/18 00:30:39 cheshire
+<rdar://problem/3806643> Infinite loop in CheckServiceRegistrations
+
+Revision 1.83 2004/09/17 00:31:51 cheshire
+For consistency with ipv6, renamed rdata field 'ip' to 'ipv4'
+
+Revision 1.82 2004/09/16 21:36:36 cheshire
+<rdar://problem/3803162> Fix unsafe use of mDNSPlatformTimeNow()
+Changes to add necessary locking calls around unicast DNS operations
+
+Revision 1.81 2004/09/16 02:29:39 cheshire
+Moved mDNS_Lock/mDNS_Unlock to DNSCommon.c; Added necessary locking around
+uDNS_ReceiveMsg, uDNS_StartQuery, uDNS_UpdateRecord, uDNS_RegisterService
+
+Revision 1.80 2004/09/16 01:58:21 cheshire
+Fix compiler warnings
+
+Revision 1.79 2004/09/16 00:24:48 cheshire
+<rdar://problem/3803162> Fix unsafe use of mDNSPlatformTimeNow()
+
+Revision 1.78 2004/09/15 01:16:57 ksekar
+<rdar://problem/3797598> mDNSResponder printing too many messages
+
+Revision 1.77 2004/09/14 23:27:47 cheshire
+Fix compile errors
+
+Revision 1.76 2004/09/14 22:22:00 ksekar
+<rdar://problem/3800920> Legacy browses broken against some BIND versions
+
+Revision 1.75 2004/09/03 19:23:05 ksekar
+<rdar://problem/3788460>: Need retransmission mechanism for wide-area service registrations
+
+Revision 1.74 2004/09/02 17:49:04 ksekar
+<rdar://problem/3785135>: 8A246: mDNSResponder crash while logging on restart
+Fixed incorrect conversions, changed %s to %##s for all domain names.
+
+Revision 1.73 2004/09/02 01:39:40 cheshire
+For better readability, follow consistent convention that QR bit comes first, followed by OP bits
+
+Revision 1.72 2004/09/01 03:59:29 ksekar
+<rdar://problem/3783453>: Conditionally compile out uDNS code on Windows
+
+Revision 1.71 2004/08/27 17:51:53 ksekar
+Replaced unnecessary LogMsg with debugf.
+
+Revision 1.70 2004/08/25 00:37:27 ksekar
+<rdar://problem/3774635>: Cleanup DynDNS hostname registration code
+
+Revision 1.69 2004/08/18 17:35:41 ksekar
+<rdar://problem/3651443>: Feature #9586: Need support for Legacy NAT gateways
+
+Revision 1.68 2004/08/14 03:22:41 cheshire
+<rdar://problem/3762579> Dynamic DNS UI <-> mDNSResponder glue
+Add GetUserSpecifiedDDNSName() routine
+Convert ServiceRegDomain to domainname instead of C string
+Replace mDNS_GenerateFQDN/mDNS_GenerateGlobalFQDN with mDNS_SetFQDNs
+
+Revision 1.67 2004/08/13 23:46:58 cheshire
+"asyncronous" -> "asynchronous"
+
+Revision 1.66 2004/08/13 23:37:02 cheshire
+Now that we do both uDNS and mDNS, global replace "uDNS_info.hostname" with
+"uDNS_info.UnicastHostname" for clarity
+
+Revision 1.65 2004/08/13 23:12:32 cheshire
+Don't use strcpy() and strlen() on "struct domainname" objects;
+use AssignDomainName() and DomainNameLength() instead
+(A "struct domainname" is a collection of packed pascal strings, not a C string.)
+
+Revision 1.64 2004/08/13 23:01:05 cheshire
+Use platform-independent mDNSNULL instead of NULL
+
+Revision 1.63 2004/08/12 00:32:36 ksekar
+<rdar://problem/3759567>: LLQ Refreshes never terminate if unanswered
+
+Revision 1.62 2004/08/10 23:19:14 ksekar
+<rdar://problem/3722542>: DNS Extension daemon for Wide Area Service Discovery
+Moved routines/constants to allow extern access for garbage collection daemon
+
+Revision 1.61 2004/07/30 17:40:06 ksekar
+<rdar://problem/3739115>: TXT Record updates not available for wide-area services
+
+Revision 1.60 2004/07/29 19:40:05 ksekar
+NATPMP Support - minor fixes and cleanup
+
+Revision 1.59 2004/07/29 19:27:15 ksekar
+NATPMP Support - minor fixes and cleanup
+
+Revision 1.58 2004/07/27 07:35:38 shersche
+fix syntax error, variables declared in the middle of a block
+
+Revision 1.57 2004/07/26 22:49:30 ksekar
+<rdar://problem/3651409>: Feature #9516: Need support for NATPMP in client
+
+Revision 1.56 2004/07/26 19:14:44 ksekar
+<rdar://problem/3737814>: 8A210: mDNSResponder crashed in startLLQHandshakeCallback
+
+Revision 1.55 2004/07/15 19:01:33 ksekar
+<rdar://problem/3681029>: Check for incorrect time comparisons
+
+Revision 1.54 2004/06/22 02:10:53 ksekar
+<rdar://problem/3705433>: Lighthouse failure causes packet flood to DNS
+
+Revision 1.53 2004/06/17 20:49:09 ksekar
+<rdar://problem/3690436>: mDNSResponder crash while location cycling
+
+Revision 1.52 2004/06/17 01:13:11 ksekar
+<rdar://problem/3696616>: polling interval too short
+
+Revision 1.51 2004/06/10 04:36:44 cheshire
+Fix compiler warning
+
+Revision 1.50 2004/06/10 00:55:13 ksekar
+<rdar://problem/3686213>: crash on network reconnect
+
+Revision 1.49 2004/06/10 00:10:50 ksekar
+<rdar://problem/3686174>: Infinite Loop in uDNS_Execute()
+
+Revision 1.48 2004/06/09 20:03:37 ksekar
+<rdar://problem/3686163>: Incorrect copying of resource record in deregistration
+
+Revision 1.47 2004/06/09 03:48:28 ksekar
+<rdar://problem/3685226>: nameserver address fails with prod. Lighthouse server
+
+Revision 1.46 2004/06/09 01:44:30 ksekar
+<rdar://problem/3681378> reworked Cache Record copy code
+
+Revision 1.45 2004/06/08 18:54:47 ksekar
+<rdar://problem/3681378>: mDNSResponder leaks after exploring in Printer Setup Utility
+
+Revision 1.44 2004/06/05 00:33:51 cheshire
+<rdar://problem/3681029>: Check for incorrect time comparisons
+
+Revision 1.43 2004/06/05 00:14:44 cheshire
+Fix signed/unsigned and other compiler warnings
+
+Revision 1.42 2004/06/04 22:36:16 ksekar
+Properly set u->nextevent in uDNS_Execute
+
+Revision 1.41 2004/06/04 08:58:29 ksekar
+<rdar://problem/3668624>: Keychain integration for secure dynamic update
+
+Revision 1.40 2004/06/03 03:09:58 ksekar
+<rdar://problem/3668626>: Garbage Collection for Dynamic Updates
+
+Revision 1.39 2004/06/01 23:46:50 ksekar
+<rdar://problem/3675149>: DynDNS: dynamically look up LLQ/Update ports
+
+Revision 1.38 2004/05/31 22:19:44 ksekar
+<rdar://problem/3258021>: Feature: DNS server->client notification on
+record changes (#7805) - revert to polling mode on setup errors
+
+Revision 1.37 2004/05/28 23:42:37 ksekar
+<rdar://problem/3258021>: Feature: DNS server->client notification on record changes (#7805)
+
+Revision 1.36 2004/05/18 23:51:25 cheshire
+Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers
+
+Revision 1.35 2004/05/07 23:01:04 ksekar
+Cleaned up list traversal in deriveGoodbyes - removed unnecessary
+conditional assignment.
+
+Revision 1.34 2004/05/05 18:26:12 ksekar
+Periodically re-transmit questions if the send() fails. Include
+internal questions in retransmission.
+
+Revision 1.33 2004/05/05 17:40:06 ksekar
+Removed prerequisite from deregistration update - it does not work for
+shared records, and is unnecessary until we have more sophisticated
+name conflict management.
+
+Revision 1.32 2004/05/05 17:32:18 ksekar
+Prevent registration of loopback interface caused by removal of
+Multicast flag in interface structure.
+
+Revision 1.31 2004/05/05 17:05:02 ksekar
+Use LargeCacheRecord structs when pulling records off packets
+
+Revision 1.30 2004/04/16 21:33:27 ksekar
+Fixed bug in processing GetZoneData responses that do not use BIND formatting.
+
+Revision 1.29 2004/04/15 20:03:13 ksekar
+Clarified log message when pulling bad resource records off packet.
+
+Revision 1.28 2004/04/15 00:51:28 bradley
+Minor tweaks for Windows and C++ builds. Added casts for signed/unsigned integers and 64-bit pointers.
+Prefix some functions with mDNS to avoid conflicts. Disable benign warnings on Microsoft compilers.
+
+Revision 1.27 2004/04/14 23:09:28 ksekar
+Support for TSIG signed dynamic updates.
+
+Revision 1.26 2004/04/14 19:36:05 ksekar
+Fixed memory corruption error in deriveGoodbyes.
+
+Revision 1.25 2004/04/14 04:07:11 ksekar
+Fixed crash in IsActiveUnicastQuery(). Removed redundant checks in routine.
+
+Revision 1.24 2004/04/08 09:41:40 bradley
+Added const to AuthRecord in deadvertiseIfCallback to match callback typedef.
+
+Revision 1.23 2004/03/24 00:29:45 ksekar
+Make it safe to call StopQuery in a unicast question callback
+
+Revision 1.22 2004/03/19 10:11:09 bradley
+Added AuthRecord * cast from umalloc for C++ builds.
+
+Revision 1.21 2004/03/15 02:03:45 bradley
+Added const to params where needed to match prototypes. Changed SetNewRData calls to use 0 instead
+of -1 for unused size to fix warning. Disable assignment within conditional warnings with Visual C++.
+
+Revision 1.20 2004/03/13 02:07:26 ksekar
+<rdar://problem/3192546>: DynDNS: Dynamic update of service records
+
+Revision 1.19 2004/03/13 01:57:33 ksekar
+<rdar://problem/3192546>: DynDNS: Dynamic update of service records
+
+Revision 1.18 2004/02/21 08:34:15 bradley
+Added casts from void * to specific type for C++ builds. Changed void * l-value cast
+r-value cast to fix problems with VC++ builds. Removed empty switch to fix VC++ error.
+
+Revision 1.17 2004/02/21 02:06:24 cheshire
+Can't use anonymous unions -- they're non-standard and don't work on all compilers
+
+Revision 1.16 2004/02/12 01:51:45 cheshire
+Don't try to send uDNS queries unless we have at least one uDNS server available
+
+Revision 1.15 2004/02/10 03:02:46 cheshire
+Fix compiler warning
+
+Revision 1.14 2004/02/06 23:04:19 ksekar
+Basic Dynamic Update support via mDNS_Register (dissabled via
+UNICAST_REGISTRATION #define)
+
+Revision 1.13 2004/02/03 22:15:01 ksekar
+Fixed nameToAddr error check: don't abort state machine on nxdomain error.
+
+Revision 1.12 2004/02/03 19:47:36 ksekar
+Added an asynchronous state machine mechanism to uDNS.c, including
+calls to find the parent zone for a domain name. Changes include code
+in repository previously dissabled via "#if 0 incomplete". Codepath
+is currently unused, and will be called to create update records, etc.
+
+Revision 1.11 2004/01/30 02:12:30 ksekar
+Changed uDNS_ReceiveMsg() to correctly return void.
+
+Revision 1.10 2004/01/29 02:59:17 ksekar
+Unicast DNS: Changed from a resource record oriented question/response
+matching to packet based matching. New callback architecture allows
+collections of records in a response to be processed differently
+depending on the nature of the request, and allows the same structure
+to be used for internal and client-driven queries with different processing needs.
+
+Revision 1.9 2004/01/28 20:20:45 ksekar
+Unified ActiveQueries and ActiveInternalQueries lists, using a flag to
+demux them. Check-in includes work-in-progress code, #ifdef'd out.
+
+Revision 1.8 2004/01/28 02:30:07 ksekar
+Added default Search Domains to unicast browsing, controlled via
+Networking sharing prefs pane. Stopped sending unicast messages on
+every interface. Fixed unicast resolving via mach-port API.
+
+Revision 1.7 2004/01/27 20:15:22 cheshire
+<rdar://problem/3541288>: Time to prune obsolete code for listening on port 53
+
+Revision 1.6 2004/01/24 23:47:17 cheshire
+Use mDNSOpaque16fromIntVal() instead of shifting and masking
+
+Revision 1.5 2004/01/24 04:59:15 cheshire
+Fixes so that Posix/Linux, OS9, Windows, and VxWorks targets build again
+
+Revision 1.4 2004/01/24 04:19:26 cheshire
+Restore overwritten checkin 1.2
+
+Revision 1.3 2004/01/23 23:23:15 ksekar
+Added TCP support for truncated unicast messages.
+
+Revision 1.2 2004/01/22 03:48:41 cheshire
+Make sure uDNS client doesn't accidentally use query ID zero
+
+Revision 1.1 2003/12/13 03:05:27 ksekar
+<rdar://problem/3192548>: DynDNS: Unicast query of service records
+
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#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
+
+#define umalloc(x) mDNSPlatformMemAllocate(x) // short hands for common routines
+#define ufree(x) mDNSPlatformMemFree(x)
+#define ubzero(x,y) mDNSPlatformMemZero(x,y)
+#define umemcpy(x, y, l) mDNSPlatformMemCopy(y, x, l) // uses memcpy(2) arg ordering
+
+// Asynchronous operation types
+
+typedef enum
+ {
+ zoneDataResult
+ // other async. operation names go here
+ } AsyncOpResultType;
+
+typedef struct
+ {
+ domainname zoneName;
+ mDNSAddr primaryAddr;
+ mDNSu16 zoneClass;
+ mDNSIPPort llqPort;
+ mDNSIPPort updatePort;
+ } zoneData_t;
+
+// other async. result struct defs go here
+
+typedef struct
+ {
+ AsyncOpResultType type;
+ zoneData_t zoneData;
+ // other async result structs go here
+ } AsyncOpResult;
+
+typedef void AsyncOpCallback(mStatus err, mDNS *const m, void *info, const AsyncOpResult *result);
+
+
+// Private Function Prototypes
+// Note: In general, functions are ordered such that they do not require forward declarations.
+// However, prototypes are used where cyclic call graphs exist (e.g. foo calls bar, and bar calls
+// foo), or when they aid in the grouping or readability of code (e.g. state machine code that is easier
+// read top-to-bottom.)
+
+mDNSlocal mDNSBool FreeNATInfo(mDNS *m, NATTraversalInfo *n);
+mDNSlocal void hndlTruncatedAnswer(DNSQuestion *question, const mDNSAddr *src, mDNS *m);
+mDNSlocal mStatus startGetZoneData(domainname *name, mDNS *m, mDNSBool findUpdatePort, mDNSBool findLLQPort, AsyncOpCallback callback, void *callbackInfo);
+mDNSlocal mDNSBool recvLLQResponse(mDNS *m, DNSMessage *msg, const mDNSu8 *end, const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSInterfaceID InterfaceID);
+mDNSlocal void sendRecordRegistration(mDNS *const m, AuthRecord *rr);
+mDNSlocal void SendServiceRegistration(mDNS *m, ServiceRecordSet *srs);
+mDNSlocal void SendServiceDeregistration(mDNS *m, ServiceRecordSet *srs);
+mDNSlocal void serviceRegistrationCallback(mStatus err, mDNS *const m, void *srsPtr, const AsyncOpResult *result);
+mDNSlocal void SuspendLLQs(mDNS *m, mDNSBool DeregisterActive);
+mDNSlocal void RestartQueries(mDNS *m);
+mDNSlocal void startLLQHandshake(mDNS *m, LLQ_Info *info, mDNSBool defer);
+mDNSlocal void llqResponseHndlr(mDNS * const m, DNSMessage *msg, const mDNSu8 *end, DNSQuestion *question, void *context);
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark - Temporary workaround
+#endif
+
+// 17 Places in this file directly call mDNSPlatformTimeNow(), which is unsafe
+// The platform function is now called mDNSPlatformRawTime(), and
+// mDNSPlatformTimeNow() is defined here as a temporary workaround.
+// This is a gross hack, and after this change has been tested for a while,
+// all these calls should be replaced by simple references to m->timenow
+
+mDNSlocal mDNSs32 mDNSPlatformTimeNow(mDNS *m)
+ {
+ if (m->mDNS_busy && m->timenow) return(m->timenow);
+ LogMsg("ERROR: uDNS.c code executing without holding main mDNS lock");
+
+ // To get a quick and easy stack trace to find out *how* this routine
+ // is being called without holding main mDNS lock, uncomment the line below:
+ // *(long*)0=0;
+
+ return(mDNS_TimeNow(m));
+ }
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark - General Utility Functions
+#endif
+
+// CountLabels() returns number of labels in name, excluding final root label
+// (e.g. for "apple.com." CountLabels returns 2.)
+mDNSlocal int CountLabels(const domainname *d)
+ {
+ int count = 0;
+ const mDNSu8 *ptr;
+
+ for (ptr = d->c; *ptr; ptr = ptr + ptr[0] + 1) count++;
+ return count;
+ }
+
+mDNSlocal mDNSOpaque16 newMessageID(uDNS_GlobalInfo *u)
+ {
+ static mDNSBool randomized = mDNSfalse;
+
+ if (!randomized) { u->NextMessageID = (mDNSu16)mDNSRandom(0xFFFF); randomized = mDNStrue; }
+ if (u->NextMessageID == 0) u->NextMessageID++;
+ return mDNSOpaque16fromIntVal(u->NextMessageID++);
+ }
+
+// unlink an AuthRecord from a linked list
+mDNSlocal mStatus unlinkAR(AuthRecord **list, AuthRecord *const rr)
+ {
+ while (*list && *list != rr) list = &(*list)->next;
+ if (*list) { *list = rr->next; rr->next = mDNSNULL; return(mStatus_NoError); }
+ LogMsg("ERROR: unlinkAR - no such active record %##s", rr->resrec.name->c);
+ return(mStatus_NoSuchRecord);
+ }
+
+mDNSlocal void unlinkSRS(mDNS *m, ServiceRecordSet *srs)
+ {
+ uDNS_GlobalInfo *u = &m->uDNS_info;
+ ServiceRecordSet **p;
+ NATTraversalInfo *n = u->NATTraversals;
+
+ // verify that no NAT objects reference this service
+ while (n)
+ {
+ if (n->reg.ServiceRegistration == srs)
+ {
+ NATTraversalInfo *tmp = n;
+ n = n->next;
+ LogMsg("ERROR: Unlinking service record set %##s still referenced by NAT traversal object!", srs->RR_SRV.resrec.name->c);
+ FreeNATInfo(m, tmp);
+ }
+ else n = n->next;
+ }
+
+ for (p = &u->ServiceRegistrations; *p; p = &(*p)->next)
+ if (*p == srs)
+ {
+ ExtraResourceRecord *e;
+ *p = srs->next;
+ srs->next = mDNSNULL;
+ for (e=srs->Extras; e; e=e->next)
+ if (unlinkAR(&u->RecordRegistrations, &e->r))
+ LogMsg("unlinkSRS: extra record %##s not found", e->r.resrec.name->c);
+ return;
+ }
+ LogMsg("ERROR: unlinkSRS - SRS not found in ServiceRegistrations list %##s", srs->RR_SRV.resrec.name->c);
+ }
+
+mDNSlocal void LinkActiveQuestion(uDNS_GlobalInfo *u, DNSQuestion *q)
+ {
+ if (uDNS_IsActiveQuery(q, u))
+ { LogMsg("LinkActiveQuestion - %##s (%d) already in list!", q->qname.c, q->qtype); return; }
+
+ q->next = u->ActiveQueries;
+ u->ActiveQueries = q;
+ }
+
+// set retry timestamp for record with exponential backoff
+// (for service record sets, use RR_SRV as representative for time checks
+mDNSlocal void SetRecordRetry(mDNS *const m, AuthRecord *rr, mStatus SendErr)
+ {
+ rr->LastAPTime = mDNSPlatformTimeNow(m);
+ if (SendErr == mStatus_TransientErr || rr->ThisAPInterval < INIT_UCAST_POLL_INTERVAL) rr->ThisAPInterval = INIT_UCAST_POLL_INTERVAL;
+ else if (rr->ThisAPInterval*2 <= MAX_UCAST_POLL_INTERVAL) rr->ThisAPInterval *= 2;
+ else if (rr->ThisAPInterval != MAX_UCAST_POLL_INTERVAL) rr->ThisAPInterval = MAX_UCAST_POLL_INTERVAL;
+ }
+
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark - Name Server List Management
+#endif
+
+mDNSexport void mDNS_AddDNSServer(mDNS *const m, const mDNSAddr *addr, const domainname *d)
+ {
+ uDNS_GlobalInfo *u = &m->uDNS_info;
+ DNSServer *s, **p = &u->Servers;
+
+ mDNS_Lock(m);
+ if (!d) d = (domainname *)"";
+
+ while (*p) // Check if we already have this {server,domain} pair registered
+ {
+ if (mDNSSameAddress(&(*p)->addr, addr) && SameDomainName(&(*p)->domain, d))
+ LogMsg("Note: DNS Server %#a for domain %##s registered more than once", addr, d->c);
+ p=&(*p)->next;
+ }
+
+ // allocate, add to list
+ s = umalloc(sizeof(*s));
+ if (!s) { LogMsg("Error: mDNS_AddDNSServer - malloc"); goto end; }
+ s->addr = *addr;
+ s->del = mDNSfalse;
+ s->teststate = DNSServer_Untested;
+ AssignDomainName(&s->domain, d);
+ s->next = mDNSNULL;
+ *p = s;
+
+ end:
+ mDNS_Unlock(m);
+ }
+
+mDNSexport void mDNS_DeleteDNSServers(mDNS *const m)
+ {
+ DNSServer *s;
+ mDNS_Lock(m);
+
+ s = m->uDNS_info.Servers;
+ m->uDNS_info.Servers = mDNSNULL;
+ while (s)
+ {
+ DNSServer *tmp = s;
+ s = s->next;
+ ufree(tmp);
+ }
+
+ mDNS_Unlock(m);
+ }
+
+ // ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark - authorization management
+#endif
+
+mDNSlocal uDNS_AuthInfo *GetAuthInfoForName(const uDNS_GlobalInfo *u, const domainname *name)
+ {
+ uDNS_AuthInfo *ptr;
+ while (name->c[0])
+ {
+ for (ptr = u->AuthInfoList; ptr; ptr = ptr->next)
+ if (SameDomainName(&ptr->zone, name)) return(ptr);
+ name = (const domainname *)(name->c + 1 + name->c[0]);
+ }
+ return mDNSNULL;
+ }
+
+mDNSlocal void DeleteAuthInfoForZone(uDNS_GlobalInfo *u, const domainname *zone)
+ {
+ uDNS_AuthInfo *ptr, *prev = mDNSNULL;
+
+ for (ptr = u->AuthInfoList; ptr; ptr = ptr->next)
+ {
+ if (SameDomainName(&ptr->zone, zone))
+ {
+ if (prev) prev->next = ptr->next;
+ else u->AuthInfoList = ptr->next;
+ ufree(ptr);
+ return;
+ }
+ prev = ptr;
+ }
+ }
+
+mDNSexport mStatus mDNS_SetSecretForZone(mDNS *m, const domainname *zone, const domainname *key, const char *sharedSecret)
+ {
+ uDNS_AuthInfo *info;
+ mDNSu8 keybuf[1024];
+ mDNSs32 keylen;
+ uDNS_GlobalInfo *u = &m->uDNS_info;
+ mStatus status = mStatus_NoError;
+
+ mDNS_Lock(m);
+
+ if (GetAuthInfoForName(u, zone)) DeleteAuthInfoForZone(u, zone);
+ if (!key) goto exit;
+
+ info = (uDNS_AuthInfo*)umalloc(sizeof(*info));
+ if (!info) { LogMsg("ERROR: umalloc"); status = mStatus_NoMemoryErr; goto exit; }
+ ubzero(info, sizeof(*info));
+ AssignDomainName(&info->zone, zone);
+ AssignDomainName(&info->keyname, key);
+
+ keylen = DNSDigest_Base64ToBin(sharedSecret, keybuf, 1024);
+ if (keylen < 0)
+ {
+ LogMsg("ERROR: mDNS_SetSecretForZone - could not convert shared secret %s from base64", sharedSecret);
+ ufree(info);
+ status = mStatus_UnknownErr;
+ goto exit;
+ }
+ DNSDigest_ConstructHMACKey(info, keybuf, (mDNSu32)keylen);
+
+ // link into list
+ info->next = m->uDNS_info.AuthInfoList;
+ m->uDNS_info.AuthInfoList = info;
+exit:
+ mDNS_Unlock(m);
+ return status;
+ }
+
+ // ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark - NAT Traversal
+#endif
+
+mDNSlocal mDNSBool DomainContainsLabelString(const domainname *d, const char *str)
+ {
+ const domainlabel *l;
+ domainlabel buf;
+
+ if (!MakeDomainLabelFromLiteralString(&buf, str)) return mDNSfalse;
+
+ for (l = (const domainlabel *)d; l->c[0]; l = (const domainlabel *)(l->c + l->c[0]+1))
+ if (SameDomainLabel(l->c, buf.c)) return mDNStrue;
+ return mDNSfalse;
+ }
+
+// allocate struct, link into global list, initialize
+mDNSlocal NATTraversalInfo *AllocNATInfo(mDNS *const m, NATOp_t op, NATResponseHndlr callback)
+ {
+ uDNS_GlobalInfo *u = &m->uDNS_info;
+ NATTraversalInfo *info = umalloc(sizeof(NATTraversalInfo));
+ if (!info) { LogMsg("ERROR: malloc"); return mDNSNULL; }
+ ubzero(info, sizeof(NATTraversalInfo));
+ info->next = u->NATTraversals;
+ u->NATTraversals = info;
+ info->retry = mDNSPlatformTimeNow(m) + NATMAP_INIT_RETRY;
+ info->op = op;
+ info->state = NATState_Init;
+ info->ReceiveResponse = callback;
+ info->PublicPort.NotAnInteger = 0;
+ info->Router = u->Router;
+ return info;
+ }
+
+// unlink from list, deallocate
+mDNSlocal mDNSBool FreeNATInfo(mDNS *m, NATTraversalInfo *n)
+ {
+ NATTraversalInfo *ptr, *prev = mDNSNULL;
+ ServiceRecordSet *s = m->uDNS_info.ServiceRegistrations;
+
+ // Verify that object is not referenced by any services
+ while (s)
+ {
+ if (s->uDNS_info.NATinfo == n)
+ {
+ LogMsg("Error: Freeing NAT info object still referenced by Service Record Set %##s!", s->RR_SRV.resrec.name->c);
+ s->uDNS_info.NATinfo = mDNSNULL;
+ }
+ s = s->next;
+ }
+
+ if (n == m->uDNS_info.LLQNatInfo) m->uDNS_info.LLQNatInfo = mDNSNULL;
+ ptr = m->uDNS_info.NATTraversals;
+ while (ptr)
+ {
+ if (ptr == n)
+ {
+ if (prev) prev->next = ptr->next;
+ else m->uDNS_info.NATTraversals = ptr->next;
+ ufree(n);
+ return mDNStrue;
+ }
+ prev = ptr;
+ ptr = ptr->next;
+ }
+ LogMsg("FreeNATInfo: NATTraversalInfo not found in list");
+ return mDNSfalse;
+ }
+
+mDNSlocal void SendNATMsg(NATTraversalInfo *info, mDNS *m)
+ {
+ mStatus err;
+ uDNS_GlobalInfo *u = &m->uDNS_info;
+
+ if (info->state != NATState_Request && info->state != NATState_Refresh)
+ { LogMsg("SendNATMsg: Bad state %d", info->state); return; }
+
+ if (u->Router.ip.v4.NotAnInteger)
+ {
+ // send msg if we have a router
+ const mDNSu8 *end = (mDNSu8 *)&info->request;
+ if (info->op == NATOp_AddrRequest) end += sizeof(NATAddrRequest);
+ else end += sizeof(NATPortMapRequest);
+
+ err = mDNSPlatformSendUDP(m, &info->request, end, 0, &u->Router, NATPMPPort);
+ if (!err) (info->ntries++); // don't increment attempt counter if the send failed
+ }
+
+ // set retry
+ if (info->RetryInterval < NATMAP_INIT_RETRY) info->RetryInterval = NATMAP_INIT_RETRY;
+ else if (info->RetryInterval * 2 > NATMAP_MAX_RETRY) info->RetryInterval = NATMAP_MAX_RETRY;
+ else info->RetryInterval *= 2;
+ info->retry = mDNSPlatformTimeNow(m) + info->RetryInterval;
+ }
+
+mDNSlocal mDNSBool ReceiveNATAddrResponse(NATTraversalInfo *n, mDNS *m, mDNSu8 *pkt, mDNSu16 len)
+ {
+ mStatus err = mStatus_NoError;
+ AuthRecord *rr = mDNSNULL;
+ NATAddrReply *response = (NATAddrReply *)pkt;
+ mDNSAddr addr;
+
+ if (n->state != NATState_Request)
+ {
+ LogMsg("ReceiveNATAddrResponse: bad state %d", n->state);
+ return mDNSfalse;
+ }
+
+ rr = n->reg.RecordRegistration;
+ if (!rr)
+ {
+ LogMsg("ReceiveNATAddrResponse: registration cancelled");
+ return mDNSfalse;
+ }
+
+ addr.type = mDNSAddrType_IPv4;
+ addr.ip.v4 = rr->resrec.rdata->u.ipv4;
+
+ if (!pkt) // timeout
+ {
+#ifdef _LEGACY_NAT_TRAVERSAL_
+ err = LNT_GetPublicIP(&addr.ip.v4);
+ if (err) goto end;
+ else n->state = NATState_Legacy;
+#else
+ debugf("ReceiveNATAddrResponse: timeout");
+ err = mStatus_NATTraversal;
+ goto end;
+#endif // _LEGACY_NAT_TRAVERSAL_
+ }
+ else
+ {
+ if (len < sizeof(*response))
+ {
+ LogMsg("ReceiveNATAddrResponse: response too short (%d bytes)", len);
+ return mDNSfalse;
+ }
+ if (response->vers != NATMAP_VERS)
+ {
+ LogMsg("ReceiveNATAddrResponse: received version %d (expect version %d)", pkt[0], NATMAP_VERS);
+ return mDNSfalse;
+ }
+ if (response->opcode != (NATOp_AddrRequest | NATMAP_RESPONSE_MASK))
+ {
+ LogMsg("ReceiveNATAddrResponse: bad response code %d", response->opcode);
+ return mDNSfalse;
+ }
+ if (response->err.NotAnInteger)
+ { LogMsg("ReceiveAddrResponse: received error %d", mDNSVal16(response->err)); err = mStatus_NATTraversal; goto end; }
+
+ addr.ip.v4 = response->PubAddr;
+ n->state = NATState_Established;
+ }
+
+ if (IsPrivateV4Addr(&addr))
+ {
+ LogMsg("ReceiveNATAddrResponse: Double NAT");
+ err = mStatus_DoubleNAT;
+ goto end;
+ }
+
+ end:
+ if (err)
+ {
+ FreeNATInfo(m, n);
+ if (rr)
+ {
+ rr->uDNS_info.NATinfo = mDNSNULL;
+ rr->uDNS_info.state = regState_Unregistered; // note that rr is not yet in global list
+ rr->RecordCallback(m, rr, mStatus_NATTraversal);
+ // note - unsafe to touch rr after callback
+ }
+ return mDNStrue;
+ }
+ else LogOperation("Received public IP address %d.%d.%d.%d from NAT.", addr.ip.v4.b[0], addr.ip.v4.b[1], addr.ip.v4.b[2], addr.ip.v4.b[3]);
+ rr->resrec.rdata->u.ipv4 = addr.ip.v4; // replace rdata w/ public address
+ uDNS_RegisterRecord(m, rr);
+ return mDNStrue;
+ }
+
+
+mDNSlocal void StartGetPublicAddr(mDNS *m, AuthRecord *AddressRec)
+ {
+ NATAddrRequest *req;
+ uDNS_GlobalInfo *u = &m->uDNS_info;
+
+ NATTraversalInfo *info = AllocNATInfo(m, NATOp_AddrRequest, ReceiveNATAddrResponse);
+ if (!info) { uDNS_RegisterRecord(m, AddressRec); return; }
+ AddressRec->uDNS_info.NATinfo = info;
+ info->reg.RecordRegistration = AddressRec;
+ info->state = NATState_Request;
+
+ // format message
+ req = &info->request.AddrReq;
+ req->vers = NATMAP_VERS;
+ req->opcode = NATOp_AddrRequest;
+
+ if (!u->Router.ip.v4.NotAnInteger)
+ {
+ debugf("No router. Will retry NAT traversal in %ld ticks", NATMAP_INIT_RETRY);
+ return;
+ }
+
+ SendNATMsg(info, m);
+ }
+
+
+mDNSlocal void RefreshNATMapping(NATTraversalInfo *n, mDNS *m)
+ {
+ n->state = NATState_Refresh;
+ n->RetryInterval = NATMAP_INIT_RETRY;
+ n->ntries = 0;
+ SendNATMsg(n, m);
+ }
+
+mDNSlocal void LLQNatMapComplete(mDNS *m)
+ {
+ uDNS_GlobalInfo *u = &m->uDNS_info;
+ LLQ_Info *llqInfo;
+ NATTraversalInfo *n = u->LLQNatInfo;
+
+ if (!n) { LogMsg("Error: LLQNatMapComplete called with NULL LLQNatInfo"); return; }
+ if (n->state != NATState_Established && n->state != NATState_Legacy && n->state != NATState_Error)
+ { LogMsg("LLQNatMapComplete - bad nat state %d", n->state); return; }
+
+ u->CurrentQuery = u->ActiveQueries;
+ while (u->CurrentQuery)
+ {
+ DNSQuestion *q = u->CurrentQuery;
+ u->CurrentQuery = u->CurrentQuery->next;
+ llqInfo = q->uDNS_info.llq;
+ if (q->LongLived && llqInfo->state == LLQ_NatMapWait)
+ {
+ if (n->state == NATState_Error)
+ {
+ llqInfo->NATMap = mDNSfalse;
+ llqInfo->question->uDNS_info.responseCallback = llqResponseHndlr;
+ llqInfo->state = LLQ_Poll;
+ llqInfo->question->LastQTime = mDNSPlatformTimeNow(m) - (2 * INIT_UCAST_POLL_INTERVAL); // trigger immediate poll
+ llqInfo->question->ThisQInterval = INIT_UCAST_POLL_INTERVAL;
+ }
+ else { llqInfo->state = LLQ_GetZoneInfo; startLLQHandshake(m, llqInfo, mDNSfalse); }
+ }
+ }
+ }
+
+mDNSlocal mDNSBool ReceivePortMapReply(NATTraversalInfo *n, mDNS *m, mDNSu8 *pkt, mDNSu16 len)
+ {
+ ServiceRecordSet *srs = n->reg.ServiceRegistration;
+ mDNSIPPort priv = srs ? srs->RR_SRV.resrec.rdata->u.srv.port : m->UnicastPort4;
+ mDNSu32 lease;
+ mDNSBool deletion = !n->request.PortReq.lease.NotAnInteger;
+ NATPortMapReply *reply = (NATPortMapReply *)pkt;
+ mDNSu8 *service = srs ? srs->RR_SRV.resrec.name->c : (mDNSu8 *)"\016LLQ event port";
+
+ if (n->state != NATState_Request && n->state != NATState_Refresh)
+ { LogMsg("ReceivePortMapReply (%##s): bad state %d", service, n->state); return mDNSfalse; }
+
+ if (!pkt && !deletion) // timeout
+ {
+#ifdef _LEGACY_NAT_TRAVERSAL_
+ mDNSIPPort pub;
+ int ntries = 0;
+ mStatus err;
+ mDNSBool tcp = (srs && DomainContainsLabelString(srs->RR_PTR.resrec.name, "_tcp"));
+
+ pub = priv; // initially request priv == pub
+ while (1)
+ {
+ err = LNT_MapPort(priv, pub, tcp);
+ if (!err)
+ {
+ n->PublicPort = pub;
+ n->state = NATState_Legacy;
+ goto end;
+ }
+ else if (err != mStatus_AlreadyRegistered || ++ntries > LEGACY_NATMAP_MAX_TRIES)
+ {
+ n->state = NATState_Error;
+ goto end;
+ }
+ else
+ {
+ // the mapping we want is taken - try a random port
+ mDNSu16 RandPort = mDNSRandom(DYN_PORT_MAX - DYN_PORT_MIN) + DYN_PORT_MIN;
+ pub = mDNSOpaque16fromIntVal(RandPort);
+ }
+ }
+#else
+ goto end;
+#endif // _LEGACY_NAT_TRAVERSAL_
+ }
+
+ if (len < sizeof(*reply)) { LogMsg("ReceivePortMapReply: response too short (%d bytes)", len); return mDNSfalse; }
+ if (reply->vers != NATMAP_VERS) { LogMsg("ReceivePortMapReply: received version %d (expect version %d)", pkt[0], NATMAP_VERS); return mDNSfalse; }
+ if (reply->opcode != (n->op | NATMAP_RESPONSE_MASK)) { LogMsg("ReceivePortMapReply: bad response code %d", pkt[1]); return mDNSfalse; }
+ if (reply->err.NotAnInteger) { LogMsg("ReceivePortMapReply: received error %d", mDNSVal16(reply->err)); return mDNSfalse; }
+ if (priv.NotAnInteger != reply->priv.NotAnInteger) return mDNSfalse; // packet does not match this request
+
+ if (!srs && n != m->uDNS_info.LLQNatInfo)
+ {
+ LogMsg("ReceivePortMapReply: registration cancelled"); //!!!KRS change to debugf before checkin
+ FreeNATInfo(m, n);
+ return mDNStrue;
+ }
+
+ if (deletion) { n->state = NATState_Deleted; return mDNStrue; }
+
+ lease = (mDNSu32)mDNSVal32(reply->lease);
+ if (lease > 0x70000000UL / mDNSPlatformOneSecond) lease = 0x70000000UL / mDNSPlatformOneSecond;
+
+ if (n->state == NATState_Refresh && reply->pub.NotAnInteger != n->PublicPort.NotAnInteger)
+ LogMsg("ReceivePortMapReply: NAT refresh changed public port from %d to %d", mDNSVal16(n->PublicPort), mDNSVal16(reply->pub));
+ // this should never happen
+ // !!!KRS to be defensive, use SRVChanged flag on service and deregister here
+
+ n->PublicPort = reply->pub;
+ if (reply->pub.NotAnInteger != n->request.PortReq.pub.NotAnInteger) n->request.PortReq.pub = reply->pub; // set message buffer for refreshes
+
+ n->retry = mDNSPlatformTimeNow(m) + ((mDNSs32)lease * mDNSPlatformOneSecond / 2); // retry half way to expiration
+
+ if (n->state == NATState_Refresh) { n->state = NATState_Established; return mDNStrue; }
+ n->state = NATState_Established;
+
+ end:
+ if (n->state != NATState_Established && n->state != NATState_Legacy)
+ {
+ LogMsg("NAT Port Mapping (%##s): timeout", service);
+ if (pkt) LogMsg("!!! timeout with non-null packet");
+ n->state = NATState_Error;
+ if (srs)
+ {
+ uDNS_HostnameInfo *hi = m->uDNS_info.Hostnames;
+ while (hi)
+ {
+ if (hi->arv6 && (hi->arv6->uDNS_info.state == regState_Registered || hi->arv6->uDNS_info.state == regState_Refresh)) break;
+ else hi = hi->next;
+ }
+
+ if (hi)
+ {
+ debugf("Port map failed for service %##s - using IPv6 service target", service);
+ srs->uDNS_info.NATinfo = mDNSNULL;
+ FreeNATInfo(m, n);
+ goto register_service;
+ }
+ else srs->uDNS_info.state = regState_NATError;
+ }
+ else LLQNatMapComplete(m);
+ return mDNStrue;
+ }
+ else LogOperation("Mapped private port %d to public port %d", mDNSVal16(priv), mDNSVal16(n->PublicPort));
+
+ if (!srs) { LLQNatMapComplete(m); return mDNStrue; }
+
+ register_service:
+ if (srs->uDNS_info.ns.ip.v4.NotAnInteger) SendServiceRegistration(m, srs); // non-zero server address means we already have necessary zone data to send update
+ else
+ {
+ srs->uDNS_info.state = regState_FetchingZoneData;
+ startGetZoneData(srs->RR_SRV.resrec.name, m, mDNStrue, mDNSfalse, serviceRegistrationCallback, srs);
+ }
+ return mDNStrue;
+ }
+
+mDNSlocal void FormatPortMaprequest(NATTraversalInfo *info, mDNSIPPort port)
+ {
+ NATPortMapRequest *req = &info->request.PortReq;
+
+ req->vers = NATMAP_VERS;
+ req->opcode = info->op;
+ req->unused.NotAnInteger = 0;
+ req->priv = port;
+ req->pub = port;
+ req->lease = mDNSOpaque32fromIntVal(NATMAP_DEFAULT_LEASE);
+ }
+
+mDNSlocal void SendInitialPMapReq(mDNS *m, NATTraversalInfo *info)
+ {
+ if (!m->uDNS_info.Router.ip.v4.NotAnInteger)
+ {
+ debugf("No router. Will retry NAT traversal in %ld seconds", NATMAP_INIT_RETRY);
+ info->retry = mDNSPlatformTimeNow(m) + NATMAP_INIT_RETRY;
+ info->RetryInterval = NATMAP_INIT_RETRY;
+ return;
+ }
+ SendNATMsg(info, m);
+ return;
+ }
+
+mDNSlocal void StartNATPortMap(mDNS *m, ServiceRecordSet *srs)
+ {
+ NATOp_t op;
+ NATTraversalInfo *info;
+
+ if (DomainContainsLabelString(srs->RR_PTR.resrec.name, "_tcp")) op = NATOp_MapTCP;
+ else if (DomainContainsLabelString(srs->RR_PTR.resrec.name, "_udp")) op = NATOp_MapUDP;
+ else { LogMsg("StartNATPortMap: could not determine transport protocol of service %##s", srs->RR_SRV.resrec.name->c); goto error; }
+
+ if (srs->uDNS_info.NATinfo) { LogMsg("Error: StartNATPortMap - NAT info already initialized!"); FreeNATInfo(m, srs->uDNS_info.NATinfo); }
+ info = AllocNATInfo(m, op, ReceivePortMapReply);
+ srs->uDNS_info.NATinfo = info;
+ info->reg.ServiceRegistration = srs;
+ info->state = NATState_Request;
+
+ FormatPortMaprequest(info, srs->RR_SRV.resrec.rdata->u.srv.port);
+ SendInitialPMapReq(m, info);
+ return;
+
+ error:
+ startGetZoneData(srs->RR_SRV.resrec.name, m, mDNStrue, mDNSfalse, serviceRegistrationCallback, srs);
+ }
+
+mDNSlocal void DeleteNATPortMapping(mDNS *m, NATTraversalInfo *nat, ServiceRecordSet *srs)
+ {
+ if (nat->state == NATState_Established) // let other edge-case states expire for simplicity
+ {
+ // zero lease
+ nat->request.PortReq.lease.NotAnInteger = 0;
+ nat->state = NATState_Request;
+ SendNATMsg(nat, m);
+ }
+#ifdef _LEGACY_NAT_TRAVERSAL_
+ else if (nat->state == NATState_Legacy)
+ {
+ mStatus err = mStatus_NoError;
+ mDNSBool tcp = srs ? DomainContainsLabelString(srs->RR_PTR.resrec.name, "_tcp") : mDNSfalse;
+ err = LNT_UnmapPort(nat->PublicPort, tcp);
+ if (err) LogMsg("Legacy NAT Traversal - unmap request failed with error %ld", err);
+ }
+#else
+ (void)srs; // unused
+#endif // _LEGACY_NAT_TRAVERSAL_
+ }
+
+mDNSlocal void StartLLQNatMap(mDNS *m)
+ {
+ NATTraversalInfo *info = AllocNATInfo(m, NATOp_MapUDP, ReceivePortMapReply);
+ uDNS_GlobalInfo *u = &m->uDNS_info;
+
+ u->LLQNatInfo = info;
+
+ info->reg.RecordRegistration = mDNSNULL;
+ info->reg.ServiceRegistration = mDNSNULL;
+ info->state = NATState_Request;
+ FormatPortMaprequest(info, m->UnicastPort4);
+ SendInitialPMapReq(m, info);
+ return;
+ }
+
+// if LLQ NAT context unreferenced, delete the mapping
+mDNSlocal void CheckForUnreferencedLLQMapping(mDNS *m)
+ {
+ NATTraversalInfo *nat = m->uDNS_info.LLQNatInfo;
+ DNSQuestion *q;
+
+ if (!nat) return;
+
+ for (q = m->uDNS_info.ActiveQueries; q; q = q->next)
+ if (q->LongLived && q->uDNS_info.llq->NATMap) return;
+
+ //to avoid race condition if we need to recreate before this finishes, we do one-shot deregistration
+ if (nat->state == NATState_Established || nat->state == NATState_Legacy)
+ DeleteNATPortMapping(m, nat, mDNSNULL); // for simplicity we allow other states to expire
+ FreeNATInfo(m, nat); // note: this clears the global LLQNatInfo pointer
+ }
+
+ // ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark - host name and interface management
+#endif
+
+// if we ever want to refine support for multiple hostnames, we can add logic matching service names to a particular hostname
+// for now, we grab the first registered DynDNS name, if any, or a static name we learned via a reverse-map query
+mDNSlocal mDNSBool GetServiceTarget(uDNS_GlobalInfo *u, AuthRecord *srv, domainname *dst)
+ {
+ uDNS_HostnameInfo *hi = u->Hostnames;
+ (void)srv; // unused
+
+ dst->c[0] = 0;
+ while (hi)
+ {
+ if (hi->arv4 && (hi->arv4->uDNS_info.state == regState_Registered || hi->arv4->uDNS_info.state == regState_Refresh))
+ {
+ AssignDomainName(dst, hi->arv4->resrec.name);
+ return mDNStrue;
+ }
+ if (hi->arv6 && (hi->arv6->uDNS_info.state == regState_Registered || hi->arv6->uDNS_info.state == regState_Refresh))
+ {
+ AssignDomainName(dst, hi->arv4->resrec.name);
+ return mDNStrue;
+ }
+ hi = hi->next;
+ }
+
+ if (u->StaticHostname.c[0]) { AssignDomainName(dst, &u->StaticHostname); return mDNStrue; }
+ return mDNSfalse;
+ }
+
+mDNSlocal void UpdateSRV(mDNS *m, ServiceRecordSet *srs)
+ {
+ uDNS_GlobalInfo *u = &m->uDNS_info;
+ ExtraResourceRecord *e;
+
+ // 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 newtarget;
+ domainname *curtarget = &srs->RR_SRV.resrec.rdata->u.srv.target;
+ mDNSBool HaveTarget = GetServiceTarget(u, &srs->RR_SRV, &newtarget);
+ mDNSBool TargetChanged = (HaveTarget && srs->uDNS_info.state == regState_NoTarget) || (curtarget->c[0] && !HaveTarget) || !SameDomainName(curtarget, &newtarget);
+ mDNSBool HaveZoneData = srs->uDNS_info.ns.ip.v4.NotAnInteger ? mDNStrue : mDNSfalse;
+
+ // 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 public port
+ // We were not behind a NAT and now we are
+
+ NATTraversalInfo *nat = srs->uDNS_info.NATinfo;
+ mDNSIPPort port = srs->RR_SRV.resrec.rdata->u.srv.port;
+ mDNSBool NATChanged = mDNSfalse;
+ mDNSBool NowBehindNAT = port.NotAnInteger && IsPrivateV4Addr(&u->AdvertisedV4);
+ mDNSBool WereBehindNAT = nat != mDNSNULL;
+ mDNSBool NATRouterChanged = nat && nat->Router.ip.v4.NotAnInteger != u->Router.ip.v4.NotAnInteger;
+ mDNSBool PortWasMapped = nat && (nat->state == NATState_Established || nat->state == NATState_Legacy) && nat->PublicPort.NotAnInteger != port.NotAnInteger;
+
+ if (WereBehindNAT && NowBehindNAT && NATRouterChanged) NATChanged = mDNStrue;
+ else if (!NowBehindNAT && PortWasMapped) NATChanged = mDNStrue;
+ else if (!WereBehindNAT && NowBehindNAT) NATChanged = mDNStrue;
+
+ if (!TargetChanged && !NATChanged) return;
+
+ debugf("UpdateSRV (%##s) HadZoneData=%d, TargetChanged=%d, HaveTarget=%d, NowBehindNAT=%d, WereBehindNAT=%d, NATRouterChanged=%d, PortWasMapped=%d",
+ srs->RR_SRV.resrec.name->c, HaveZoneData, TargetChanged, HaveTarget, NowBehindNAT, WereBehindNAT, NATRouterChanged, PortWasMapped);
+
+ switch(srs->uDNS_info.state)
+ {
+ case regState_FetchingZoneData:
+ case regState_Cancelled:
+ case regState_DeregPending:
+ case regState_DeregDeferred:
+ case regState_Unregistered:
+ case regState_NATMap:
+ case regState_ExtraQueued:
+ // 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
+ return;
+
+ case regState_Pending:
+ case regState_Refresh:
+ case regState_UpdatePending:
+ // let the in-flight operation complete before updating
+ srs->uDNS_info.SRVUpdateDeferred = mDNStrue;
+ return;
+
+ case regState_NATError:
+ if (!NATChanged) return;
+ // if nat changed, register if we have a target (below)
+
+ case regState_NoTarget:
+ if (HaveTarget)
+ {
+ debugf("UpdateSRV: %s service %##s", HaveZoneData ? (NATChanged && NowBehindNAT ? "Starting Port Map for" : "Registering") : "Getting Zone Data for", srs->RR_SRV.resrec.name->c);
+ if (!HaveZoneData)
+ {
+ srs->uDNS_info.state = regState_FetchingZoneData;
+ startGetZoneData(srs->RR_SRV.resrec.name, m, mDNStrue, mDNSfalse, serviceRegistrationCallback, srs);
+ }
+ else
+ {
+ if (nat && (NATChanged || !NowBehindNAT)) { srs->uDNS_info.NATinfo = mDNSNULL; FreeNATInfo(m, nat); }
+ if (NATChanged && NowBehindNAT) { srs->uDNS_info.state = regState_NATMap; StartNATPortMap(m, srs); }
+ else SendServiceRegistration(m, srs);
+ }
+ }
+ return;
+
+ case regState_Registered:
+ // target or nat changed. deregister service. upon completion, we'll look for a new target
+ debugf("UpdateSRV: SRV record changed for service %##s - deregistering (will re-register with new SRV)", srs->RR_SRV.resrec.name->c);
+ for (e = srs->Extras; e; e = e->next) e->r.uDNS_info.state = regState_ExtraQueued; // extra will be re-registed if the service is re-registered
+ srs->uDNS_info.SRVChanged = mDNStrue;
+ SendServiceDeregistration(m, srs);
+ return;
+ }
+ }
+
+mDNSlocal void UpdateSRVRecords(mDNS *m)
+ {
+ ServiceRecordSet *srs;
+
+ for (srs = m->uDNS_info.ServiceRegistrations; srs; srs = srs->next) UpdateSRV(m, srs);
+ }
+
+mDNSlocal void HostnameCallback(mDNS *const m, AuthRecord *const rr, mStatus result)
+ {
+ uDNS_HostnameInfo *hi = (uDNS_HostnameInfo *)rr->RecordContext;
+
+ if (result == mStatus_MemFree)
+ {
+ if (hi)
+ {
+ if (hi->arv4 == rr) hi->arv4 = mDNSNULL;
+ else if (hi->arv4 == rr) hi->arv6 = mDNSNULL;
+ rr->RecordContext = mDNSNULL;
+ if (!hi->arv4 && !hi->arv6) ufree(hi); // free hi when both v4 and v6 AuthRecs deallocated
+ }
+ ufree(rr);
+ 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 %ld for registration of %##s IP %.4a", result, rr->resrec.name->c, &rr->resrec.rdata->u.ipv4);
+ else
+ LogMsg("HostnameCallback: Error %ld for registration of %##s IP %.16a", result, rr->resrec.name->c, &rr->resrec.rdata->u.ipv6);
+ if (!hi) { ufree(rr); return; }
+ if (rr->uDNS_info.state != regState_Unregistered) LogMsg("Error: HostnameCallback invoked with error code for record not in regState_Unregistered!");
+
+ if ((!hi->arv4 || hi->arv4->uDNS_info.state == regState_Unregistered) &&
+ (!hi->arv6 || hi->arv6->uDNS_info.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
+ UpdateSRVRecords(m);
+
+ // Deliver success to client
+ if (!hi) { LogMsg("HostnameCallback invoked with orphaned address record"); return; }
+ if (rr->resrec.rrtype == kDNSType_A)
+ LogMsg("Registered hostname %##s IP %.4a", rr->resrec.name->c, &rr->resrec.rdata->u.ipv4);
+ else
+ LogMsg("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;
+ }
+
+// register record or begin NAT traversal
+mDNSlocal void AdvertiseHostname(mDNS *m, uDNS_HostnameInfo *h)
+ {
+ uDNS_GlobalInfo *u = &m->uDNS_info;
+
+ if (u->AdvertisedV4.ip.v4.NotAnInteger && h->arv4->uDNS_info.state == regState_Unregistered)
+ {
+ if (IsPrivateV4Addr(&u->AdvertisedV4))
+ StartGetPublicAddr(m, h->arv4);
+ else
+ {
+ LogMsg("Advertising %##s IP %.4a", h->arv4->resrec.name->c, &u->AdvertisedV4.ip.v4);
+ uDNS_RegisterRecord(m, h->arv4);
+ }
+ }
+ if (u->AdvertisedV6.ip.v6.b[0] && h->arv6->uDNS_info.state == regState_Unregistered)
+ {
+ LogMsg("Advertising %##s IP %.16a", h->arv4->resrec.name->c, &u->AdvertisedV6.ip.v6);
+ uDNS_RegisterRecord(m, h->arv6);
+ }
+ }
+
+mDNSlocal void FoundStaticHostname(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
+ {
+ const domainname *pktname = &answer->rdata->u.name;
+ domainname *storedname = &m->uDNS_info.StaticHostname;
+ uDNS_HostnameInfo *h = m->uDNS_info.Hostnames;
+
+ (void)question;
+
+ debugf("FoundStaticHostname: %##s -> %##s (%s)", question->qname.c, answer->rdata->u.name.c, AddRecord ? "added" : "removed");
+ if (AddRecord && !SameDomainName(pktname, storedname))
+ {
+ AssignDomainName(storedname, pktname);
+ while (h)
+ {
+ if ((h->arv4 && (h->arv4->uDNS_info.state == regState_FetchingZoneData || h->arv4->uDNS_info.state == regState_Pending || h->arv4->uDNS_info.state == regState_NATMap)) ||
+ (h->arv6 && (h->arv6->uDNS_info.state == regState_FetchingZoneData || h->arv6->uDNS_info.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->uDNS_info.DelaySRVUpdate = mDNStrue;
+ m->uDNS_info.NextSRVUpdate = mDNSPlatformTimeNow(m) + (5 * mDNSPlatformOneSecond);
+ return;
+ }
+ h = h->next;
+ }
+ UpdateSRVRecords(m);
+ }
+ else if (!AddRecord && SameDomainName(pktname, storedname))
+ {
+ storedname->c[0] = 0;
+ UpdateSRVRecords(m);
+ }
+ }
+
+mDNSlocal void GetStaticHostname(mDNS *m)
+ {
+ char buf[MAX_ESCAPED_DOMAIN_NAME];
+ DNSQuestion *q = &m->uDNS_info.ReverseMap;
+ mDNSu8 *ip = m->uDNS_info.AdvertisedV4.ip.v4.b;
+ mStatus err;
+
+ if (m->uDNS_info.ReverseMapActive)
+ {
+ uDNS_StopQuery(m, q);
+ m->uDNS_info.ReverseMapActive = mDNSfalse;
+ }
+
+ m->uDNS_info.StaticHostname.c[0] = 0;
+ if (!m->uDNS_info.AdvertisedV4.ip.v4.NotAnInteger) return;
+ ubzero(q, sizeof(*q));
+ mDNS_snprintf(buf, MAX_ESCAPED_DOMAIN_NAME, "%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->QuestionCallback = FoundStaticHostname;
+ q->QuestionContext = mDNSNULL;
+
+ err = uDNS_StartQuery(m, q);
+ if (err) LogMsg("Error: GetStaticHostname - StartQuery returned error %d", err);
+ else m->uDNS_info.ReverseMapActive = mDNStrue;
+ }
+
+mDNSlocal void AssignHostnameInfoAuthRecord(mDNS *m, uDNS_HostnameInfo *hi, int type)
+ {
+ AuthRecord **dst = (type == mDNSAddrType_IPv4 ? &hi->arv4 : &hi->arv6);
+ AuthRecord *ar = umalloc(sizeof(*ar));
+ uDNS_GlobalInfo *u = &m->uDNS_info;
+
+ if (type != mDNSAddrType_IPv4 && type != mDNSAddrType_IPv6) { LogMsg("ERROR: AssignHostnameInfoAuthRecord - bad type %d", type); return; }
+ if (!ar) { LogMsg("ERROR: AssignHostnameInfoAuthRecord - malloc"); return; }
+
+ mDNS_SetupResourceRecord(ar, mDNSNULL, 0, type == mDNSAddrType_IPv4 ? kDNSType_A : kDNSType_AAAA, 1, kDNSRecordTypeKnownUnique, HostnameCallback, hi);
+ AssignDomainName(ar->resrec.name, &hi->fqdn);
+
+ // only set RData if we have a valid IP
+ if (type == mDNSAddrType_IPv4 && u->AdvertisedV4.ip.v4.NotAnInteger)
+ {
+ if (u->MappedV4.ip.v4.NotAnInteger) ar->resrec.rdata->u.ipv4 = u->MappedV4.ip.v4;
+ else ar->resrec.rdata->u.ipv4 = u->AdvertisedV4.ip.v4;
+ }
+ else if (type == mDNSAddrType_IPv6 && u->AdvertisedV6.ip.v6.b[0])
+ {
+ ar->resrec.rdata->u.ipv6 = u->AdvertisedV6.ip.v6;
+ }
+
+ ar->uDNS_info.state = regState_Unregistered;
+
+ if (*dst)
+ {
+ LogMsg("ERROR: AssignHostnameInfoAuthRecord - overwriting %s AuthRec", type == mDNSAddrType_IPv4 ? "IPv4" : "IPv6");
+ unlinkAR(&u->RecordRegistrations, *dst);
+ (*dst)->RecordContext = mDNSNULL; // defensively clear backpointer to avoid doubly-referenced context
+ }
+
+ *dst = ar;
+ }
+
+
+// Deregister hostnames and register new names for each host domain with the current global
+// values for the hostlabel and primary IP address
+mDNSlocal void UpdateHostnameRegistrations(mDNS *m)
+ {
+ uDNS_GlobalInfo *u = &m->uDNS_info;
+ uDNS_HostnameInfo *i;
+
+ for (i = u->Hostnames; i; i = i->next)
+ {
+ if (i->arv4 && i->arv4->uDNS_info.state != regState_Unregistered &&
+ i->arv4->resrec.rdata->u.ipv4.NotAnInteger != u->AdvertisedV4.ip.v4.NotAnInteger &&
+ i->arv4->resrec.rdata->u.ipv4.NotAnInteger !=u->MappedV4.ip.v4.NotAnInteger)
+ {
+ uDNS_DeregisterRecord(m, i->arv4);
+ i->arv4 = mDNSNULL;
+ }
+ if (i->arv6 && !mDNSPlatformMemSame(i->arv6->resrec.rdata->u.ipv6.b, u->AdvertisedV6.ip.v6.b, 16) && i->arv6->uDNS_info.state != regState_Unregistered)
+ {
+ uDNS_DeregisterRecord(m, i->arv6);
+ i->arv6 = mDNSNULL;
+ }
+
+ if (!i->arv4 && u->AdvertisedV4.ip.v4.NotAnInteger) AssignHostnameInfoAuthRecord(m, i, mDNSAddrType_IPv4);
+ else if (i->arv4 && i->arv4->uDNS_info.state == regState_Unregistered) i->arv4->resrec.rdata->u.ipv4 = u->AdvertisedV4.ip.v4; // simply overwrite unregistered
+ if (!i->arv6 && u->AdvertisedV6.ip.v6.b[0]) AssignHostnameInfoAuthRecord(m, i, mDNSAddrType_IPv6);
+ else if (i->arv6 &&i->arv6->uDNS_info.state == regState_Unregistered) i->arv6->resrec.rdata->u.ipv6 = u->AdvertisedV6.ip.v6;
+
+ AdvertiseHostname(m, i);
+ }
+ }
+
+mDNSexport void mDNS_AddDynDNSHostName(mDNS *m, const domainname *fqdn, mDNSRecordCallback *StatusCallback, const void *StatusContext)
+ {
+ uDNS_GlobalInfo *u = &m->uDNS_info;
+ uDNS_HostnameInfo *ptr, *new;
+
+ mDNS_Lock(m);
+
+ // check if domain already registered
+ for (ptr = u->Hostnames; ptr; ptr = ptr->next)
+ {
+ if (SameDomainName(fqdn, &ptr->fqdn))
+ { LogMsg("Host Domain %##s already in list", fqdn->c); goto exit; }
+ }
+
+ // allocate and format new address record
+ new = umalloc(sizeof(*new));
+ if (!new) { LogMsg("ERROR: mDNS_AddDynDNSHostname - malloc"); goto exit; }
+ ubzero(new, sizeof(*new));
+ new->next = u->Hostnames;
+ u->Hostnames = new;
+
+ AssignDomainName(&new->fqdn, fqdn);
+ new->StatusCallback = StatusCallback;
+ new->StatusContext = StatusContext;
+
+ if (u->AdvertisedV4.ip.v4.NotAnInteger) AssignHostnameInfoAuthRecord(m, new, mDNSAddrType_IPv4);
+ else new->arv4 = mDNSNULL;
+ if (u->AdvertisedV6.ip.v6.b[0]) AssignHostnameInfoAuthRecord(m, new, mDNSAddrType_IPv6);
+ else new->arv6 = mDNSNULL;
+
+ if (u->AdvertisedV6.ip.v6.b[0] || u->AdvertisedV4.ip.v4.NotAnInteger) AdvertiseHostname(m, new);
+
+exit:
+ mDNS_Unlock(m);
+ }
+
+mDNSexport void mDNS_RemoveDynDNSHostName(mDNS *m, const domainname *fqdn)
+ {
+ uDNS_GlobalInfo *u = &m->uDNS_info;
+ uDNS_HostnameInfo **ptr = &u->Hostnames;
+
+ mDNS_Lock(m);
+
+ while (*ptr && !SameDomainName(fqdn, &(*ptr)->fqdn)) ptr = &(*ptr)->next;
+ if (!*ptr) LogMsg("mDNS_RemoveDynDNSHostName: no such domainname %##s", fqdn->c);
+ else
+ {
+ uDNS_HostnameInfo *hi = *ptr;
+ *ptr = (*ptr)->next; // unlink
+ if (hi->arv4)
+ {
+ hi->arv4->RecordContext = mDNSNULL; // about to free wrapper struct
+ if (hi->arv4->uDNS_info.state != regState_Unregistered) uDNS_DeregisterRecord(m, hi->arv4);
+ else { ufree(hi->arv4); hi->arv4 = mDNSNULL; }
+ }
+ if (hi->arv6)
+ {
+ hi->arv6->RecordContext = mDNSNULL; // about to free wrapper struct
+ if (hi->arv6->uDNS_info.state != regState_Unregistered) uDNS_DeregisterRecord(m, hi->arv6);
+ else { ufree(hi->arv6); hi->arv6 = mDNSNULL; }
+ }
+ ufree(hi);
+ }
+ UpdateSRVRecords(m);
+ mDNS_Unlock(m);
+ }
+
+mDNSexport void mDNS_SetPrimaryInterfaceInfo(mDNS *m, const mDNSAddr *v4addr, const mDNSAddr *v6addr, const mDNSAddr *router)
+ {
+ uDNS_GlobalInfo *u = &m->uDNS_info;
+ mDNSBool v4Changed, v6Changed, RouterChanged;
+
+ if (v4addr && v4addr->type != mDNSAddrType_IPv4) { LogMsg("mDNS_SetPrimaryInterfaceInfo V4 address - incorrect type. Discarding."); return; }
+ if (v6addr && v6addr->type != mDNSAddrType_IPv6) { LogMsg("mDNS_SetPrimaryInterfaceInfo V6 address - incorrect type. Discarding."); return; }
+ if (router && router->type != mDNSAddrType_IPv4) { LogMsg("mDNS_SetPrimaryInterfaceInfo passed non-V4 router. Discarding."); return; }
+
+ mDNS_Lock(m);
+
+ v4Changed = (v4addr ? v4addr->ip.v4.NotAnInteger : 0) != u->AdvertisedV4.ip.v4.NotAnInteger;
+ v6Changed = v6addr ? !mDNSPlatformMemSame(v6addr, &u->AdvertisedV6, sizeof(*v6addr)) : (u->AdvertisedV6.ip.v6.b[0] != 0);
+ RouterChanged = (router ? router->ip.v4.NotAnInteger : 0) != u->Router.ip.v4.NotAnInteger;
+
+#if MDNS_DEBUGMSGS
+ if (v4addr && (v4Changed || RouterChanged))
+ LogMsg("mDNS_SetPrimaryInterfaceInfo: address changed from %d.%d.%d.%d to %d.%d.%d.%d:%d",
+ u->AdvertisedV4.ip.v4.b[0], u->AdvertisedV4.ip.v4.b[1], u->AdvertisedV4.ip.v4.b[2], u->AdvertisedV4.ip.v4.b[3],
+ v4addr->ip.v4.b[0], v4addr->ip.v4.b[1], v4addr->ip.v4.b[2], v4addr->ip.v4.b[3]);
+#endif // MDNS_DEBUGMSGS
+
+ if ((v4Changed || RouterChanged) && u->MappedV4.ip.v4.NotAnInteger) u->MappedV4.ip.v4.NotAnInteger = 0;
+ if (v4addr) u->AdvertisedV4 = *v4addr; else u->AdvertisedV4.ip.v4.NotAnInteger = 0;
+ if (v6addr) u->AdvertisedV6 = *v6addr; else ubzero(u->AdvertisedV6.ip.v6.b, 16);
+ if (router) u->Router = *router; else u->Router.ip.v4.NotAnInteger = 0;
+ // setting router to zero indicates that nat mappings must be reestablished when router is reset
+
+ if ((v4Changed || RouterChanged || v6Changed) && v4addr)
+ {
+ // don't update these unless we've got V4
+ UpdateHostnameRegistrations(m);
+ UpdateSRVRecords(m);
+ GetStaticHostname(m); // look up reverse map record to find any static hostnames for our IP address
+ }
+
+ mDNS_Unlock(m);
+ }
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark - Incoming Message Processing
+#endif
+
+mDNSlocal mDNSBool kaListContainsAnswer(DNSQuestion *question, CacheRecord *rr)
+ {
+ CacheRecord *ptr;
+
+ for (ptr = question->uDNS_info.knownAnswers; ptr; ptr = ptr->next)
+ if (SameResourceRecord(&ptr->resrec, &rr->resrec)) return mDNStrue;
+
+ return mDNSfalse;
+ }
+
+
+mDNSlocal void removeKnownAnswer(DNSQuestion *question, CacheRecord *rr)
+ {
+ CacheRecord *ptr, *prev = mDNSNULL;
+
+ for (ptr = question->uDNS_info.knownAnswers; ptr; ptr = ptr->next)
+ {
+ if (SameResourceRecord(&ptr->resrec, &rr->resrec))
+ {
+ if (prev) prev->next = ptr->next;
+ else question->uDNS_info.knownAnswers = ptr->next;
+ ufree(ptr);
+ return;
+ }
+ prev = ptr;
+ }
+ LogMsg("removeKnownAnswer() called for record not in KA list");
+ }
+
+
+mDNSlocal void addKnownAnswer(DNSQuestion *question, const CacheRecord *rr)
+ {
+ CacheRecord *newCR = mDNSNULL;
+ mDNSu32 size;
+
+ size = sizeof(CacheRecord) + rr->resrec.rdlength - InlineCacheRDSize;
+ newCR = (CacheRecord *)umalloc(size);
+ if (!newCR) { LogMsg("ERROR: addKnownAnswer - malloc"); return; }
+ umemcpy(newCR, rr, size);
+ newCR->resrec.rdata = (RData*)&newCR->rdatastorage;
+ newCR->resrec.rdata->MaxRDLength = rr->resrec.rdlength;
+ newCR->resrec.name = &question->qname;
+ newCR->next = question->uDNS_info.knownAnswers;
+ question->uDNS_info.knownAnswers = newCR;
+ }
+
+mDNSlocal void deriveGoodbyes(mDNS * const m, DNSMessage *msg, const mDNSu8 *end, DNSQuestion *question)
+ {
+ const mDNSu8 *ptr;
+ int i;
+ CacheRecord *fptr, *ka, *cr, *answers = mDNSNULL, *prev = mDNSNULL;
+ LargeCacheRecord *lcr;
+
+ if (question != m->uDNS_info.CurrentQuery) { LogMsg("ERROR: deriveGoodbyes called without CurrentQuery set!"); return; }
+
+ ptr = LocateAnswers(msg, end);
+ if (!ptr) goto pkt_error;
+
+ if (!msg->h.numAnswers)
+ {
+ // delete the whole KA list
+ ka = question->uDNS_info.knownAnswers;
+ while (ka)
+ {
+ debugf("deriving goodbye for %##s", ka->resrec.name->c);
+
+ m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback
+ question->QuestionCallback(m, question, &ka->resrec, mDNSfalse);
+ m->mDNS_reentrancy--; // Decrement to block mDNS API calls again
+ // CAUTION: Need to be careful after calling question->QuestionCallback(),
+ // because the client's callback function is allowed to do anything,
+ // including starting/stopping queries, registering/deregistering records, etc.
+ if (question != m->uDNS_info.CurrentQuery)
+ {
+ debugf("deriveGoodbyes - question removed via callback. returning.");
+ return;
+ }
+ fptr = ka;
+ ka = ka->next;
+ ufree(fptr);
+ }
+ question->uDNS_info.knownAnswers = mDNSNULL;
+ return;
+ }
+
+ // make a list of all the new answers
+ for (i = 0; i < msg->h.numAnswers; i++)
+ {
+ lcr = (LargeCacheRecord *)umalloc(sizeof(LargeCacheRecord));
+ if (!lcr) goto malloc_error;
+ ubzero(lcr, sizeof(LargeCacheRecord));
+ ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAns, lcr);
+ if (!ptr) goto pkt_error;
+ cr = &lcr->r;
+ if (ResourceRecordAnswersQuestion(&cr->resrec, question))
+ {
+ cr->next = answers;
+ answers = cr;
+ }
+ else ufree(cr);
+ }
+
+ // make sure every known answer is in the answer list
+ ka = question->uDNS_info.knownAnswers;
+ while (ka)
+ {
+ for (cr = answers; cr; cr = cr->next)
+ { if (SameResourceRecord(&ka->resrec, &cr->resrec)) break; }
+ if (!cr)
+ {
+ // record is in KA list but not answer list - remove from KA list
+ if (prev) prev->next = ka->next;
+ else question->uDNS_info.knownAnswers = ka->next;
+ debugf("deriving goodbye for %##s", ka->resrec.name->c);
+ m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback
+ question->QuestionCallback(m, question, &ka->resrec, mDNSfalse);
+ m->mDNS_reentrancy--; // Decrement to block mDNS API calls again
+ // CAUTION: Need to be careful after calling question->QuestionCallback(),
+ // because the client's callback function is allowed to do anything,
+ // including starting/stopping queries, registering/deregistering records, etc.
+ if (question != m->uDNS_info.CurrentQuery)
+ {
+ debugf("deriveGoodbyes - question removed via callback. returning.");
+ return;
+ }
+ fptr = ka;
+ ka = ka->next;
+ ufree(fptr);
+ }
+ else
+ {
+ prev = ka;
+ ka = ka->next;
+ }
+ }
+
+ // free temp answers list
+ cr = answers;
+ while (cr) { fptr = cr; cr = cr->next; ufree(fptr); }
+
+ return;
+
+ pkt_error:
+ LogMsg("ERROR: deriveGoodbyes - received malformed response to query for %##s (%d)",
+ question->qname.c, question->qtype);
+ return;
+
+ malloc_error:
+ LogMsg("ERROR: Malloc");
+ }
+
+mDNSlocal void pktResponseHndlr(mDNS * const m, DNSMessage *msg, const mDNSu8 *end, DNSQuestion *question, mDNSBool llq)
+ {
+ const mDNSu8 *ptr;
+ int i;
+ CacheRecord *cr = &m->rec.r;
+ mDNSBool goodbye, inKAList;
+ int followedCNames = 0;
+ static const int maxCNames = 5;
+ LLQ_Info *llqInfo = question->uDNS_info.llq;
+ domainname origname;
+ origname.c[0] = 0;
+
+ if (question != m->uDNS_info.CurrentQuery)
+ { LogMsg("ERROR: pktResponseHdnlr called without CurrentQuery ptr set!"); return; }
+
+ if (question->uDNS_info.Answered == 0 && msg->h.numAnswers == 0 && !llq)
+ {
+ /* NXDOMAIN error or empty RR set - notify client */
+ question->uDNS_info.Answered = mDNStrue;
+
+ /* Create empty resource record */
+ cr->resrec.RecordType = kDNSRecordTypeUnregistered;
+ cr->resrec.InterfaceID = mDNSNULL;
+ cr->resrec.name = &question->qname;
+ cr->resrec.rrtype = question->qtype;
+ cr->resrec.rrclass = question->qclass;
+ cr->resrec.rroriginalttl = 1; /* What should we use for the TTL? TTL from SOA for domain? */
+ cr->resrec.rdlength = 0;
+ cr->resrec.rdestimate = 0;
+ cr->resrec.namehash = 0;
+ cr->resrec.namehash = 0;
+ cr->resrec.rdata = (RData*)&cr->rdatastorage;
+ cr->resrec.rdata->MaxRDLength = cr->resrec.rdlength;
+
+ /* Pass empty answer to callback */
+ m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback
+ question->QuestionCallback(m, question, &cr->resrec, 0);
+ m->mDNS_reentrancy--; // Decrement to block mDNS API calls again
+ // CAUTION: Need to be careful after calling question->QuestionCallback(),
+ // because the client's callback function is allowed to do anything,
+ // including starting/stopping queries, registering/deregistering records, etc.
+ m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
+ if (question != m->uDNS_info.CurrentQuery)
+ {
+ debugf("pktResponseHndlr - CurrentQuery changed by QuestionCallback - returning.");
+ return;
+ }
+ }
+
+ question->uDNS_info.Answered = mDNStrue;
+
+ ptr = LocateAnswers(msg, end);
+ if (!ptr) goto pkt_error;
+
+ for (i = 0; i < msg->h.numAnswers; i++)
+ {
+ ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAns, &m->rec);
+ if (!ptr) goto pkt_error;
+ if (ResourceRecordAnswersQuestion(&cr->resrec, question))
+ {
+ goodbye = llq ? ((mDNSs32)cr->resrec.rroriginalttl == -1) : mDNSfalse;
+ if (cr->resrec.rrtype == kDNSType_CNAME)
+ {
+ if (followedCNames > (maxCNames - 1)) LogMsg("Error: too many CNAME referals for question %##s", &origname);
+ else
+ {
+ debugf("Following cname %##s -> %##s", question->qname.c, cr->resrec.rdata->u.name.c);
+ if (question->ReturnCNAME)
+ {
+ m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback
+ question->QuestionCallback(m, question, &cr->resrec, !goodbye);
+ m->mDNS_reentrancy--; // Decrement to block mDNS API calls again
+ // CAUTION: Need to be careful after calling question->QuestionCallback(),
+ // because the client's callback function is allowed to do anything,
+ // including starting/stopping queries, registering/deregistering records, etc.
+ if (question != m->uDNS_info.CurrentQuery)
+ {
+ m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
+ debugf("pktResponseHndlr - CurrentQuery changed by QuestionCallback - returning.");
+ return;
+ }
+ }
+ AssignDomainName(&origname, &question->qname);
+ AssignDomainName(&question->qname, &cr->resrec.rdata->u.name);
+ question->qnamehash = DomainNameHashValue(&question->qname);
+ followedCNames++;
+ i = -1; // restart packet answer matching
+ ptr = LocateAnswers(msg, end);
+ m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
+ continue;
+ }
+ }
+
+ inKAList = kaListContainsAnswer(question, cr);
+
+ if ((goodbye && !inKAList) || (!goodbye && inKAList))
+ {
+ m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
+ continue; // list up to date
+ }
+ if (!inKAList) addKnownAnswer(question, cr);
+ if (goodbye) removeKnownAnswer(question, cr);
+ m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback
+ question->QuestionCallback(m, question, &cr->resrec, !goodbye);
+ m->mDNS_reentrancy--; // Decrement to block mDNS API calls again
+ if (question != m->uDNS_info.CurrentQuery)
+ {
+ m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
+ debugf("pktResponseHndlr - CurrentQuery changed by QuestionCallback - returning");
+ return;
+ }
+ }
+
+ m->rec.r.resrec.RecordType = 0; // Clear RecordType to show we're not still using it
+ }
+
+ if (!llq || llqInfo->state == LLQ_Poll || llqInfo->deriveRemovesOnResume)
+ {
+ deriveGoodbyes(m, msg, end,question);
+ if (llq && llqInfo->deriveRemovesOnResume) llqInfo->deriveRemovesOnResume = mDNSfalse;
+ }
+
+ // Our interval may be set lower to recover from failures -- now that we have an answer, fully back off retry.
+ // If the server advertised an LLQ-specific port number then that implies that this zone
+ // *wants* to support LLQs, so if the setup fails (e.g. because we are behind a NAT)
+ // then we use a slightly faster polling rate to give slightly better user experience.
+ if (llq && llqInfo->state == LLQ_Poll && llqInfo->servPort.NotAnInteger) question->ThisQInterval = LLQ_POLL_INTERVAL;
+ else if (question->ThisQInterval < MAX_UCAST_POLL_INTERVAL) question->ThisQInterval = MAX_UCAST_POLL_INTERVAL;
+ return;
+
+ pkt_error:
+ LogMsg("ERROR: pktResponseHndlr - received malformed response to query for %##s (%d)",
+ question->qname.c, question->qtype);
+ return;
+ }
+
+mDNSlocal void simpleResponseHndlr(mDNS * const m, DNSMessage *msg, const mDNSu8 *end, DNSQuestion *question, void *context)
+ {
+ (void)context; // unused
+ pktResponseHndlr(m, msg, end, question, mDNSfalse);
+ }
+
+mDNSlocal void llqResponseHndlr(mDNS * const m, DNSMessage *msg, const mDNSu8 *end, DNSQuestion *question, void *context)
+ {
+ (void)context; // unused
+ pktResponseHndlr(m, msg, end, question, mDNStrue);
+ }
+
+mDNSlocal mStatus ParseTSIGError(mDNS *m, const DNSMessage *msg, const mDNSu8 *end, const domainname *displayname)
+ {
+ LargeCacheRecord lcr;
+ 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, &lcr);
+ if (!ptr) goto finish;
+ if (lcr.r.resrec.rrtype == kDNSType_TSIG)
+ {
+ mDNSu32 macsize;
+ mDNSu8 *rd = lcr.r.resrec.rdata->u.data;
+ mDNSu8 *rdend = rd + MaximumRDSize;
+ int alglen = DomainNameLength(&lcr.r.resrec.rdata->u.name);
+
+ if (rd + alglen > rdend) 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;
+ }
+ }
+
+ finish:
+ return err;
+ }
+
+mDNSlocal mStatus checkUpdateResult(domainname *displayname, mDNSu8 rcode, mDNS *m, const DNSMessage *msg, const mDNSu8 *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 FmtErr 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_FmtErr)
+ {
+ 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;
+ }
+ }
+
+mDNSlocal void hndlServiceUpdateReply(mDNS * const m, ServiceRecordSet *srs, mStatus err)
+ {
+ mDNSBool InvokeCallback = mDNSfalse;
+ uDNS_RegInfo *info = &srs->uDNS_info;
+ NATTraversalInfo *nat = srs->uDNS_info.NATinfo;
+ ExtraResourceRecord **e = &srs->Extras;
+ AuthRecord *txt = &srs->RR_TXT;
+ uDNS_RegInfo *txtInfo = &txt->uDNS_info;
+ switch (info->state)
+ {
+ case regState_Pending:
+ if (err == mStatus_NameConflict && !info->TestForSelfConflict)
+ {
+ info->TestForSelfConflict = mDNStrue;
+ debugf("checking for self-conflict of service %##s", srs->RR_SRV.resrec.name->c);
+ SendServiceRegistration(m, srs);
+ return;
+ }
+ else if (info->TestForSelfConflict)
+ {
+ info->TestForSelfConflict = mDNSfalse;
+ if (err == mStatus_NoSuchRecord) err = mStatus_NameConflict; // NoSuchRecord implies that our prereq was not met, so we actually have a name conflict
+ if (err) info->state = regState_Unregistered;
+ else info->state = regState_Registered;
+ InvokeCallback = mDNStrue;
+ break;
+ }
+ else if (err == mStatus_UnknownErr && info->lease)
+ {
+ LogMsg("Re-trying update of service %##s without lease option", srs->RR_SRV.resrec.name->c);
+ info->lease = mDNSfalse;
+ SendServiceRegistration(m, srs);
+ return;
+ }
+ else
+ {
+ if (err) { LogMsg("Error %ld for registration of service %##s", err, srs->RR_SRV.resrec.name->c); info->state = regState_Unregistered; } //!!!KRS make sure all structs will still get cleaned up when client calls DeregisterService with this state
+ else info->state = regState_Registered;
+ InvokeCallback = mDNStrue;
+ break;
+ }
+ case regState_Refresh:
+ if (err)
+ {
+ LogMsg("Error %ld for refresh of service %##s", err, srs->RR_SRV.resrec.name->c);
+ InvokeCallback = mDNStrue;
+ info->state = regState_Unregistered;
+ }
+ else info->state = regState_Registered;
+ break;
+ case regState_DeregPending:
+ if (err) LogMsg("Error %ld for deregistration of service %##s", err, srs->RR_SRV.resrec.name->c);
+ if (info->SRVChanged)
+ {
+ info->state = regState_NoTarget; // NoTarget will allow us to pick up new target OR nat traversal state
+ break;
+ }
+ err = mStatus_MemFree;
+ InvokeCallback = mDNStrue;
+ if (nat)
+ {
+ if (nat->state == NATState_Deleted) { info->NATinfo = mDNSNULL; FreeNATInfo(m, nat); } // deletion copmleted
+ else nat->reg.ServiceRegistration = mDNSNULL; // allow mapping deletion to continue
+ }
+ info->state = regState_Unregistered;
+ break;
+ case regState_DeregDeferred:
+ if (err)
+ {
+ debugf("Error %ld received prior to deferred derigstration of %##s", err, srs->RR_SRV.resrec.name->c);
+ err = mStatus_MemFree;
+ InvokeCallback = mDNStrue;
+ info->state = regState_Unregistered;
+ break;
+ }
+ else
+ {
+ debugf("Performing deferred deregistration of %##s", srs->RR_SRV.resrec.name->c);
+ info->state = regState_Registered;
+ SendServiceDeregistration(m, srs);
+ return;
+ }
+ case regState_UpdatePending:
+ if (err)
+ {
+ LogMsg("hndlServiceUpdateReply: error updating TXT record for service %##s", srs->RR_SRV.resrec.name->c);
+ info->state = regState_Unregistered;
+ InvokeCallback = mDNStrue;
+ }
+ else
+ {
+ info->state = regState_Registered;
+ // deallocate old RData
+ if (txtInfo->UpdateRDCallback) txtInfo->UpdateRDCallback(m, txt, txtInfo->OrigRData);
+ SetNewRData(&txt->resrec, txtInfo->InFlightRData, txtInfo->InFlightRDLen);
+ txtInfo->OrigRData = mDNSNULL;
+ txtInfo->InFlightRData = mDNSNULL;
+ }
+ break;
+ case regState_FetchingZoneData:
+ case regState_Registered:
+ case regState_Cancelled:
+ case regState_Unregistered:
+ case regState_NATMap:
+ case regState_NoTarget:
+ case regState_ExtraQueued:
+ case regState_NATError:
+ LogMsg("hndlServiceUpdateReply called for service %##s in unexpected state %d with error %ld. Unlinking.",
+ srs->RR_SRV.resrec.name->c, info->state, err);
+ err = mStatus_UnknownErr;
+ }
+
+ if ((info->SRVChanged || info->SRVUpdateDeferred) && (info->state == regState_NoTarget || info->state == regState_Registered))
+ {
+ if (InvokeCallback)
+ {
+ info->ClientCallbackDeferred = mDNStrue;
+ info->DeferredStatus = err;
+ }
+ info->SRVChanged = mDNSfalse;
+ UpdateSRV(m, srs);
+ return;
+ }
+
+ while (*e)
+ {
+ uDNS_RegInfo *einfo = &(*e)->r.uDNS_info;
+ if (einfo->state == regState_ExtraQueued)
+ {
+ if (info->state == regState_Registered && !err)
+ {
+ // extra resource record queued for this service - copy zone info and register
+ AssignDomainName(&einfo->zone, &info->zone);
+ einfo->ns = info->ns;
+ einfo->port = info->port;
+ einfo->lease = info->lease;
+ sendRecordRegistration(m, &(*e)->r);
+ e = &(*e)->next;
+ }
+ else if (err && einfo->state != regState_Unregistered)
+ {
+ // unlink extra from list
+ einfo->state = regState_Unregistered;
+ *e = (*e)->next;
+ }
+ else e = &(*e)->next;
+ }
+ else e = &(*e)->next;
+ }
+
+ srs->RR_SRV.ThisAPInterval = INIT_UCAST_POLL_INTERVAL - 1; // reset retry delay for future refreshes, dereg, etc.
+ if (info->state == regState_Unregistered) unlinkSRS(m, srs);
+ else if (txtInfo->QueuedRData && info->state == regState_Registered)
+ {
+ if (InvokeCallback)
+ {
+ // if we were supposed to give a client callback, we'll do it after we update the primary txt record
+ info->ClientCallbackDeferred = mDNStrue;
+ info->DeferredStatus = err;
+ }
+ info->state = regState_UpdatePending;
+ txtInfo->InFlightRData = txtInfo->QueuedRData;
+ txtInfo->InFlightRDLen = txtInfo->QueuedRDLen;
+ info->OrigRData = txt->resrec.rdata;
+ info->OrigRDLen = txt->resrec.rdlength;
+ txtInfo->QueuedRData = mDNSNULL;
+ SendServiceRegistration(m, srs);
+ return;
+ }
+
+ m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback
+ if (InvokeCallback) srs->ServiceCallback(m, srs, err);
+ else if (info->ClientCallbackDeferred)
+ {
+ info->ClientCallbackDeferred = mDNSfalse;
+ srs->ServiceCallback(m, srs, info->DeferredStatus);
+ }
+ m->mDNS_reentrancy--; // Decrement to block mDNS API calls again
+ // NOTE: do not touch structures after calling ServiceCallback
+ }
+
+mDNSlocal void hndlRecordUpdateReply(mDNS *m, AuthRecord *rr, mStatus err)
+ {
+ uDNS_RegInfo *info = &rr->uDNS_info;
+ mDNSBool InvokeCallback = mDNStrue;
+
+ if (info->state == regState_UpdatePending)
+ {
+ if (err)
+ {
+ LogMsg("Update record failed for %##s (err %d)", rr->resrec.name->c, err);
+ info->state = regState_Unregistered;
+ }
+ else
+ {
+ debugf("Update record %##s - success", rr->resrec.name->c);
+ info->state = regState_Registered;
+ // deallocate old RData
+ if (info->UpdateRDCallback) info->UpdateRDCallback(m, rr, info->OrigRData);
+ SetNewRData(&rr->resrec, info->InFlightRData, info->InFlightRDLen);
+ info->OrigRData = mDNSNULL;
+ info->InFlightRData = mDNSNULL;
+ }
+ }
+
+ if (info->state == regState_DeregPending)
+ {
+ debugf("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 %ld",
+ rr->resrec.name->c, rr->resrec.rrtype, err);
+ err = mStatus_MemFree;
+ info->state = regState_Unregistered;
+ }
+
+ if (info->state == regState_DeregDeferred)
+ {
+ if (err)
+ {
+ LogMsg("Cancelling deferred deregistration record %##s type %d due to registration error %ld",
+ rr->resrec.name->c, rr->resrec.rrtype, err);
+ info->state = regState_Unregistered;
+ }
+ debugf("Calling deferred deregistration of record %##s type %d", rr->resrec.name->c, rr->resrec.rrtype);
+ info->state = regState_Registered;
+ uDNS_DeregisterRecord(m, rr);
+ return;
+ }
+
+ if (info->state == regState_Pending || info->state == regState_Refresh)
+ {
+ if (!err)
+ {
+ info->state = regState_Registered;
+ if (info->state == regState_Refresh) InvokeCallback = mDNSfalse;
+ }
+ else
+ {
+ if (info->lease && err == mStatus_UnknownErr)
+ {
+ LogMsg("Re-trying update of record %##s without lease option", rr->resrec.name->c);
+ info->lease = mDNSfalse;
+ sendRecordRegistration(m, rr);
+ return;
+ }
+ LogMsg("Registration of record %##s type %d failed with error %ld", rr->resrec.name->c, rr->resrec.rrtype, err);
+ info->state = regState_Unregistered;
+ }
+ }
+
+ if (info->state == regState_Unregistered) unlinkAR(&m->uDNS_info.RecordRegistrations, rr);
+ else rr->ThisAPInterval = INIT_UCAST_POLL_INTERVAL - 1; // reset retry delay for future refreshes, dereg, etc.
+
+ if (info->QueuedRData && info->state == regState_Registered)
+ {
+ info->state = regState_UpdatePending;
+ info->InFlightRData = info->QueuedRData;
+ info->InFlightRDLen = info->QueuedRDLen;
+ info->OrigRData = rr->resrec.rdata;
+ info->OrigRDLen = rr->resrec.rdlength;
+ info->QueuedRData = mDNSNULL;
+ sendRecordRegistration(m, rr);
+ return;
+ }
+
+ if (InvokeCallback)
+ {
+ m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback
+ if (rr->RecordCallback) rr->RecordCallback(m, rr, err);
+ m->mDNS_reentrancy--; // Decrement to block mDNS API calls again
+ }
+ }
+
+
+mDNSlocal void SetUpdateExpiration(mDNS *m, DNSMessage *msg, const mDNSu8 *end, uDNS_RegInfo *info)
+ {
+ LargeCacheRecord lcr;
+ const mDNSu8 *ptr;
+ int i;
+ mDNSu32 lease = 0;
+ mDNSs32 expire;
+
+ ptr = LocateAdditionals(msg, end);
+
+ if (info->lease && (ptr = LocateAdditionals(msg, end)))
+ {
+ for (i = 0; i < msg->h.numAdditionals; i++)
+ {
+ ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &lcr);
+ if (!ptr) break;
+ if (lcr.r.resrec.rrtype == kDNSType_OPT)
+ {
+ if (lcr.r.resrec.rdlength < LEASE_OPT_RDLEN) continue;
+ if (lcr.r.resrec.rdata->u.opt.opt != kDNSOpt_Lease) continue;
+ lease = lcr.r.resrec.rdata->u.opt.OptData.lease;
+ break;
+ }
+ }
+ }
+
+ if (lease > 0)
+ {
+ expire = (mDNSPlatformTimeNow(m) + (((mDNSs32)lease * mDNSPlatformOneSecond)) * 3/4);
+ if (info->state == regState_UpdatePending)
+ // if updating individual record, the service record set may expire sooner
+ { if (expire - info->expire < 0) info->expire = expire; }
+ else info->expire = expire;
+ }
+ else info->lease = mDNSfalse;
+ }
+
+mDNSexport void uDNS_ReceiveNATMap(mDNS *m, mDNSu8 *pkt, mDNSu16 len)
+ {
+ uDNS_GlobalInfo *u = &m->uDNS_info;
+ NATTraversalInfo *ptr = u->NATTraversals;
+ NATOp_t op;
+
+ // check length, version, opcode
+ if (len < sizeof(NATPortMapReply) && len < sizeof(NATAddrReply)) { LogMsg("NAT Traversal message too short (%d bytes)", len); return; }
+ if (pkt[0] != NATMAP_VERS) { LogMsg("Received NAT Traversal response with version %d (expect version %d)", pkt[0], NATMAP_VERS); return; }
+ op = pkt[1];
+ if (!(op & NATMAP_RESPONSE_MASK)) { LogMsg("Received NAT Traversal message that is not a response (opcode %d)", op); return; }
+
+ while (ptr)
+ {
+ if ((ptr->state == NATState_Request || ptr->state == NATState_Refresh) && (ptr->op | NATMAP_RESPONSE_MASK) == op)
+ if (ptr->ReceiveResponse(ptr, m, pkt, len)) break; // note callback may invalidate ptr if it return value is non-zero
+ ptr = ptr->next;
+ }
+ }
+
+mDNSlocal const domainname *DNSRelayTestQuestion = (domainname*)
+ "\x1" "1" "\x1" "0" "\x1" "0" "\x3" "127" "\xa" "dnsbugtest"
+ "\x1" "1" "\x1" "0" "\x1" "0" "\x3" "127" "\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 mDNSInterfaceID InterfaceID)
+ {
+ const mDNSu8 *ptr = msg->data;
+ DNSQuestion q;
+ DNSServer *s;
+ mDNSu32 result = 0;
+ mDNSBool found = mDNSfalse;
+
+ // 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, InterfaceID, &q);
+ if (!ptr) return(mDNSfalse);
+ if (q.qtype != kDNSType_PTR || q.qclass != kDNSClass_IN) return(mDNSfalse);
+ if (!SameDomainName(&q.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) == 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->uDNS_info.Servers; s; s = s->next)
+ if (mDNSSameAddress(srcaddr, &s->addr) && s->teststate != result)
+ { s->teststate = result; found = mDNStrue; }
+
+ // 4. Assuming we found the server in question in our list (don't want to risk being victim of a deliberate DOS attack here)
+ // log a message to let the user know why Wide-Area Service Discovery isn't working
+ if (found && result == DNSServer_Failed)
+ LogMsg("NOTE: Wide-Area Service Discovery disabled to avoid crashing defective DNS relay %#a.", srcaddr);
+
+ return(mDNStrue); // Return mDNStrue to tell uDNS_ReceiveMsg it doens't need to process this packet further
+ }
+
+mDNSexport void uDNS_ReceiveMsg(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)
+ {
+ DNSQuestion *qptr;
+ AuthRecord *rptr;
+ ServiceRecordSet *sptr;
+ mStatus err = mStatus_NoError;
+ uDNS_GlobalInfo *u = &m->uDNS_info;
+
+ 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);
+
+ mDNSs32 timenow = mDNSPlatformTimeNow(m);
+
+ // unused
+ (void)dstaddr;
+ (void)dstport;
+ (void)InterfaceID;
+
+ if (QR_OP == StdR)
+ {
+ // !!!KRS we should to a table lookup here to see if it answers an LLQ or a 1-shot
+ // LLQ Responses over TCP not currently supported
+ if (srcaddr && recvLLQResponse(m, msg, end, srcaddr, srcport, InterfaceID)) return;
+
+ if (uDNS_ReceiveTestQuestionResponse(m, msg, end, srcaddr, InterfaceID)) return;
+
+ for (qptr = u->ActiveQueries; qptr; qptr = qptr->next)
+ {
+ //!!!KRS we should have a hashtable, hashed on message id
+ if (qptr->uDNS_info.id.NotAnInteger == msg->h.id.NotAnInteger)
+ {
+ if (timenow - (qptr->LastQTime + RESPONSE_WINDOW) > 0)
+ { debugf("uDNS_ReceiveMsg - response received after maximum allowed window. Discarding"); return; }
+ if (msg->h.flags.b[0] & kDNSFlag0_TC)
+ { hndlTruncatedAnswer(qptr, srcaddr, m); return; }
+ else
+ {
+ u->CurrentQuery = qptr;
+ qptr->uDNS_info.responseCallback(m, msg, end, qptr, qptr->uDNS_info.context);
+ u->CurrentQuery = mDNSNULL;
+ // Note: responseCallback can invalidate qptr
+ return;
+ }
+ }
+ }
+ }
+ if (QR_OP == UpdateR)
+ {
+ for (sptr = u->ServiceRegistrations; sptr; sptr = sptr->next)
+ {
+ if (sptr->uDNS_info.id.NotAnInteger == msg->h.id.NotAnInteger)
+ {
+ err = checkUpdateResult(sptr->RR_SRV.resrec.name, rcode, m, msg, end);
+ if (!err) SetUpdateExpiration(m, msg, end, &sptr->uDNS_info);
+ hndlServiceUpdateReply(m, sptr, err);
+ return;
+ }
+ }
+ for (rptr = u->RecordRegistrations; rptr; rptr = rptr->next)
+ {
+ if (rptr->uDNS_info.id.NotAnInteger == msg->h.id.NotAnInteger)
+ {
+ err = checkUpdateResult(rptr->resrec.name, rcode, m, msg, end);
+ if (!err) SetUpdateExpiration(m, msg, end, &rptr->uDNS_info);
+ hndlRecordUpdateReply(m, rptr, err);
+ return;
+ }
+ }
+ }
+ debugf("Received unexpected response: ID %d matches no active records", mDNSVal16(msg->h.id));
+ }
+
+// lookup a DNS Server, matching by name in split-dns configurations. Result stored in addr parameter if successful
+mDNSlocal DNSServer *GetServerForName(uDNS_GlobalInfo *u, const domainname *name)
+ {
+ DNSServer *curmatch = mDNSNULL, *p = u->Servers;
+ int i, curmatchlen = -1;
+ int ncount = name ? CountLabels(name) : 0;
+
+ while (p)
+ {
+ int scount = CountLabels(&p->domain);
+ if (scount <= ncount && scount > curmatchlen)
+ {
+ // only inspect if server's domain is longer than current best match and shorter than the name itself
+ const domainname *tail = name;
+ for (i = 0; i < ncount - scount; i++)
+ tail = (domainname *)(tail->c + 1 + tail->c[0]); // find "tail" (scount labels) of name
+ if (SameDomainName(tail, &p->domain)) { curmatch = p; curmatchlen = scount; }
+ }
+ p = p->next;
+ }
+ return(curmatch);
+ }
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark - Query Routines
+#endif
+
+#define sameID(x,y) mDNSPlatformMemSame(x,y,8)
+
+mDNSlocal void initializeQuery(DNSMessage *msg, DNSQuestion *question)
+ {
+ ubzero(msg, sizeof(msg));
+ InitializeDNSMessage(&msg->h, question->uDNS_info.id, uQueryFlags);
+ }
+
+mDNSlocal mStatus constructQueryMsg(DNSMessage *msg, mDNSu8 **endPtr, DNSQuestion *const question)
+ {
+ initializeQuery(msg, question);
+
+ *endPtr = putQuestion(msg, msg->data, msg->data + AbsoluteMaxDNSMessageData, &question->qname, question->qtype, question->qclass);
+ if (!*endPtr)
+ {
+ LogMsg("ERROR: Unicast query out of space in packet");
+ return mStatus_UnknownErr;
+ }
+ return mStatus_NoError;
+ }
+
+mDNSlocal mDNSu8 *putLLQ(DNSMessage *const msg, mDNSu8 *ptr, DNSQuestion *question, LLQOptData *data, mDNSBool includeQuestion)
+ {
+ 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
+ if (includeQuestion)
+ {
+ 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)
+ ubzero(&rr, sizeof(AuthRecord));
+ mDNS_SetupResourceRecord(&rr, mDNSNULL, mDNSInterface_Any, kDNSType_OPT, kStandardTTL, kDNSRecordTypeKnownUnique, mDNSNULL, mDNSNULL);
+ opt->rdlength = LLQ_OPT_RDLEN;
+ opt->rdestimate = LLQ_OPT_RDLEN;
+
+ optRD = &rr.resrec.rdata->u.opt;
+ optRD->opt = kDNSOpt_LLQ;
+ optRD->optlen = LLQ_OPTLEN;
+ umemcpy(&optRD->OptData.llq, data, sizeof(*data));
+ ptr = PutResourceRecordTTLJumbo(msg, ptr, &msg->h.numAdditionals, opt, 0);
+ if (!ptr) { LogMsg("ERROR: putLLQ - PutResourceRecordTTLJumbo"); return mDNSNULL; }
+
+ return ptr;
+ }
+
+
+mDNSlocal mDNSBool getLLQAtIndex(mDNS *m, DNSMessage *msg, const mDNSu8 *end, LLQOptData *llq, int index)
+ {
+ LargeCacheRecord lcr;
+ int i;
+ const mDNSu8 *ptr;
+
+ ubzero(&lcr, sizeof(lcr));
+
+ ptr = LocateAdditionals(msg, end);
+ if (!ptr) return mDNSfalse;
+
+ // find the last additional
+ for (i = 0; i < msg->h.numAdditionals; i++)
+// { ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &lcr); if (!ptr) return mDNSfalse; }
+//!!!KRS workaround for LH server bug, which puts OPT as first additional
+ { ptr = GetLargeResourceRecord(m, msg, ptr, end, 0, kDNSRecordTypePacketAdd, &lcr); if (!ptr) return mDNSfalse; if (lcr.r.resrec.rrtype == kDNSType_OPT) break; }
+ if (lcr.r.resrec.rrtype != kDNSType_OPT) return mDNSfalse;
+ if (lcr.r.resrec.rdlength < (index + 1) * LLQ_OPT_RDLEN) return mDNSfalse; // rdata too small
+ umemcpy(llq, (mDNSu8 *)&lcr.r.resrec.rdata->u.opt.OptData.llq + (index * sizeof(*llq)), sizeof(*llq));
+ return mDNStrue;
+ }
+
+mDNSlocal void recvRefreshReply(mDNS *m, DNSMessage *msg, const mDNSu8 *end, DNSQuestion *q)
+ {
+ LLQ_Info *qInfo;
+ LLQOptData pktData;
+
+ qInfo = q->uDNS_info.llq;
+ if (!getLLQAtIndex(m, msg, end, &pktData, 0)) { LogMsg("ERROR recvRefreshReply - getLLQAtIndex"); return; }
+ if (pktData.llqOp != kLLQOp_Refresh) return;
+ if (!sameID(pktData.id, qInfo->id)) { LogMsg("recvRefreshReply - ID mismatch. Discarding"); return; }
+ if (pktData.err != LLQErr_NoError) { LogMsg("recvRefreshReply: received error %d from server", pktData.err); return; }
+
+ qInfo->expire = mDNSPlatformTimeNow(m) + ((mDNSs32)pktData.lease * mDNSPlatformOneSecond);
+ qInfo->retry = qInfo->expire - ((mDNSs32)pktData.lease * mDNSPlatformOneSecond/2);
+
+ qInfo->origLease = pktData.lease;
+ qInfo->state = LLQ_Established;
+ }
+
+mDNSlocal void sendLLQRefresh(mDNS *m, DNSQuestion *q, mDNSu32 lease)
+ {
+ DNSMessage msg;
+ mDNSu8 *end;
+ LLQOptData llq;
+ LLQ_Info *info = q->uDNS_info.llq;
+ mStatus err;
+ mDNSs32 timenow;
+
+ timenow = mDNSPlatformTimeNow(m);
+ if ((info->state == LLQ_Refresh && info->ntries >= kLLQ_MAX_TRIES) ||
+ info->expire - timenow < 0)
+ {
+ LogMsg("Unable to refresh LLQ %##s - will retry in %d minutes", q->qname.c, kLLQ_DEF_RETRY/60);
+ info->state = LLQ_Retry;
+ info->retry = mDNSPlatformTimeNow(m) + kLLQ_DEF_RETRY * mDNSPlatformOneSecond;
+ info->deriveRemovesOnResume = mDNStrue;
+ return;
+ //!!!KRS handle this - periodically try to re-establish
+ }
+
+ llq.vers = kLLQ_Vers;
+ llq.llqOp = kLLQOp_Refresh;
+ llq.err = LLQErr_NoError;
+ umemcpy(llq.id, info->id, 8);
+ llq.lease = lease;
+
+ initializeQuery(&msg, q);
+ end = putLLQ(&msg, msg.data, q, &llq, mDNStrue);
+ if (!end) { LogMsg("ERROR: sendLLQRefresh - putLLQ"); return; }
+
+ err = mDNSSendDNSMessage(m, &msg, end, mDNSInterface_Any, &info->servAddr, info->servPort, -1, mDNSNULL);
+ if (err) debugf("ERROR: sendLLQRefresh - mDNSSendDNSMessage returned %ld", err);
+
+ if (info->state == LLQ_Established) info->ntries = 1;
+ else info->ntries++;
+ info->state = LLQ_Refresh;
+ q->LastQTime = timenow;
+ info->retry = (info->expire - q->LastQTime) / 2;
+ }
+
+mDNSlocal mDNSBool recvLLQEvent(mDNS *m, DNSQuestion *q, DNSMessage *msg, const mDNSu8 *end, const mDNSAddr *srcaddr, mDNSIPPort srcport, mDNSInterfaceID InterfaceID)
+ {
+ DNSMessage ack;
+ mDNSu8 *ackEnd = ack.data;
+ mStatus err;
+ LLQOptData opt;
+
+ (void)InterfaceID; // unused
+
+ // find Opt RR, verify correct ID
+ if (!getLLQAtIndex(m, msg, end, &opt, 0)) { debugf("Pkt does not contain LLQ Opt"); return mDNSfalse; }
+ if (!q->uDNS_info.llq) { LogMsg("Error: recvLLQEvent - question object does not contain LLQ metadata"); return mDNSfalse; }
+ if (!sameID(opt.id, q->uDNS_info.llq->id)) { return mDNSfalse; }
+ if (opt.llqOp != kLLQOp_Event) { if (!q->uDNS_info.llq->ntries) LogMsg("recvLLQEvent - Bad LLQ Opcode %d", opt.llqOp); return mDNSfalse; }
+
+ // invoke response handler
+ m->uDNS_info.CurrentQuery = q;
+ q->uDNS_info.responseCallback(m, msg, end, q, q->uDNS_info.context);
+ if (m->uDNS_info.CurrentQuery != q) return mDNStrue;
+
+ // format and send ack
+ InitializeDNSMessage(&ack.h, msg->h.id, ResponseFlags);
+ ackEnd = putLLQ(&ack, ack.data, mDNSNULL, &opt, mDNSfalse);
+ if (!ackEnd) { LogMsg("ERROR: recvLLQEvent - putLLQ"); return mDNSfalse; }
+ err = mDNSSendDNSMessage(m, &ack, ackEnd, mDNSInterface_Any, srcaddr, srcport, -1, mDNSNULL);
+ if (err) debugf("ERROR: recvLLQEvent - mDNSSendDNSMessage returned %ld", err);
+ return mDNStrue;
+ }
+
+
+
+mDNSlocal void hndlChallengeResponseAck(mDNS *m, DNSMessage *pktMsg, const mDNSu8 *end, LLQOptData *llq, DNSQuestion *q)
+ {
+ LLQ_Info *info = q->uDNS_info.llq;
+
+ if (llq->err) { LogMsg("hndlChallengeResponseAck - received error %d from server", llq->err); goto error; }
+ if (!sameID(info->id, llq->id)) { LogMsg("hndlChallengeResponseAck - ID changed. discarding"); return; } // this can happen rarely (on packet loss + reordering)
+ info->expire = mDNSPlatformTimeNow(m) + ((mDNSs32)llq->lease * mDNSPlatformOneSecond);
+ info->retry = info->expire - ((mDNSs32)llq->lease * mDNSPlatformOneSecond / 2);
+
+ info->origLease = llq->lease;
+ info->state = LLQ_Established;
+
+ q->uDNS_info.responseCallback = llqResponseHndlr;
+ llqResponseHndlr(m, pktMsg, end, q, mDNSNULL);
+ return;
+
+ error:
+ info->state = LLQ_Error;
+ }
+
+mDNSlocal void sendChallengeResponse(mDNS *m, DNSQuestion *q, LLQOptData *llq)
+ {
+ LLQ_Info *info = q->uDNS_info.llq;
+ DNSMessage response;
+ mDNSu8 *responsePtr = response.data;
+ mStatus err;
+ LLQOptData llqBuf;
+ mDNSs32 timenow = mDNSPlatformTimeNow(m);
+
+ if (info->ntries++ == kLLQ_MAX_TRIES)
+ {
+ LogMsg("sendChallengeResponse: %d failed attempts for LLQ %##s. Will re-try in %d minutes",
+ kLLQ_MAX_TRIES, q->qname.c, kLLQ_DEF_RETRY / 60);
+ info->state = LLQ_Retry;
+ info->retry = timenow + (kLLQ_DEF_RETRY * mDNSPlatformOneSecond);
+ // !!!KRS give a callback error in these cases?
+ return;
+ }
+
+ if (!llq)
+ {
+ llq = &llqBuf;
+ llq->vers = kLLQ_Vers;
+ llq->llqOp = kLLQOp_Setup;
+ llq->err = LLQErr_NoError;
+ umemcpy(llq->id, info->id, 8);
+ llq->lease = info->origLease;
+ }
+
+ q->LastQTime = timenow;
+ info->retry = timenow + (kLLQ_INIT_RESEND * info->ntries * mDNSPlatformOneSecond);
+
+ if (constructQueryMsg(&response, &responsePtr, q)) goto error;
+ responsePtr = putLLQ(&response, responsePtr, q, llq, mDNSfalse);
+ if (!responsePtr) { LogMsg("ERROR: sendChallengeResponse - putLLQ"); goto error; }
+
+ err = mDNSSendDNSMessage(m, &response, responsePtr, mDNSInterface_Any, &info->servAddr, info->servPort, -1, mDNSNULL);
+ if (err) debugf("ERROR: sendChallengeResponse - mDNSSendDNSMessage returned %ld", err);
+ // on error, we procede as normal and retry after the appropriate interval
+
+ return;
+
+ error:
+ info->state = LLQ_Error;
+ }
+
+
+
+mDNSlocal void hndlRequestChallenge(mDNS *m, DNSMessage *pktMsg, const mDNSu8 *end, LLQOptData *llq, DNSQuestion *q)
+ {
+ LLQ_Info *info = q->uDNS_info.llq;
+ mDNSs32 timenow = mDNSPlatformTimeNow(m);
+ switch(llq->err)
+ {
+ case LLQErr_NoError: break;
+ case LLQErr_ServFull:
+ LogMsg("hndlRequestChallenge - received ServFull from server for LLQ %##s. Retry in %lu sec", q->qname.c, llq->lease);
+ info->retry = timenow + ((mDNSs32)llq->lease * mDNSPlatformOneSecond);
+ info->state = LLQ_Retry;
+ simpleResponseHndlr(m, pktMsg, end, q, mDNSNULL); // get available answers
+ info->deriveRemovesOnResume = mDNStrue;
+ case LLQErr_Static:
+ info->state = LLQ_Static;
+ LogMsg("LLQ %##s: static", q->qname.c);
+ simpleResponseHndlr(m, pktMsg, end, q, mDNSNULL);
+ return;
+ case LLQErr_FormErr:
+ LogMsg("ERROR: hndlRequestChallenge - received FormErr from server for LLQ %##s", q->qname.c);
+ goto error;
+ case LLQErr_BadVers:
+ LogMsg("ERROR: hndlRequestChallenge - received BadVers from server");
+ goto error;
+ case LLQErr_UnknownErr:
+ LogMsg("ERROR: hndlRequestChallenge - received UnknownErr from server for LLQ %##s", q->qname.c);
+ goto error;
+ default:
+ LogMsg("ERROR: hndlRequestChallenge - received invalid error %d for LLQ %##s", llq->err, q->qname.c);
+ goto error;
+ }
+
+ if (info->origLease != llq->lease)
+ debugf("hndlRequestChallenge: requested lease %lu, granted lease %lu", info->origLease, llq->lease);
+
+ // cache expiration in case we go to sleep before finishing setup
+ info->origLease = llq->lease;
+ info->expire = timenow + ((mDNSs32)llq->lease * mDNSPlatformOneSecond);
+
+ // update state
+ info->state = LLQ_SecondaryRequest;
+ umemcpy(info->id, llq->id, 8);
+ info->ntries = 0; // first attempt to send response
+
+ sendChallengeResponse(m, q, llq);
+ return;
+
+
+ error:
+ info->state = LLQ_Error;
+ }
+
+
+// response handler for initial and secondary setup responses
+mDNSlocal void recvSetupResponse(mDNS *m, DNSMessage *pktMsg, const mDNSu8 *end, DNSQuestion *q, void *clientContext)
+ {
+ DNSQuestion pktQuestion;
+ LLQOptData llq;
+ const mDNSu8 *ptr = pktMsg->data;
+ LLQ_Info *info = q->uDNS_info.llq;
+ mDNSu8 rcode = (mDNSu8)(pktMsg->h.flags.b[1] & kDNSFlag1_RC);
+
+ (void)clientContext; // unused
+
+ if (rcode && rcode != kDNSFlag1_RC_NXDomain) goto poll;
+
+ ptr = getQuestion(pktMsg, ptr, end, 0, &pktQuestion);
+ if (!ptr) { LogMsg("ERROR: recvSetupResponse - getQuestion"); goto poll; }
+ if (!SameDomainName(&q->qname, &pktQuestion.qname))
+ { LogMsg("ERROR: recvSetupResponse - mismatched question in response for llq setup %##s", q->qname.c); goto poll; }
+
+ if (!getLLQAtIndex(m, pktMsg, end, &llq, 0)) { debugf("recvSetupResponse - GetLLQAtIndex"); goto poll; }
+ if (llq.llqOp != kLLQOp_Setup) { LogMsg("ERROR: recvSetupResponse - bad op %d", llq.llqOp); goto poll; }
+ if (llq.vers != kLLQ_Vers) { LogMsg("ERROR: recvSetupResponse - bad vers %d", llq.vers); goto poll; }
+
+ if (info->state == LLQ_InitialRequest) { hndlRequestChallenge(m, pktMsg, end, &llq, q); return; }
+ if (info->state == LLQ_SecondaryRequest) { hndlChallengeResponseAck(m, pktMsg, end, &llq, q); return; }
+ LogMsg("recvSetupResponse - bad state %d", info->state);
+
+ poll:
+ info->state = LLQ_Poll;
+ q->uDNS_info.responseCallback = llqResponseHndlr;
+ info->question->LastQTime = mDNSPlatformTimeNow(m) - (2 * INIT_UCAST_POLL_INTERVAL); // trigger immediate poll
+ info->question->ThisQInterval = INIT_UCAST_POLL_INTERVAL;
+ }
+
+mDNSlocal void startLLQHandshake(mDNS *m, LLQ_Info *info, mDNSBool defer)
+ {
+ DNSMessage msg;
+ mDNSu8 *end;
+ LLQOptData llqData;
+ DNSQuestion *q = info->question;
+ mStatus err = mStatus_NoError;
+ mDNSs32 timenow = mDNSPlatformTimeNow(m);
+ uDNS_GlobalInfo *u = &m->uDNS_info;
+
+ if (IsPrivateV4Addr(&u->AdvertisedV4))
+ {
+ if (!u->LLQNatInfo)
+ {
+ info->state = LLQ_NatMapWait;
+ StartLLQNatMap(m);
+ return;
+ }
+ if (u->LLQNatInfo->state == NATState_Error) goto poll;
+ if (u->LLQNatInfo->state != NATState_Established && u->LLQNatInfo->state != NATState_Legacy)
+ { info->state = LLQ_NatMapWait; info->NATMap = mDNStrue; return; }
+ info->NATMap = mDNStrue; // this llq references the global llq nat mapping
+ }
+
+ if (info->ntries++ >= kLLQ_MAX_TRIES)
+ {
+ debugf("startLLQHandshake: %d failed attempts for LLQ %##s. Polling.", kLLQ_MAX_TRIES, q->qname.c, kLLQ_DEF_RETRY / 60);
+ goto poll;
+ }
+
+ // set llq rdata
+ llqData.vers = kLLQ_Vers;
+ llqData.llqOp = kLLQOp_Setup;
+ llqData.err = LLQErr_NoError;
+ ubzero(llqData.id, 8);
+ llqData.lease = kLLQ_DefLease;
+
+ initializeQuery(&msg, q);
+ end = putLLQ(&msg, msg.data, q, &llqData, mDNStrue);
+ if (!end)
+ {
+ LogMsg("ERROR: startLLQHandshake - putLLQ");
+ info->state = LLQ_Error;
+ return;
+ }
+
+ if (!defer) // if we are to defer, we simply set the retry timers so the request goes out in the future
+ {
+ err = mDNSSendDNSMessage(m, &msg, end, mDNSInterface_Any, &info->servAddr, info->servPort, -1, mDNSNULL);
+ if (err) debugf("ERROR: startLLQHandshake - mDNSSendDNSMessage returned %ld", err);
+ // on error, we procede as normal and retry after the appropriate interval
+ }
+
+ // update question/info state
+ info->state = LLQ_InitialRequest;
+ info->origLease = kLLQ_DefLease;
+ info->retry = timenow + (kLLQ_INIT_RESEND * mDNSPlatformOneSecond);
+ q->LastQTime = timenow;
+ q->uDNS_info.responseCallback = recvSetupResponse;
+ q->uDNS_info.internal = mDNStrue;
+ return;
+
+ poll:
+ info->question->uDNS_info.responseCallback = llqResponseHndlr;
+ info->state = LLQ_Poll;
+ info->question->LastQTime = mDNSPlatformTimeNow(m) - (2 * INIT_UCAST_POLL_INTERVAL); // trigger immediate poll
+ info->question->ThisQInterval = INIT_UCAST_POLL_INTERVAL;
+ }
+
+// wrapper for startLLQHandshake, invoked by async op callback
+mDNSlocal void startLLQHandshakeCallback(mStatus err, mDNS *const m, void *llqInfo, const AsyncOpResult *result)
+ {
+ LLQ_Info *info = (LLQ_Info *)llqInfo;
+ const zoneData_t *zoneInfo = mDNSNULL;
+
+ // check state first to make sure it is OK to touch question object
+ if (info->state == LLQ_Cancelled)
+ {
+ // StopQuery was called while we were getting the zone info
+ debugf("startLLQHandshake - LLQ Cancelled.");
+ info->question = mDNSNULL; // question may be deallocated
+ ufree(info);
+ return;
+ }
+
+ if (!info->question)
+ { LogMsg("ERROR: startLLQHandshakeCallback invoked with NULL question"); goto error; }
+
+ if (info->state != LLQ_GetZoneInfo)
+ { LogMsg("ERROR: startLLQHandshake - bad state %d", info->state); goto error; }
+
+ if (err)
+ { LogMsg("ERROR: startLLQHandshakeCallback %##s invoked with error code %ld", info->question->qname.c, err); goto poll; }
+
+ if (!result)
+ { LogMsg("ERROR: startLLQHandshakeCallback invoked with NULL result and no error code"); goto error; }
+
+ zoneInfo = &result->zoneData;
+
+ if (!zoneInfo->llqPort.NotAnInteger)
+ { debugf("LLQ port lookup failed - reverting to polling"); info->servPort.NotAnInteger = 0; goto poll; }
+
+ // cache necessary zone data
+ info->servAddr = zoneInfo->primaryAddr;
+ info->servPort = zoneInfo->llqPort;
+ info->ntries = 0;
+
+ if (info->state == LLQ_SuspendDeferred) info->state = LLQ_Suspended;
+ else startLLQHandshake(m, info, mDNSfalse);
+ return;
+
+ poll:
+ info->question->uDNS_info.responseCallback = llqResponseHndlr;
+ info->state = LLQ_Poll;
+ info->question->LastQTime = mDNSPlatformTimeNow(m) - (2 * INIT_UCAST_POLL_INTERVAL); // trigger immediate poll
+ info->question->ThisQInterval = INIT_UCAST_POLL_INTERVAL;
+ return;
+
+ error:
+ info->state = LLQ_Error;
+ }
+
+mDNSlocal mStatus startLLQ(mDNS *m, DNSQuestion *question)
+ {
+ LLQ_Info *info;
+ mStatus err = mStatus_NoError;
+
+ // allocate / init info struct
+ info = umalloc(sizeof(LLQ_Info));
+ if (!info) { LogMsg("ERROR: startLLQ - malloc"); return mStatus_NoMemoryErr; }
+ ubzero(info, sizeof(LLQ_Info));
+ info->state = LLQ_GetZoneInfo;
+
+ // link info/question
+ info->question = question;
+ question->uDNS_info.llq = info;
+
+ question->uDNS_info.responseCallback = llqResponseHndlr;
+
+ err = startGetZoneData(&question->qname, m, mDNSfalse, mDNStrue, startLLQHandshakeCallback, info);
+ if (err)
+ {
+ LogMsg("ERROR: startLLQ - startGetZoneData returned %ld", err);
+ info->question = mDNSNULL;
+ ufree(info);
+ question->uDNS_info.llq = mDNSNULL;
+ return err;
+ }
+
+ LinkActiveQuestion(&m->uDNS_info, question);
+ return err;
+ }
+
+mDNSlocal mDNSBool recvLLQResponse(mDNS *m, DNSMessage *msg, const mDNSu8 *end, const mDNSAddr *srcaddr, mDNSIPPort srcport, const mDNSInterfaceID InterfaceID)
+ {
+ DNSQuestion pktQ, *q;
+ uDNS_GlobalInfo *u = &m->uDNS_info;
+ const mDNSu8 *ptr = msg->data;
+ LLQ_Info *llqInfo;
+
+ if (!msg->h.numQuestions) return mDNSfalse;
+
+ ptr = getQuestion(msg, ptr, end, 0, &pktQ);
+ if (!ptr) return mDNSfalse;
+ pktQ.uDNS_info.id = msg->h.id;
+
+ q = u->ActiveQueries;
+ while (q)
+ {
+ llqInfo = q->uDNS_info.llq;
+ if (q->LongLived &&
+ llqInfo &&
+ q->qnamehash == pktQ.qnamehash &&
+ q->qtype == pktQ.qtype &&
+ SameDomainName(&q->qname, &pktQ.qname))
+ {
+ u->CurrentQuery = q;
+ if (llqInfo->state == LLQ_Established || (llqInfo->state == LLQ_Refresh && msg->h.numAnswers))
+ { if (recvLLQEvent(m, q, msg, end, srcaddr, srcport, InterfaceID)) return mDNStrue; }
+ else if (msg->h.id.NotAnInteger == q->uDNS_info.id.NotAnInteger)
+ {
+ if (llqInfo->state == LLQ_Refresh && msg->h.numAdditionals && !msg->h.numAnswers)
+ { recvRefreshReply(m, msg, end, q); return mDNStrue; }
+ if (llqInfo->state < LLQ_Static)
+ {
+ if ((llqInfo->state != LLQ_InitialRequest && llqInfo->state != LLQ_SecondaryRequest) || mDNSSameAddress(srcaddr, &llqInfo->servAddr))
+ { q->uDNS_info.responseCallback(m, msg, end, q, q->uDNS_info.context); return mDNStrue; }
+ }
+ }
+ }
+ q = q->next;
+ }
+ return mDNSfalse;
+ }
+
+mDNSexport mDNSBool uDNS_IsActiveQuery(DNSQuestion *const question, uDNS_GlobalInfo *u)
+ {
+ DNSQuestion *q;
+
+ for (q = u->ActiveQueries; q; q = q->next)
+ {
+ if (q == question)
+ {
+ if (!question->uDNS_info.id.NotAnInteger || question->InterfaceID == mDNSInterface_LocalOnly || IsLocalDomain(&question->qname))
+ LogMsg("Warning: Question %##s in Active Unicast Query list with id %d, interfaceID %p",
+ question->qname.c, question->uDNS_info.id.NotAnInteger, question->InterfaceID);
+ return mDNStrue;
+ }
+ }
+ return mDNSfalse;
+ }
+
+// stopLLQ happens IN ADDITION to stopQuery
+mDNSlocal void stopLLQ(mDNS *m, DNSQuestion *question)
+ {
+ LLQ_Info *info = question->uDNS_info.llq;
+ (void)m; // unused
+
+ if (!question->LongLived) { LogMsg("ERROR: stopLLQ - LongLived flag not set"); return; }
+ if (!info) { LogMsg("ERROR: stopLLQ - llq info is NULL"); return; }
+
+ switch (info->state)
+ {
+ case LLQ_UnInit:
+ LogMsg("ERROR: stopLLQ - state LLQ_UnInit");
+ //!!!KRS should we unlink info<->question here?
+ return;
+ case LLQ_GetZoneInfo:
+ case LLQ_SuspendDeferred:
+ info->question = mDNSNULL; // remove ref to question, as it may be freed when we get called back from async op
+ info->state = LLQ_Cancelled;
+ return;
+ case LLQ_Established:
+ case LLQ_Refresh:
+ // refresh w/ lease 0
+ sendLLQRefresh(m, question, 0);
+ goto end;
+ default:
+ debugf("stopLLQ - silently discarding LLQ in state %d", info->state);
+ goto end;
+ }
+
+ end:
+ if (info->NATMap) info->NATMap = mDNSfalse;
+ CheckForUnreferencedLLQMapping(m);
+ info->question = mDNSNULL;
+ ufree(info);
+ question->uDNS_info.llq = mDNSNULL;
+ question->LongLived = mDNSfalse;
+ }
+
+mDNSexport mStatus uDNS_StopQuery(mDNS *const m, DNSQuestion *const question)
+ {
+ uDNS_GlobalInfo *u = &m->uDNS_info;
+ DNSQuestion *qptr, *prev = mDNSNULL;
+ CacheRecord *ka;
+
+ qptr = u->ActiveQueries;
+ while (qptr)
+ {
+ if (qptr == question)
+ {
+ if (question->LongLived && question->uDNS_info.llq)
+ stopLLQ(m, question);
+ if (m->uDNS_info.CurrentQuery == question)
+ m->uDNS_info.CurrentQuery = m->uDNS_info.CurrentQuery->next;
+ while (question->uDNS_info.knownAnswers)
+ {
+ ka = question->uDNS_info.knownAnswers;
+ question->uDNS_info.knownAnswers = question->uDNS_info.knownAnswers->next;
+ ufree(ka);
+ }
+ if (prev) prev->next = question->next;
+ else u->ActiveQueries = question->next;
+ return mStatus_NoError;
+ }
+ prev = qptr;
+ qptr = qptr->next;
+ }
+ LogMsg("uDNS_StopQuery: no such active query (%##s)", question->qname.c);
+ return mStatus_UnknownErr;
+ }
+
+mDNSlocal mStatus startQuery(mDNS *const m, DNSQuestion *const question, mDNSBool internal)
+ {
+ uDNS_GlobalInfo *u = &m->uDNS_info;
+ //!!!KRS we should check if the question is already in our activequestion list
+ if (!ValidateDomainName(&question->qname))
+ {
+ LogMsg("Attempt to start query with invalid qname %##s (%s)", question->qname.c, DNSTypeName(question->qtype));
+ return mStatus_Invalid;
+ }
+
+ question->next = mDNSNULL;
+ question->qnamehash = DomainNameHashValue(&question->qname); // to do quick domain name comparisons
+ question->uDNS_info.id = newMessageID(u);
+ question->uDNS_info.Answered = mDNSfalse;
+
+ // break here if its and LLQ
+ if (question->LongLived) return startLLQ(m, question);
+
+ question->ThisQInterval = INIT_UCAST_POLL_INTERVAL / 2;
+ question->LastQTime = mDNSPlatformTimeNow(m) - question->ThisQInterval;
+ // store the question/id in active question list
+ question->uDNS_info.internal = internal;
+ LinkActiveQuestion(u, question);
+ question->uDNS_info.knownAnswers = mDNSNULL;
+ LogOperation("uDNS startQuery: %##s (%s)", question->qname.c, DNSTypeName(question->qtype));
+
+ return mStatus_NoError;
+ }
+
+mDNSexport mStatus uDNS_StartQuery(mDNS *const m, DNSQuestion *const question)
+ {
+ ubzero(&question->uDNS_info, sizeof(uDNS_QuestionInfo));
+ question->uDNS_info.responseCallback = simpleResponseHndlr;
+ question->uDNS_info.context = mDNSNULL;
+ //LogOperation("uDNS_StartQuery %##s (%s)", question->qname.c, DNSTypeName(question->qtype));
+ return startQuery(m, question, 0);
+ }
+
+// explicitly set response handler
+mDNSlocal mStatus startInternalQuery(DNSQuestion *q, mDNS *m, InternalResponseHndlr callback, void *hndlrContext)
+ {
+ ubzero(&q->uDNS_info, sizeof(uDNS_QuestionInfo));
+ q->QuestionContext = hndlrContext;
+ q->uDNS_info.responseCallback = callback;
+ q->uDNS_info.context = hndlrContext;
+ return startQuery(m, q, 1);
+ }
+
+
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark - Domain -> Name Server Conversion
+#endif
+
+
+/* startGetZoneData
+ *
+ * Asynchronously find the address of the nameserver for the enclosing zone for a given domain name,
+ * i.e. the server to which update and LLQ requests will be sent for a given name. Once the address is
+ * derived, it will be passed to the callback, along with a context pointer. If the zone cannot
+ * be determined or if an error occurs, an all-zeros address will be passed and a message will be
+ * written to the syslog.
+ *
+ * If the FindUpdatePort arg is set, the port on which the server accepts dynamic updates is determined
+ * by querying for the _dns-update._udp.<zone>. SRV record. Likewise, if the FindLLQPort arg is set,
+ * the port on which the server accepts long lived queries is determined by querying for
+ * _dns-llq._udp.<zone>. record. If either of these queries fail, or flags are not specified,
+ * the llqPort and updatePort fields in the result structure are set to zero.
+ *
+ * Steps for deriving the zone name are as follows:
+ *
+ * Query for an SOA record for the required domain. If we don't get an answer (or an SOA in the Authority
+ * section), we strip the leading label from the name and repeat, until we get an answer.
+ *
+ * The name of the SOA record is our enclosing zone. The mname field in the SOA rdata is the domain
+ * name of the primary NS.
+ *
+ * We verify that there is an NS record with this zone for a name and the mname for its rdata.
+ * (!!!KRS this seems redundant, but BIND does this, and it should normally be zero-overhead since
+ * the NS query will get us address records in the additionals section, which we'd otherwise have to
+ * explicitly query for.)
+ *
+ * We then query for the address record for this nameserver (if it is not in the addionals section of
+ * the NS record response.)
+ */
+
+
+// state machine types and structs
+//
+
+// state machine states
+typedef enum
+ {
+ init,
+ lookupSOA,
+ foundZone,
+ lookupNS,
+ foundNS,
+ lookupA,
+ foundA,
+ lookupPort,
+ foundPort,
+ complete
+ } ntaState;
+
+// state machine actions
+typedef enum
+ {
+ smContinue, // continue immediately to next state
+ smBreak, // break until next packet/timeout
+ smError // terminal error - cleanup and abort
+ } smAction;
+
+typedef struct
+ {
+ domainname origName; // name we originally try to convert
+ domainname *curSOA; // name we have an outstanding SOA query for
+ ntaState state; // determines what we do upon receiving a packet
+ mDNS *m;
+ domainname zone; // left-hand-side of SOA record
+ mDNSu16 zoneClass;
+ domainname ns; // mname in SOA rdata, verified in confirmNS state
+ mDNSv4Addr addr; // address of nameserver
+ DNSQuestion question; // storage for any active question
+ DNSQuestion extraQuestion; // additional storage
+ mDNSBool questionActive; // if true, StopQuery() can be called on the question field
+ mDNSBool findUpdatePort;
+ mDNSBool findLLQPort;
+ mDNSIPPort updatePort;
+ mDNSIPPort llqPort;
+ AsyncOpCallback *callback; // caller specified function to be called upon completion
+ void *callbackInfo;
+ } ntaContext;
+
+
+// function prototypes (for routines that must be used as fn pointers prior to their definitions,
+// and allows states to be read top-to-bottom in logical order)
+mDNSlocal void getZoneData(mDNS *const m, DNSMessage *msg, const mDNSu8 *end, DNSQuestion *question, void *contextPtr);
+mDNSlocal smAction hndlLookupSOA(DNSMessage *msg, const mDNSu8 *end, ntaContext *context);
+mDNSlocal void processSOA(ntaContext *context, ResourceRecord *rr);
+mDNSlocal smAction confirmNS(DNSMessage *msg, const mDNSu8 *end, ntaContext *context);
+mDNSlocal smAction lookupNSAddr(DNSMessage *msg, const mDNSu8 *end, ntaContext *context);
+mDNSlocal smAction hndlLookupPorts(DNSMessage *msg, const mDNSu8 *end, ntaContext *context);
+
+// initialization
+mDNSlocal mStatus startGetZoneData(domainname *name, mDNS *m, mDNSBool findUpdatePort, mDNSBool findLLQPort,
+ AsyncOpCallback callback, void *callbackInfo)
+ {
+ ntaContext *context = (ntaContext*)umalloc(sizeof(ntaContext));
+ if (!context) { LogMsg("ERROR: startGetZoneData - umalloc failed"); return mStatus_NoMemoryErr; }
+ ubzero(context, sizeof(ntaContext));
+ AssignDomainName(&context->origName, name);
+ context->state = init;
+ context->m = m;
+ context->callback = callback;
+ context->callbackInfo = callbackInfo;
+ context->findUpdatePort = findUpdatePort;
+ context->findLLQPort = findLLQPort;
+ getZoneData(m, mDNSNULL, mDNSNULL, mDNSNULL, context);
+ return mStatus_NoError;
+ }
+
+// state machine entry routine
+mDNSlocal void getZoneData(mDNS *const m, DNSMessage *msg, const mDNSu8 *end, DNSQuestion *question, void *contextPtr)
+ {
+ AsyncOpResult result;
+ ntaContext *context = (ntaContext*)contextPtr;
+ smAction action;
+
+ // unused
+ (void)m;
+ (void)question;
+
+ // stop any active question
+ if (context->questionActive)
+ {
+ uDNS_StopQuery(context->m, &context->question);
+ context->questionActive = mDNSfalse;
+ }
+
+ if (msg && msg->h.flags.b[2] >> 4 && msg->h.flags.b[2] >> 4 != kDNSFlag1_RC_NXDomain)
+ {
+ // rcode non-zero, non-nxdomain
+ LogMsg("ERROR: getZoneData - received response w/ rcode %d", msg->h.flags.b[2] >> 4);
+ goto error;
+ }
+
+ switch (context->state)
+ {
+ case init:
+ case lookupSOA:
+ action = hndlLookupSOA(msg, end, context);
+ if (action == smError) goto error;
+ if (action == smBreak) return;
+ case foundZone:
+ case lookupNS:
+ action = confirmNS(msg, end, context);
+ if (action == smError) goto error;
+ if (action == smBreak) return;
+ case foundNS:
+ case lookupA:
+ action = lookupNSAddr(msg, end, context);
+ if (action == smError) goto error;
+ if (action == smBreak) return;
+ case foundA:
+ if (!context->findUpdatePort && !context->findLLQPort)
+ {
+ context->state = complete;
+ break;
+ }
+ case lookupPort:
+ action = hndlLookupPorts(msg, end, context);
+ if (action == smError) goto error;
+ if (action == smBreak) return;
+ if (action == smContinue) context->state = complete;
+ case foundPort:
+ case complete: break;
+ }
+
+ if (context->state != complete)
+ {
+ LogMsg("ERROR: getZoneData - exited state machine with state %d", context->state);
+ goto error;
+ }
+
+ result.type = zoneDataResult;
+ result.zoneData.primaryAddr.ip.v4 = context->addr;
+ result.zoneData.primaryAddr.type = mDNSAddrType_IPv4;
+ AssignDomainName(&result.zoneData.zoneName, &context->zone);
+ result.zoneData.zoneClass = context->zoneClass;
+
+ if (context->findLLQPort)
+ result.zoneData.llqPort = context->llqPort;
+ else
+ result.zoneData.llqPort = zeroIPPort;
+
+ if (context->findUpdatePort)
+ result.zoneData.updatePort = context->updatePort;
+ else
+ result.zoneData.updatePort = zeroIPPort;
+
+ context->callback(mStatus_NoError, context->m, context->callbackInfo, &result);
+ goto cleanup;
+
+error:
+ if (context && context->callback)
+ context->callback(mStatus_UnknownErr, context->m, context->callbackInfo, mDNSNULL);
+cleanup:
+ if (context && context->questionActive)
+ {
+ uDNS_StopQuery(context->m, &context->question);
+ context->questionActive = mDNSfalse;
+ }
+ if (context) ufree(context);
+ }
+
+mDNSlocal smAction hndlLookupSOA(DNSMessage *msg, const mDNSu8 *end, ntaContext *context)
+ {
+ mStatus err;
+ LargeCacheRecord lcr;
+ ResourceRecord *rr = &lcr.r.resrec;
+ DNSQuestion *query = &context->question;
+ const mDNSu8 *ptr;
+
+ if (msg)
+ {
+ // if msg contains SOA record in answer or authority sections, update context/state and return
+ int i;
+ ptr = LocateAnswers(msg, end);
+ for (i = 0; i < msg->h.numAnswers; i++)
+ {
+ ptr = GetLargeResourceRecord(context->m, msg, ptr, end, 0, kDNSRecordTypePacketAns, &lcr);
+ if (!ptr) { LogMsg("ERROR: hndlLookupSOA, Answers - GetLargeResourceRecord returned NULL"); return smError; }
+ if (rr->rrtype == kDNSType_SOA && SameDomainName(context->curSOA, rr->name))
+ {
+ processSOA(context, rr);
+ return smContinue;
+ }
+ }
+ ptr = LocateAuthorities(msg, end);
+ // SOA not in answers, check in authority
+ for (i = 0; i < msg->h.numAuthorities; i++)
+ {
+ ptr = GetLargeResourceRecord(context->m, msg, ptr, end, 0, kDNSRecordTypePacketAns, &lcr); ///!!!KRS using type PacketAns for auth
+ if (!ptr) { LogMsg("ERROR: hndlLookupSOA, Authority - GetLargeResourceRecord returned NULL"); return smError; }
+ if (rr->rrtype == kDNSType_SOA)
+ {
+ processSOA(context, rr);
+ return smContinue;
+ }
+ }
+ }
+
+ if (context->state != init && !context->curSOA->c[0])
+ {
+ // we've gone down to the root and have not found an SOA
+ LogMsg("ERROR: hndlLookupSOA - recursed to root label of %##s without finding SOA",
+ context->origName.c);
+ return smError;
+ }
+
+ ubzero(query, sizeof(DNSQuestion));
+ // chop off leading label unless this is our first try
+ if (context->state == init) context->curSOA = &context->origName;
+ else context->curSOA = (domainname *)(context->curSOA->c + context->curSOA->c[0]+1);
+
+ context->state = lookupSOA;
+ AssignDomainName(&query->qname, context->curSOA);
+ query->qtype = kDNSType_SOA;
+ query->qclass = kDNSClass_IN;
+ err = startInternalQuery(query, context->m, getZoneData, context);
+ context->questionActive = mDNStrue;
+ if (err) LogMsg("hndlLookupSOA: startInternalQuery returned error %ld (breaking until next periodic retransmission)", err);
+
+ return smBreak; // break from state machine until we receive another packet
+ }
+
+mDNSlocal void processSOA(ntaContext *context, ResourceRecord *rr)
+ {
+ AssignDomainName(&context->zone, rr->name);
+ context->zoneClass = rr->rrclass;
+ AssignDomainName(&context->ns, &rr->rdata->u.soa.mname);
+ context->state = foundZone;
+ }
+
+
+mDNSlocal smAction confirmNS(DNSMessage *msg, const mDNSu8 *end, ntaContext *context)
+ {
+ DNSQuestion *query = &context->question;
+ mStatus err;
+ LargeCacheRecord lcr;
+ const ResourceRecord *const rr = &lcr.r.resrec;
+ const mDNSu8 *ptr;
+ int i;
+
+ if (context->state == foundZone)
+ {
+ // we've just learned the zone. confirm that an NS record exists
+ AssignDomainName(&query->qname, &context->zone);
+ query->qtype = kDNSType_NS;
+ query->qclass = kDNSClass_IN;
+ err = startInternalQuery(query, context->m, getZoneData, context);
+ context->questionActive = mDNStrue;
+ if (err) LogMsg("confirmNS: startInternalQuery returned error %ld (breaking until next periodic retransmission", err);
+ context->state = lookupNS;
+ return smBreak; // break from SM until we receive another packet
+ }
+ else if (context->state == lookupNS)
+ {
+ ptr = LocateAnswers(msg, end);
+ for (i = 0; i < msg->h.numAnswers; i++)
+ {
+ ptr = GetLargeResourceRecord(context->m, msg, ptr, end, 0, kDNSRecordTypePacketAns, &lcr);
+ if (!ptr) { LogMsg("ERROR: confirmNS, Answers - GetLargeResourceRecord returned NULL"); return smError; }
+ if (rr->rrtype == kDNSType_NS &&
+ SameDomainName(&context->zone, rr->name) && SameDomainName(&context->ns, &rr->rdata->u.name))
+ {
+ context->state = foundNS;
+ return smContinue; // next routine will examine additionals section of A record
+ }
+ }
+ debugf("ERROR: could not confirm existence of record %##s NS %##s", context->zone.c, context->ns.c);
+ return smError;
+ }
+ else { LogMsg("ERROR: confirmNS - bad state %d", context->state); return smError; }
+ }
+
+mDNSlocal smAction queryNSAddr(ntaContext *context)
+ {
+ mStatus err;
+ DNSQuestion *query = &context->question;
+
+ AssignDomainName(&query->qname, &context->ns);
+ query->qtype = kDNSType_A;
+ query->qclass = kDNSClass_IN;
+ err = startInternalQuery(query, context->m, getZoneData, context);
+ context->questionActive = mDNStrue;
+ if (err) LogMsg("confirmNS: startInternalQuery returned error %ld (breaking until next periodic retransmission)", err);
+ context->state = lookupA;
+ return smBreak;
+ }
+
+mDNSlocal smAction lookupNSAddr(DNSMessage *msg, const mDNSu8 *end, ntaContext *context)
+ {
+ const mDNSu8 *ptr;
+ int i;
+ LargeCacheRecord lcr;
+ ResourceRecord *rr = &lcr.r.resrec;
+
+ if (context->state == foundNS)
+ {
+ // we just found the NS record - look for the corresponding A record in the Additionals section
+ if (!msg->h.numAdditionals) return queryNSAddr(context);
+ ptr = LocateAdditionals(msg, end);
+ if (!ptr)
+ {
+ LogMsg("ERROR: lookupNSAddr - LocateAdditionals returned NULL, expected %d additionals", msg->h.numAdditionals);
+ return queryNSAddr(context);
+ }
+ else
+ {
+ for (i = 0; i < msg->h.numAdditionals; i++)
+ {
+ ptr = GetLargeResourceRecord(context->m, msg, ptr, end, 0, kDNSRecordTypePacketAns, &lcr);
+ if (!ptr)
+ {
+ LogMsg("ERROR: lookupNSAddr, Additionals - GetLargeResourceRecord returned NULL");
+ return queryNSAddr(context);
+ }
+ if (rr->rrtype == kDNSType_A && SameDomainName(&context->ns, rr->name))
+ {
+ context->addr = rr->rdata->u.ipv4;
+ context->state = foundA;
+ return smContinue;
+ }
+ }
+ }
+ // no A record in Additionals - query the server
+ return queryNSAddr(context);
+ }
+ else if (context->state == lookupA)
+ {
+ ptr = LocateAnswers(msg, end);
+ if (!ptr) { LogMsg("ERROR: lookupNSAddr: LocateAnswers returned NULL"); return smError; }
+ for (i = 0; i < msg->h.numAnswers; i++)
+ {
+ ptr = GetLargeResourceRecord(context->m, msg, ptr, end, 0, kDNSRecordTypePacketAns, &lcr);
+ if (!ptr) { LogMsg("ERROR: lookupNSAddr, Answers - GetLargeResourceRecord returned NULL"); break; }
+ if (rr->rrtype == kDNSType_A && SameDomainName(&context->ns, rr->name))
+ {
+ context->addr = rr->rdata->u.ipv4;
+ context->state = foundA;
+ return smContinue;
+ }
+ }
+ LogMsg("ERROR: lookupNSAddr: Address record not found in answer section");
+ return smError;
+ }
+ else { LogMsg("ERROR: lookupNSAddr - bad state %d", context->state); return smError; }
+ }
+
+mDNSlocal smAction lookupDNSPort(DNSMessage *msg, const mDNSu8 *end, ntaContext *context, char *portName, mDNSIPPort *port)
+ {
+ int i;
+ LargeCacheRecord lcr;
+ const mDNSu8 *ptr;
+ DNSQuestion *q;
+ mStatus err;
+
+ if (context->state == lookupPort) // we've already issued the query
+ {
+ if (!msg) { LogMsg("ERROR: hndlLookupUpdatePort - NULL message"); return smError; }
+ ptr = LocateAnswers(msg, end);
+ for (i = 0; i < msg->h.numAnswers; i++)
+ {
+ ptr = GetLargeResourceRecord(context->m, msg, ptr, end, 0, kDNSRecordTypePacketAns, &lcr);
+ if (!ptr) { LogMsg("ERROR: hndlLookupUpdatePort - GetLargeResourceRecord returned NULL"); return smError; }
+ if (ResourceRecordAnswersQuestion(&lcr.r.resrec, &context->question))
+ {
+ *port = lcr.r.resrec.rdata->u.srv.port;
+ context->state = foundPort;
+ return smContinue;
+ }
+ }
+ debugf("hndlLookupUpdatePort - no answer for type %s", portName);
+ port->NotAnInteger = 0;
+ context->state = foundPort;
+ return smContinue;
+ }
+
+ // query the server for the update port for the zone
+ context->state = lookupPort;
+ q = &context->question;
+ MakeDomainNameFromDNSNameString(&q->qname, portName);
+ AppendDomainName(&q->qname, &context->zone);
+ q->qtype = kDNSType_SRV;
+ q->qclass = kDNSClass_IN;
+ err = startInternalQuery(q, context->m, getZoneData, context);
+ context->questionActive = mDNStrue;
+ if (err) LogMsg("hndlLookupSOA: startInternalQuery returned error %ld (breaking until next periodic retransmission)", err);
+ return smBreak; // break from state machine until we receive another packet
+ }
+
+mDNSlocal smAction hndlLookupPorts(DNSMessage *msg, const mDNSu8 *end, ntaContext *context)
+ {
+ smAction action;
+
+ if (context->findUpdatePort && !context->updatePort.NotAnInteger)
+ {
+ action = lookupDNSPort(msg, end, context, UPDATE_PORT_NAME, &context->updatePort);
+ if (action != smContinue) return action;
+ }
+ if (context->findLLQPort && !context->llqPort.NotAnInteger)
+ return lookupDNSPort(msg, end, context, LLQ_PORT_NAME, &context->llqPort);
+
+ return smContinue;
+ }
+
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark - Truncation Handling
+#endif
+
+typedef struct
+ {
+ DNSQuestion *question;
+ DNSMessage *reply;
+ mDNSu16 replylen;
+ int nread;
+ mDNS *m;
+ } tcpInfo_t;
+
+// issue queries over a conected socket
+mDNSlocal void conQueryCallback(int sd, void *context, mDNSBool ConnectionEstablished)
+ {
+ mStatus err = 0;
+ char msgbuf[356]; // 96 (hdr) + 256 (domain) + 4 (class/type)
+ DNSMessage *msg;
+ mDNSu8 *end;
+ tcpInfo_t *info = (tcpInfo_t *)context;
+ DNSQuestion *question = info->question;
+ int n;
+ mDNS *m = info->m;
+
+ mDNS_Lock(m);
+
+ if (ConnectionEstablished)
+ {
+ // connection is established - send the message
+ msg = (DNSMessage *)&msgbuf;
+ err = constructQueryMsg(msg, &end, question);
+ if (err) { LogMsg("ERROR: conQueryCallback: constructQueryMsg - %ld", err); goto error; }
+ err = mDNSSendDNSMessage(m, msg, end, mDNSInterface_Any, &zeroAddr, zeroIPPort, sd, mDNSNULL);
+ question->LastQTime = mDNSPlatformTimeNow(m);
+ if (err) { debugf("ERROR: conQueryCallback: mDNSSendDNSMessage_tcp - %ld", err); goto error; }
+ }
+ else
+ {
+ if (!info->nread)
+ {
+ // read msg len
+ mDNSu8 lenbuf[2];
+ n = mDNSPlatformReadTCP(sd, lenbuf, 2);
+ if (n != 2)
+ {
+ LogMsg("ERROR:conQueryCallback - attempt to read message length failed (read returned %d)", n);
+ goto error;
+ }
+ info->replylen = (mDNSu16)((mDNSu16)lenbuf[0] << 8 | lenbuf[1]);
+ if (info->replylen < sizeof(DNSMessageHeader))
+ { LogMsg("ERROR: conQueryCallback - length too short (%d bytes)", info->replylen); goto error; }
+ info->reply = umalloc(info->replylen);
+ if (!info->reply) { LogMsg("ERROR: conQueryCallback - malloc failed"); goto error; }
+ }
+ n = mDNSPlatformReadTCP(sd, ((char *)info->reply) + info->nread, info->replylen - info->nread);
+ if (n < 0) { LogMsg("ERROR: conQueryCallback - read returned %d", n); goto error; }
+ info->nread += n;
+ if (info->nread == info->replylen)
+ {
+ // Finished reading message; convert the integer parts which are in IETF byte-order (MSB first, LSB second)
+ DNSMessage *msg = info->reply;
+ mDNSu8 *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]);
+ uDNS_ReceiveMsg(m, msg, (mDNSu8 *)msg + info->replylen, mDNSNULL, zeroIPPort, mDNSNULL, zeroIPPort, question->InterfaceID);
+ mDNSPlatformTCPCloseConnection(sd);
+ ufree(info->reply);
+ ufree(info);
+ }
+ }
+
+ mDNS_Unlock(m);
+ return;
+
+ error:
+ mDNSPlatformTCPCloseConnection(sd);
+ if (info->reply) ufree(info->reply);
+ ufree(info);
+ mDNS_Unlock(m);
+ }
+
+mDNSlocal void hndlTruncatedAnswer(DNSQuestion *question, const mDNSAddr *src, mDNS *m)
+ {
+ mStatus connectionStatus;
+ uDNS_QuestionInfo *info = &question->uDNS_info;
+ int sd;
+ tcpInfo_t *context;
+
+ if (!src) { LogMsg("hndlTruncatedAnswer: TCP DNS response had TC bit set: ignoring"); return; }
+
+ context = (tcpInfo_t *)umalloc(sizeof(tcpInfo_t));
+ if (!context) { LogMsg("ERROR: hndlTruncatedAnswer - memallocate failed"); return; }
+ ubzero(context, sizeof(tcpInfo_t));
+ context->question = question;
+ context->m = m;
+ info->id = newMessageID(&m->uDNS_info);
+
+ connectionStatus = mDNSPlatformTCPConnect(src, UnicastDNSPort, question->InterfaceID, conQueryCallback, context, &sd);
+ if (connectionStatus == mStatus_ConnEstablished) // manually invoke callback if connection completes
+ {
+ conQueryCallback(sd, context, mDNStrue);
+ return;
+ }
+ if (connectionStatus == mStatus_ConnPending) return; // callback will be automatically invoked when connection completes
+ LogMsg("hndlTruncatedAnswer: connection failed");
+ uDNS_StopQuery(m, question); //!!!KRS can we really call this here?
+ }
+
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark - Dynamic Updates
+#endif
+
+mDNSlocal void sendRecordRegistration(mDNS *const m, AuthRecord *rr)
+ {
+ DNSMessage msg;
+ mDNSu8 *ptr = msg.data;
+ mDNSu8 *end = (mDNSu8 *)&msg + sizeof(DNSMessage);
+ uDNS_GlobalInfo *u = &m->uDNS_info;
+ mDNSOpaque16 id;
+ uDNS_RegInfo *regInfo = &rr->uDNS_info;
+ mStatus err = mStatus_UnknownErr;
+
+ id = newMessageID(u);
+ InitializeDNSMessage(&msg.h, id, UpdateReqFlags);
+ rr->uDNS_info.id = id;
+
+ // set zone
+ ptr = putZone(&msg, ptr, end, &regInfo->zone, mDNSOpaque16fromIntVal(rr->resrec.rrclass));
+ if (!ptr) goto error;
+
+ if (regInfo->state == regState_UpdatePending)
+ {
+ // delete old RData
+ SetNewRData(&rr->resrec, regInfo->OrigRData, regInfo->OrigRDLen);
+ if (!(ptr = putDeletionRecord(&msg, ptr, &rr->resrec))) goto error; // delete old rdata
+
+ // add new RData
+ SetNewRData(&rr->resrec, regInfo->InFlightRData, regInfo->InFlightRDLen);
+ if (!(ptr = PutResourceRecordTTLJumbo(&msg, ptr, &msg.h.mDNS_numUpdates, &rr->resrec, rr->resrec.rroriginalttl))) goto error;
+ }
+
+ else
+ {
+ if (rr->resrec.RecordType == kDNSRecordTypeKnownUnique)
+ {
+ // KnownUnique: Delete any previous value
+ ptr = putDeleteRRSet(&msg, ptr, rr->resrec.name, rr->resrec.rrtype);
+ if (!ptr) goto error;
+ }
+
+ else if (rr->resrec.RecordType != kDNSRecordTypeShared)
+ {
+ ptr = putPrereqNameNotInUse(rr->resrec.name, &msg, ptr, end);
+ if (!ptr) goto error;
+ }
+
+ ptr = PutResourceRecordTTLJumbo(&msg, ptr, &msg.h.mDNS_numUpdates, &rr->resrec, rr->resrec.rroriginalttl);
+ if (!ptr) goto error;
+ }
+
+ if (rr->uDNS_info.lease)
+ { ptr = putUpdateLease(&msg, ptr, DEFAULT_UPDATE_LEASE); if (!ptr) goto error; }
+
+ err = mDNSSendDNSMessage(m, &msg, ptr, mDNSInterface_Any, &regInfo->ns, regInfo->port, -1, GetAuthInfoForName(u, rr->resrec.name));
+ if (err) debugf("ERROR: sendRecordRegistration - mDNSSendDNSMessage - %ld", err);
+
+ SetRecordRetry(m, rr, err);
+
+ if (regInfo->state != regState_Refresh && regInfo->state != regState_DeregDeferred && regInfo->state != regState_UpdatePending)
+ regInfo->state = regState_Pending;
+
+ return;
+
+error:
+ LogMsg("sendRecordRegistration: Error formatting message");
+ if (rr->uDNS_info.state != regState_Unregistered)
+ {
+ unlinkAR(&u->RecordRegistrations, rr);
+ rr->uDNS_info.state = regState_Unregistered;
+ }
+ m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback
+ if (rr->RecordCallback) rr->RecordCallback(m, rr, err);
+ m->mDNS_reentrancy--; // Decrement to block mDNS API calls again
+ // NOTE: not safe to touch any client structures here
+ }
+
+mDNSlocal void RecordRegistrationCallback(mStatus err, mDNS *const m, void *authPtr, const AsyncOpResult *result)
+ {
+ AuthRecord *newRR = (AuthRecord*)authPtr;
+ const zoneData_t *zoneData = mDNSNULL;
+ uDNS_GlobalInfo *u = &m->uDNS_info;
+ AuthRecord *ptr;
+
+ // make sure record is still in list
+ for (ptr = u->RecordRegistrations; ptr; ptr = ptr->next)
+ if (ptr == newRR) break;
+ if (!ptr) { LogMsg("RecordRegistrationCallback - RR no longer in list. Discarding."); return; }
+
+ // check error/result
+ if (err) { LogMsg("RecordRegistrationCallback: error %ld", err); goto error; }
+ if (!result) { LogMsg("ERROR: RecordRegistrationCallback invoked with NULL result and no error"); goto error; }
+ else zoneData = &result->zoneData;
+
+ if (newRR->uDNS_info.state == regState_Cancelled)
+ {
+ //!!!KRS we should send a memfree callback here!
+ debugf("Registration of %##s type %d cancelled prior to update",
+ newRR->resrec.name->c, newRR->resrec.rrtype);
+ newRR->uDNS_info.state = regState_Unregistered;
+ unlinkAR(&u->RecordRegistrations, newRR);
+ return;
+ }
+
+ if (result->type != zoneDataResult)
+ {
+ LogMsg("ERROR: buildUpdatePacket passed incorrect result type %d", result->type);
+ goto error;
+ }
+
+ 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);
+ goto error;
+ }
+
+ // 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)
+ {
+ LogMsg("ERROR: Only name server claiming responsibility for \"%##s\" is \"%##s\"!",
+ newRR->resrec.name->c, zoneData->zoneName.c);
+ err = mStatus_NoSuchNameErr;
+ goto error;
+ }
+
+ // cache zone data
+ AssignDomainName(&newRR->uDNS_info.zone, &zoneData->zoneName);
+ newRR->uDNS_info.ns = zoneData->primaryAddr;
+ if (zoneData->updatePort.NotAnInteger) newRR->uDNS_info.port = zoneData->updatePort;
+ else
+ {
+ debugf("Update port not advertised via SRV - guessing port 53, no lease option");
+ newRR->uDNS_info.port = UnicastDNSPort;
+ newRR->uDNS_info.lease = mDNSfalse;
+ }
+
+ sendRecordRegistration(m, newRR);
+ return;
+
+error:
+ if (newRR->uDNS_info.state != regState_Unregistered)
+ {
+ unlinkAR(&u->RecordRegistrations, newRR);
+ newRR->uDNS_info.state = regState_Unregistered;
+ }
+ m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback
+ if (newRR->RecordCallback)
+ newRR->RecordCallback(m, newRR, err);
+ m->mDNS_reentrancy--; // Decrement to block mDNS API calls again
+ // NOTE: not safe to touch any client structures here
+ }
+
+mDNSlocal void SendServiceRegistration(mDNS *m, ServiceRecordSet *srs)
+ {
+ DNSMessage msg;
+ mDNSu8 *ptr = msg.data;
+ mDNSu8 *end = (mDNSu8 *)&msg + sizeof(DNSMessage);
+ uDNS_GlobalInfo *u = &m->uDNS_info;
+ mDNSOpaque16 id;
+ uDNS_RegInfo *rInfo = &srs->uDNS_info;
+ mStatus err = mStatus_UnknownErr;
+ mDNSIPPort privport;
+ NATTraversalInfo *nat = srs->uDNS_info.NATinfo;
+ mDNSBool mapped = mDNSfalse;
+ domainname target;
+ AuthRecord *srv = &srs->RR_SRV;
+ mDNSu32 i;
+
+ privport = zeroIPPort;
+
+ if (!rInfo->ns.ip.v4.NotAnInteger) { LogMsg("SendServiceRegistration - NS not set!"); return; }
+
+ id = newMessageID(u);
+ InitializeDNSMessage(&msg.h, id, UpdateReqFlags);
+
+ // setup resource records
+ SetNewRData(&srs->RR_PTR.resrec, mDNSNULL, 0);
+ SetNewRData(&srs->RR_TXT.resrec, mDNSNULL, 0);
+
+ // replace port w/ NAT mapping if necessary
+ if (nat && nat->PublicPort.NotAnInteger &&
+ (nat->state == NATState_Established || nat->state == NATState_Refresh || nat->state == NATState_Legacy))
+ {
+ privport = srv->resrec.rdata->u.srv.port;
+ srv->resrec.rdata->u.srv.port = nat->PublicPort;
+ mapped = mDNStrue;
+ }
+
+ // construct update packet
+ // set zone
+ ptr = putZone(&msg, ptr, end, &rInfo->zone, mDNSOpaque16fromIntVal(srv->resrec.rrclass));
+ if (!ptr) goto error;
+
+ if (srs->uDNS_info.TestForSelfConflict)
+ {
+ // update w/ prereq that SRV already exist to make sure previous registration was ours, and delete any stale TXT records
+ if (!(ptr = PutResourceRecordTTLJumbo(&msg, ptr, &msg.h.mDNS_numPrereqs, &srs->RR_SRV.resrec, 0))) goto error;
+ if (!(ptr = putDeleteRRSet(&msg, ptr, srs->RR_TXT.resrec.name, srs->RR_TXT.resrec.rrtype))) goto error;
+ }
+
+ else if (srs->uDNS_info.state != regState_Refresh && srs->uDNS_info.state != regState_UpdatePending)
+ {
+ // use SRV name for prereq
+ ptr = putPrereqNameNotInUse(srv->resrec.name, &msg, ptr, end);
+ if (!ptr) goto error;
+ }
+
+ //!!!KRS Need to do bounds checking and use TCP if it won't fit!!!
+ if (!(ptr = PutResourceRecordTTLJumbo(&msg, ptr, &msg.h.mDNS_numUpdates, &srs->RR_PTR.resrec, srs->RR_PTR.resrec.rroriginalttl))) goto error;
+
+ for (i = 0; i < srs->NumSubTypes; i++)
+ if (!(ptr = PutResourceRecordTTLJumbo(&msg, ptr, &msg.h.mDNS_numUpdates, &srs->SubTypes[i].resrec, srs->SubTypes[i].resrec.rroriginalttl))) goto error;
+
+ if (rInfo->state == regState_UpdatePending) // we're updating the txt record
+ {
+ AuthRecord *txt = &srs->RR_TXT;
+ uDNS_RegInfo *txtInfo = &txt->uDNS_info;
+ // delete old RData
+ SetNewRData(&txt->resrec, txtInfo->OrigRData, txtInfo->OrigRDLen);
+ if (!(ptr = putDeletionRecord(&msg, ptr, &srs->RR_TXT.resrec))) goto error; // delete old rdata
+
+ // add new RData
+ SetNewRData(&txt->resrec, txtInfo->InFlightRData, txtInfo->InFlightRDLen);
+ if (!(ptr = PutResourceRecordTTLJumbo(&msg, ptr, &msg.h.mDNS_numUpdates, &srs->RR_TXT.resrec, srs->RR_TXT.resrec.rroriginalttl))) goto error;
+ }
+ else
+ if (!(ptr = PutResourceRecordTTLJumbo(&msg, ptr, &msg.h.mDNS_numUpdates, &srs->RR_TXT.resrec, srs->RR_TXT.resrec.rroriginalttl))) goto error;
+
+ if (!GetServiceTarget(u, srv, &target))
+ {
+ debugf("Couldn't get target for service %##s", srv->resrec.name->c);
+ rInfo->state = regState_NoTarget;
+ return;
+ }
+
+ if (!SameDomainName(&target, &srv->resrec.rdata->u.srv.target))
+ {
+ AssignDomainName(&srv->resrec.rdata->u.srv.target, &target);
+ SetNewRData(&srv->resrec, mDNSNULL, 0);
+ }
+
+ ptr = PutResourceRecordTTLJumbo(&msg, ptr, &msg.h.mDNS_numUpdates, &srv->resrec, srv->resrec.rroriginalttl);
+ if (!ptr) goto error;
+
+ if (srs->uDNS_info.lease)
+ { ptr = putUpdateLease(&msg, ptr, DEFAULT_UPDATE_LEASE); if (!ptr) goto error; }
+
+ err = mDNSSendDNSMessage(m, &msg, ptr, mDNSInterface_Any, &rInfo->ns, rInfo->port, -1, GetAuthInfoForName(u, srs->RR_SRV.resrec.name));
+ if (err) debugf("ERROR: SendServiceRegistration - mDNSSendDNSMessage - %ld", err);
+
+ if (rInfo->state != regState_Refresh && rInfo->state != regState_DeregDeferred && srs->uDNS_info.state != regState_UpdatePending)
+ rInfo->state = regState_Pending;
+
+ SetRecordRetry(m, &srs->RR_SRV, err);
+ rInfo->id = id;
+ if (mapped) srv->resrec.rdata->u.srv.port = privport;
+ return;
+
+error:
+ LogMsg("SendServiceRegistration - Error formatting message");
+ if (mapped) srv->resrec.rdata->u.srv.port = privport;
+ unlinkSRS(m, srs);
+ rInfo->state = regState_Unregistered;
+ m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback
+ srs->ServiceCallback(m, srs, err);
+ m->mDNS_reentrancy--; // Decrement to block mDNS API calls again
+ //!!!KRS will mem still be free'd on error?
+ // NOTE: not safe to touch any client structures here
+ }
+
+mDNSlocal void serviceRegistrationCallback(mStatus err, mDNS *const m, void *srsPtr, const AsyncOpResult *result)
+ {
+ ServiceRecordSet *srs = (ServiceRecordSet *)srsPtr;
+ const zoneData_t *zoneData = mDNSNULL;
+
+ if (err) goto error;
+ if (!result) { LogMsg("ERROR: serviceRegistrationCallback invoked with NULL result and no error"); goto error; }
+ else zoneData = &result->zoneData;
+
+ if (result->type != zoneDataResult)
+ {
+ LogMsg("ERROR: buildUpdatePacket passed incorrect result type %d", result->type);
+ goto error;
+ }
+
+ if (srs->uDNS_info.state == regState_Cancelled)
+ {
+ // client cancelled registration while fetching zone data
+ srs->uDNS_info.state = regState_Unregistered;
+ unlinkSRS(m, srs);
+ m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback
+ srs->ServiceCallback(m, srs, mStatus_MemFree);
+ m->mDNS_reentrancy--; // Decrement to block mDNS API calls again
+ return;
+ }
+
+ if (srs->RR_SRV.resrec.rrclass != zoneData->zoneClass)
+ {
+ LogMsg("Service %##s - class does not match zone", srs->RR_SRV.resrec.name->c);
+ goto error;
+ }
+
+ // cache zone data
+ AssignDomainName(&srs->uDNS_info.zone, &zoneData->zoneName);
+ srs->uDNS_info.ns.type = mDNSAddrType_IPv4;
+ srs->uDNS_info.ns = zoneData->primaryAddr;
+ if (zoneData->updatePort.NotAnInteger) srs->uDNS_info.port = zoneData->updatePort;
+ else
+ {
+ debugf("Update port not advertised via SRV - guessing port 53, no lease option");
+ srs->uDNS_info.port = UnicastDNSPort;
+ srs->uDNS_info.lease = mDNSfalse;
+ }
+
+ if (srs->RR_SRV.resrec.rdata->u.srv.port.NotAnInteger && IsPrivateV4Addr(&m->uDNS_info.AdvertisedV4))
+ { srs->uDNS_info.state = regState_NATMap; StartNATPortMap(m, srs); }
+ else SendServiceRegistration(m, srs);
+ return;
+
+error:
+ unlinkSRS(m, srs);
+ srs->uDNS_info.state = regState_Unregistered;
+ m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback
+ srs->ServiceCallback(m, srs, err);
+ m->mDNS_reentrancy--; // Decrement to block mDNS API calls again
+ // NOTE: not safe to touch any client structures here
+ }
+
+mDNSlocal mStatus SetupRecordRegistration(mDNS *m, AuthRecord *rr)
+ {
+ domainname *target = GetRRDomainNameTarget(&rr->resrec);
+ AuthRecord *ptr = m->uDNS_info.RecordRegistrations;
+
+ while (ptr && ptr != rr) ptr = ptr->next;
+ if (ptr) { LogMsg("Error: SetupRecordRegistration - record %##s already in list!", rr->resrec.name->c); return mStatus_AlreadyRegistered; }
+
+ if (rr->uDNS_info.state == regState_FetchingZoneData ||
+ rr->uDNS_info.state == regState_Pending ||
+ rr->uDNS_info.state == regState_Registered)
+ {
+ LogMsg("Requested double-registration of physical record %##s type %d",
+ rr->resrec.name->c, rr->resrec.rrtype);
+ return mStatus_AlreadyRegistered;
+ }
+
+ 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.rdlength, &rr->resrec.rdata->u);
+
+ rr->uDNS_info.state = regState_FetchingZoneData;
+ rr->next = m->uDNS_info.RecordRegistrations;
+ m->uDNS_info.RecordRegistrations = rr;
+ rr->uDNS_info.lease = mDNStrue;
+
+ return mStatus_NoError;
+ }
+
+mDNSexport mStatus uDNS_RegisterRecord(mDNS *const m, AuthRecord *const rr)
+ {
+ mStatus err = SetupRecordRegistration(m, rr);
+ if (err) return err;
+ else return startGetZoneData(rr->resrec.name, m, mDNStrue, mDNSfalse, RecordRegistrationCallback, rr);
+ }
+
+mDNSlocal void SendRecordDeregistration(mDNS *m, AuthRecord *rr)
+ {
+ uDNS_GlobalInfo *u = &m->uDNS_info;
+ DNSMessage msg;
+ mDNSu8 *ptr = msg.data;
+ mDNSu8 *end = (mDNSu8 *)&msg + sizeof(DNSMessage);
+ mStatus err;
+
+ InitializeDNSMessage(&msg.h, rr->uDNS_info.id, UpdateReqFlags);
+
+ ptr = putZone(&msg, ptr, end, &rr->uDNS_info.zone, mDNSOpaque16fromIntVal(rr->resrec.rrclass));
+ if (!ptr) goto error;
+ if (!(ptr = putDeletionRecord(&msg, ptr, &rr->resrec))) goto error;
+
+ err = mDNSSendDNSMessage(m, &msg, ptr, mDNSInterface_Any, &rr->uDNS_info.ns, rr->uDNS_info.port, -1, GetAuthInfoForName(u, rr->resrec.name));
+ if (err) debugf("ERROR: SendRecordDeregistration - mDNSSendDNSMessage - %ld", err);
+
+ SetRecordRetry(m, rr, err);
+ rr->uDNS_info.state = regState_DeregPending;
+ return;
+
+ error:
+ LogMsg("Error: SendRecordDeregistration - could not contruct deregistration packet");
+ unlinkAR(&u->RecordRegistrations, rr);
+ rr->uDNS_info.state = regState_Unregistered;
+ }
+
+
+
+mDNSexport mStatus uDNS_DeregisterRecord(mDNS *const m, AuthRecord *const rr)
+ {
+ uDNS_GlobalInfo *u = &m->uDNS_info;
+ NATTraversalInfo *n = rr->uDNS_info.NATinfo;
+
+ switch (rr->uDNS_info.state)
+ {
+ case regState_NATMap:
+ // we're in the middle of a NAT traversal operation
+ rr->uDNS_info.NATinfo = mDNSNULL;
+ if (!n) LogMsg("uDNS_DeregisterRecord: no NAT info context");
+ else FreeNATInfo(m, n); // cause response to outstanding request to be ignored.
+ // Note: normally here we're trying to determine our public address,
+ //in which case there is not state to be torn down. For simplicity,
+ //we allow other operations to expire.
+ rr->uDNS_info.state = regState_Unregistered;
+ break;
+ case regState_ExtraQueued:
+ rr->uDNS_info.state = regState_Unregistered;
+ break;
+ case regState_FetchingZoneData:
+ rr->uDNS_info.state = regState_Cancelled;
+ return mStatus_NoError;
+ case regState_Refresh:
+ case regState_Pending:
+ case regState_UpdatePending:
+ rr->uDNS_info.state = regState_DeregDeferred;
+ LogMsg("Deferring deregistration of record %##s until registration completes", rr->resrec.name->c);
+ return mStatus_NoError;
+ case regState_Registered:
+ case regState_DeregPending:
+ break;
+ case regState_DeregDeferred:
+ case regState_Cancelled:
+ LogMsg("Double deregistration of record %##s type %d",
+ rr->resrec.name->c, rr->resrec.rrtype);
+ return mStatus_UnknownErr;
+ case regState_Unregistered:
+ LogMsg("Requested deregistration of unregistered record %##s type %d",
+ rr->resrec.name->c, rr->resrec.rrtype);
+ return mStatus_UnknownErr;
+ case regState_NATError:
+ case regState_NoTarget:
+ LogMsg("ERROR: uDNS_DeregisterRecord called for record %##s with bad state %s", rr->resrec.name->c, rr->uDNS_info.state == regState_NoTarget ? "regState_NoTarget" : "regState_NATError");
+ return mStatus_UnknownErr;
+ }
+
+ if (rr->uDNS_info.state == regState_Unregistered)
+ {
+ // unlink and deliver memfree
+
+ unlinkAR(&u->RecordRegistrations, rr);
+ m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback
+ if (rr->RecordCallback) rr->RecordCallback(m, rr, mStatus_MemFree);
+ m->mDNS_reentrancy--; // Decrement to block mDNS API calls again
+ return mStatus_NoError;
+ }
+
+ rr->uDNS_info.NATinfo = mDNSNULL;
+ if (n) FreeNATInfo(m, n);
+
+ SendRecordDeregistration(m, rr);
+ return mStatus_NoError;
+ }
+
+mDNSexport mStatus uDNS_RegisterService(mDNS *const m, ServiceRecordSet *srs)
+ {
+ mDNSu32 i;
+ domainname target;
+ uDNS_RegInfo *info = &srs->uDNS_info;
+ ServiceRecordSet **p = &m->uDNS_info.ServiceRegistrations;
+ while (*p && *p != srs) p=&(*p)->next;
+ if (*p) { LogMsg("uDNS_RegisterService: %p %##s already in list", srs, srs->RR_SRV.resrec.name->c); return(mStatus_AlreadyRegistered); }
+ ubzero(info, sizeof(*info));
+ *p = srs;
+ srs->next = mDNSNULL;
+
+ srs->RR_SRV.resrec.rroriginalttl = kWideAreaTTL;
+ srs->RR_TXT.resrec.rroriginalttl = kWideAreaTTL;
+ srs->RR_PTR.resrec.rroriginalttl = kWideAreaTTL;
+ for (i = 0; i < srs->NumSubTypes;i++) srs->SubTypes[i].resrec.rroriginalttl = kWideAreaTTL;
+
+ info->lease = mDNStrue;
+
+ srs->RR_SRV.resrec.rdata->u.srv.target.c[0] = 0;
+ if (!GetServiceTarget(&m->uDNS_info, &srs->RR_SRV, &target))
+ {
+ // defer registration until we've got a target
+ debugf("uDNS_RegisterService - no target for %##s", srs->RR_SRV.resrec.name->c);
+ info->state = regState_NoTarget;
+ return mStatus_NoError;
+ }
+
+ info->state = regState_FetchingZoneData;
+ return startGetZoneData(srs->RR_SRV.resrec.name, m, mDNStrue, mDNSfalse, serviceRegistrationCallback, srs);
+ }
+
+mDNSlocal void SendServiceDeregistration(mDNS *m, ServiceRecordSet *srs)
+ {
+ uDNS_RegInfo *info = &srs->uDNS_info;
+ uDNS_GlobalInfo *u = &m->uDNS_info;
+ DNSMessage msg;
+ mDNSOpaque16 id;
+ mDNSu8 *ptr = msg.data;
+ mDNSu8 *end = (mDNSu8 *)&msg + sizeof(DNSMessage);
+ mStatus err = mStatus_UnknownErr;
+ mDNSu32 i;
+
+ id = newMessageID(u);
+ InitializeDNSMessage(&msg.h, id, UpdateReqFlags);
+
+ // put zone
+ ptr = putZone(&msg, ptr, end, &info->zone, mDNSOpaque16fromIntVal(srs->RR_SRV.resrec.rrclass));
+ if (!ptr) { LogMsg("ERROR: SendServiceDeregistration - putZone"); goto error; }
+
+ if (!(ptr = putDeleteAllRRSets(&msg, ptr, srs->RR_SRV.resrec.name))) goto error; // this deletes SRV, TXT, and Extras
+ if (!(ptr = putDeletionRecord(&msg, ptr, &srs->RR_PTR.resrec))) goto error;
+ for (i = 0; i < srs->NumSubTypes; i++)
+ if (!(ptr = putDeletionRecord(&msg, ptr, &srs->SubTypes[i].resrec))) goto error;
+
+
+ err = mDNSSendDNSMessage(m, &msg, ptr, mDNSInterface_Any, &info->ns, info->port, -1, GetAuthInfoForName(u, srs->RR_SRV.resrec.name));
+ if (err && err != mStatus_TransientErr) { debugf("ERROR: SendServiceDeregistration - mDNSSendDNSMessage - %ld", err); goto error; }
+
+ SetRecordRetry(m, &srs->RR_SRV, err);
+ info->id = id;
+ info->state = regState_DeregPending;
+
+ return;
+
+ error:
+ unlinkSRS(m, srs);
+ info->state = regState_Unregistered;
+ }
+
+mDNSexport mStatus uDNS_DeregisterService(mDNS *const m, ServiceRecordSet *srs)
+ {
+ NATTraversalInfo *nat = srs->uDNS_info.NATinfo;
+ char *errmsg = "Unknown State";
+
+ // don't re-register with a new target following deregistration
+ srs->uDNS_info.SRVChanged = srs->uDNS_info.SRVUpdateDeferred = mDNSfalse;
+
+ if (nat)
+ {
+ if (nat->state == NATState_Established || nat->state == NATState_Refresh || nat->state == NATState_Legacy)
+ DeleteNATPortMapping(m, nat, srs);
+ nat->reg.ServiceRegistration = mDNSNULL;
+ srs->uDNS_info.NATinfo = mDNSNULL;
+ FreeNATInfo(m, nat);
+ }
+
+ switch (srs->uDNS_info.state)
+ {
+ case regState_Unregistered:
+ debugf("uDNS_DeregisterService - service %##s not registered", srs->RR_SRV.resrec.name->c);
+ return mStatus_BadReferenceErr;
+ case regState_FetchingZoneData:
+ // let the async op complete, then terminate
+ srs->uDNS_info.state = regState_Cancelled;
+ return mStatus_NoError; // deliver memfree upon completion of async op
+ case regState_Pending:
+ case regState_Refresh:
+ case regState_UpdatePending:
+ // deregister following completion of in-flight operation
+ srs->uDNS_info.state = regState_DeregDeferred;
+ return mStatus_NoError;
+ case regState_DeregPending:
+ case regState_DeregDeferred:
+ case regState_Cancelled:
+ debugf("Double deregistration of service %##s", srs->RR_SRV.resrec.name->c);
+ return mStatus_NoError;
+ case regState_NATError: // not registered
+ case regState_NATMap: // not registered
+ case regState_NoTarget: // not registered
+ unlinkSRS(m, srs);
+ srs->uDNS_info.state = regState_Unregistered;
+ m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback
+ srs->ServiceCallback(m, srs, mStatus_MemFree);
+ m->mDNS_reentrancy--; // Decrement to block mDNS API calls again
+ return mStatus_NoError;
+ case regState_Registered:
+ srs->uDNS_info.state = regState_DeregPending;
+ SendServiceDeregistration(m, srs);
+ return mStatus_NoError;
+ case regState_ExtraQueued: // only for record registrations
+ errmsg = "bad state (regState_ExtraQueued)";
+ goto error;
+ }
+
+ error:
+ LogMsg("Error, uDNS_DeregisterService: %s", errmsg);
+ return mStatus_BadReferenceErr;
+ }
+
+mDNSexport mStatus uDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra)
+ {
+ mStatus err = mStatus_UnknownErr;
+
+ extra->r.resrec.RecordType = kDNSRecordTypeShared; // don't want it to conflict with the service name
+ extra->r.RecordCallback = mDNSNULL; // don't generate callbacks for extra RRs
+
+ if (sr->uDNS_info.state == regState_Registered || sr->uDNS_info.state == regState_Refresh)
+ err = uDNS_RegisterRecord(m, &extra->r);
+ else
+ {
+ err = SetupRecordRegistration(m, &extra->r);
+ extra->r.uDNS_info.state = regState_ExtraQueued; // %%% Is it okay to overwrite the previous uDNS_info.state?
+ }
+
+ if (!err)
+ {
+ extra->next = sr->Extras;
+ sr->Extras = extra;
+ }
+ return err;
+ }
+
+mDNSexport mStatus uDNS_UpdateRecord(mDNS *m, AuthRecord *rr)
+ {
+ uDNS_GlobalInfo *u = &m->uDNS_info;
+ ServiceRecordSet *parent = mDNSNULL;
+ AuthRecord *rptr;
+ uDNS_RegInfo *info = &rr->uDNS_info;
+ regState_t *stateptr = mDNSNULL;
+
+ // find the record in registered service list
+ for (parent = u->ServiceRegistrations; parent; parent = parent->next)
+ if (&parent->RR_TXT == rr) { stateptr = &parent->uDNS_info.state; break; }
+
+ if (!parent)
+ {
+ // record not part of a service - check individual record registrations
+ for (rptr = u->RecordRegistrations; rptr; rptr = rptr->next)
+ if (rptr == rr) { stateptr = &rr->uDNS_info.state; break; }
+ if (!rptr) goto unreg_error;
+ }
+
+ switch(*stateptr)
+ {
+ case regState_DeregPending:
+ case regState_DeregDeferred:
+ case regState_Cancelled:
+ case regState_Unregistered:
+ // not actively registered
+ goto unreg_error;
+
+ case regState_FetchingZoneData:
+ case regState_NATMap:
+ case regState_ExtraQueued:
+ case regState_NoTarget:
+ // change rdata directly since it hasn't been sent yet
+ if (info->UpdateRDCallback) info->UpdateRDCallback(m, rr, rr->resrec.rdata);
+ 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 (info->QueuedRData && info->UpdateRDCallback)
+ // if unsent rdata is already queued, free it before we replace it
+ info->UpdateRDCallback(m, rr, info->QueuedRData);
+ info->QueuedRData = rr->NewRData;
+ info->QueuedRDLen = rr->newrdlength;
+ rr->NewRData = mDNSNULL;
+ return mStatus_NoError;
+
+ case regState_Registered:
+ info->OrigRData = rr->resrec.rdata;
+ info->OrigRDLen = rr->resrec.rdlength;
+ info->InFlightRData = rr->NewRData;
+ info->InFlightRDLen = rr->newrdlength;
+ rr->NewRData = mDNSNULL;
+ *stateptr = regState_UpdatePending;
+ if (parent) SendServiceRegistration(m, parent);
+ else sendRecordRegistration(m, rr);
+ 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
+ }
+
+ unreg_error:
+ LogMsg("Requested update of record %##s type %d, part of service not currently registered",
+ rr->resrec.name->c, rr->resrec.rrtype);
+ return mStatus_Invalid;
+ }
+
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark - Periodic Execution Routines
+#endif
+
+
+mDNSlocal mDNSs32 CheckNATMappings(mDNS *m, mDNSs32 timenow)
+ {
+ NATTraversalInfo *ptr = m->uDNS_info.NATTraversals;
+ mDNSs32 nextevent = timenow + MIN_UCAST_PERIODIC_EXEC;
+
+ while (ptr)
+ {
+ NATTraversalInfo *cur = ptr;
+ ptr = ptr->next;
+ if (cur->op != NATOp_AddrRequest || cur->state != NATState_Established) // no refresh necessary for established Add requests
+ {
+ if (cur->retry - timenow < 0)
+ {
+ if (cur->state == NATState_Established) RefreshNATMapping(cur, m);
+ else if (cur->state == NATState_Request || cur->state == NATState_Refresh)
+ {
+ if (cur->ntries >= NATMAP_MAX_TRIES) cur->ReceiveResponse(cur, m, mDNSNULL, 0); // may invalidate "cur"
+ else SendNATMsg(cur, m);
+ }
+ }
+ else if (cur->retry - nextevent < 0) nextevent = cur->retry;
+ }
+ }
+ return nextevent;
+ }
+
+mDNSlocal mDNSs32 CheckQueries(mDNS *m, mDNSs32 timenow)
+ {
+ DNSQuestion *q;
+ uDNS_GlobalInfo *u = &m->uDNS_info;
+ LLQ_Info *llq;
+ mDNSs32 sendtime;
+ mDNSs32 nextevent = timenow + MIN_UCAST_PERIODIC_EXEC;
+ DNSMessage msg;
+ mStatus err = mStatus_NoError;
+ mDNSu8 *end;
+ uDNS_QuestionInfo *info;
+
+ u->CurrentQuery = u->ActiveQueries;
+ while (u->CurrentQuery)
+ {
+ q = u->CurrentQuery;
+ info = &q->uDNS_info;
+ llq = info->llq;
+
+ if (!info->internal && ((!q->LongLived && !info->Answered) || (llq && llq->state < LLQ_Established)) &&
+ info->RestartTime + RESTART_GOODBYE_DELAY - timenow < 0)
+ {
+ // if we've been spinning on restart setup, and we have known answers, give goodbyes (they may be re-added later)
+ while (info->knownAnswers)
+ {
+ CacheRecord *cr = info->knownAnswers;
+ info->knownAnswers = info->knownAnswers->next;
+
+ m->mDNS_reentrancy++; // Increment to allow client to legally make mDNS API calls from the callback
+ q->QuestionCallback(m, q, &cr->resrec, mDNSfalse);
+ m->mDNS_reentrancy--; // Decrement to block mDNS API calls again
+ ufree(cr);
+ if (q != u->CurrentQuery) { debugf("CheckQueries - question removed via callback."); break; }
+ }
+ }
+ if (q != u->CurrentQuery) continue;
+
+ if (q->LongLived && llq->state != LLQ_Poll)
+ {
+ if (llq->state >= LLQ_InitialRequest && llq->state <= LLQ_Established)
+ {
+ if (llq->retry - timenow < 0)
+ {
+ // sanity check to avoid packet flood bugs
+ if (!llq->retry)
+ LogMsg("ERROR: retry timer not set for LLQ %##s in state %d", q->qname.c, llq->state);
+ else if (llq->state == LLQ_Established || llq->state == LLQ_Refresh)
+ sendLLQRefresh(m, q, llq->origLease);
+ else if (llq->state == LLQ_InitialRequest)
+ startLLQHandshake(m, llq, mDNSfalse);
+ else if (llq->state == LLQ_SecondaryRequest)
+ sendChallengeResponse(m, q, mDNSNULL);
+ else if (llq->state == LLQ_Retry)
+ { llq->ntries = 0; startLLQHandshake(m, llq, mDNSfalse); }
+ }
+ else if (llq->retry - nextevent < 0) nextevent = llq->retry;
+ }
+ }
+ else
+ {
+ sendtime = q->LastQTime + q->ThisQInterval;
+ if (m->SuppressStdPort53Queries &&
+ sendtime - m->SuppressStdPort53Queries < 0) // Don't allow sendtime to be earlier than SuppressStdPort53Queries
+ sendtime = m->SuppressStdPort53Queries;
+ if (sendtime - timenow < 0)
+ {
+ DNSServer *server = GetServerForName(&m->uDNS_info, &q->qname);
+ if (server)
+ {
+ if (server->teststate == DNSServer_Untested)
+ {
+ InitializeDNSMessage(&msg.h, newMessageID(&m->uDNS_info), uQueryFlags);
+ end = putQuestion(&msg, msg.data, msg.data + AbsoluteMaxDNSMessageData, DNSRelayTestQuestion, kDNSType_PTR, kDNSClass_IN);
+ }
+ else
+ err = constructQueryMsg(&msg, &end, q);
+ if (err) LogMsg("Error: uDNS_Idle - constructQueryMsg. Skipping question %##s", q->qname.c);
+ else
+ {
+ if (server->teststate != DNSServer_Failed)
+ err = mDNSSendDNSMessage(m, &msg, end, mDNSInterface_Any, &server->addr, UnicastDNSPort, -1, mDNSNULL);
+ m->SuppressStdPort53Queries = NonZeroTime(m->timenow + (mDNSPlatformOneSecond+99)/100);
+ q->LastQTime = timenow;
+ if (err) debugf("ERROR: uDNS_idle - mDNSSendDNSMessage - %ld", err); // surpress syslog messages if we have no network
+ else if (q->ThisQInterval < MAX_UCAST_POLL_INTERVAL) q->ThisQInterval = q->ThisQInterval * 2; // don't increase interval if send failed
+ }
+ }
+ }
+ else if (sendtime - nextevent < 0) nextevent = sendtime;
+ }
+ u->CurrentQuery = u->CurrentQuery->next;
+ }
+ return nextevent;
+ }
+
+mDNSlocal mDNSs32 CheckRecordRegistrations(mDNS *m, mDNSs32 timenow)
+ {
+ AuthRecord *rr;
+ uDNS_RegInfo *rInfo;
+ uDNS_GlobalInfo *u = &m->uDNS_info;
+ mDNSs32 nextevent = timenow + MIN_UCAST_PERIODIC_EXEC;
+
+ //!!!KRS list should be pre-sorted by expiration
+ for (rr = u->RecordRegistrations; rr; rr = rr->next)
+ {
+ rInfo = &rr->uDNS_info;
+ if (rInfo->state == regState_Pending || rInfo->state == regState_DeregPending || rInfo->state == regState_UpdatePending || rInfo->state == regState_DeregDeferred || rInfo->state == regState_Refresh)
+ {
+ if (rr->LastAPTime + rr->ThisAPInterval - timenow < 0)
+ {
+#if MDNS_DEBUGMSGS
+ char *op = "(unknown operation)";
+ if (rInfo->state == regState_Pending) op = "registration";
+ else if (rInfo->state == regState_DeregPending) op = "deregistration";
+ else if (rInfo->state == regState_Refresh) op = "refresh";
+ debugf("Retransmit record %s %##s", op, rr->resrec.name->c);
+#endif
+ //LogMsg("Retransmit record %##s", rr->resrec.name->c);
+ if (rInfo->state == regState_DeregPending) SendRecordDeregistration(m, rr);
+ else sendRecordRegistration(m, rr);
+ }
+ if (rr->LastAPTime + rr->ThisAPInterval - nextevent < 0) nextevent = rr->LastAPTime + rr->ThisAPInterval;
+ }
+ if (rInfo->lease && rInfo->state == regState_Registered)
+ {
+ if (rInfo->expire - timenow < 0)
+ {
+ debugf("refreshing record %##s", rr->resrec.name->c);
+ rInfo->state = regState_Refresh;
+ sendRecordRegistration(m, rr);
+ }
+ if (rInfo->expire - nextevent < 0) nextevent = rInfo->expire;
+ }
+ }
+ return nextevent;
+ }
+
+mDNSlocal mDNSs32 CheckServiceRegistrations(mDNS *m, mDNSs32 timenow)
+ {
+ ServiceRecordSet *s = m->uDNS_info.ServiceRegistrations;
+ uDNS_RegInfo *rInfo;
+ mDNSs32 nextevent = timenow + MIN_UCAST_PERIODIC_EXEC;
+
+ // Note: ServiceRegistrations list is in the order they were created; important for in-order event delivery
+ while (s)
+ {
+ ServiceRecordSet *srs = s;
+ // NOTE: Must advance s here -- SendServiceDeregistration may delete the object we're looking at,
+ // and then if we tried to do srs = srs->next at the end we'd be referencing a dead object
+ s = s->next;
+
+ rInfo = &srs->uDNS_info;
+ if (rInfo->state == regState_Pending || rInfo->state == regState_DeregPending || rInfo->state == regState_DeregDeferred || rInfo->state == regState_Refresh || rInfo->state == regState_UpdatePending)
+ {
+ if (srs->RR_SRV.LastAPTime + srs->RR_SRV.ThisAPInterval - timenow < 0)
+ {
+#if MDNS_DEBUGMSGS
+ char *op = "unknown";
+ if (rInfo->state == regState_Pending) op = "registration";
+ else if (rInfo->state == regState_DeregPending) op = "deregistration";
+ else if (rInfo->state == regState_Refresh) op = "refresh";
+ else if (rInfo->state == regState_UpdatePending) op = "txt record update";
+ debugf("Retransmit service %s %##s", op, srs->RR_SRV.resrec.name->c);
+#endif
+ if (rInfo->state == regState_DeregPending) { SendServiceDeregistration(m, srs); continue; }
+ else SendServiceRegistration (m, srs);
+ }
+ if (nextevent - srs->RR_SRV.LastAPTime + srs->RR_SRV.ThisAPInterval > 0)
+ nextevent = srs->RR_SRV.LastAPTime + srs->RR_SRV.ThisAPInterval;
+ }
+
+ if (rInfo->lease && rInfo->state == regState_Registered)
+ {
+ if (rInfo->expire - timenow < 0)
+ {
+ debugf("refreshing service %##s", srs->RR_SRV.resrec.name->c);
+ rInfo->state = regState_Refresh;
+ SendServiceRegistration(m, srs);
+ }
+ if (rInfo->expire - nextevent < 0) nextevent = rInfo->expire;
+ }
+ }
+ return nextevent;
+ }
+
+mDNSexport void uDNS_Execute(mDNS *const m)
+ {
+ uDNS_GlobalInfo *u = &m->uDNS_info;
+ mDNSs32 nexte, timenow = mDNSPlatformTimeNow(m);
+
+ u->nextevent = timenow + MIN_UCAST_PERIODIC_EXEC;
+
+ if (u->DelaySRVUpdate && u->NextSRVUpdate - timenow < 0)
+ {
+ u->DelaySRVUpdate = mDNSfalse;
+ UpdateSRVRecords(m);
+ }
+
+ nexte = CheckNATMappings(m, timenow);
+ if (nexte - u->nextevent < 0) u->nextevent = nexte;
+
+ if (m->SuppressStdPort53Queries && m->timenow - m->SuppressStdPort53Queries >= 0)
+ m->SuppressStdPort53Queries = 0; // If suppression time has passed, clear it
+
+ nexte = CheckQueries(m, timenow);
+ if (nexte - u->nextevent < 0) u->nextevent = nexte;
+
+ nexte = CheckRecordRegistrations(m, timenow);
+ if (nexte - u->nextevent < 0) u->nextevent = nexte;
+
+ nexte = CheckServiceRegistrations(m, timenow);
+ if (nexte - u->nextevent < 0) u->nextevent = nexte;
+
+ }
+
+// ***************************************************************************
+#if COMPILER_LIKES_PRAGMA_MARK
+#pragma mark - Startup, Shutdown, and Sleep
+#endif
+
+// DeregisterActive causes active LLQs to be removed from the server, e.g. before sleep. Pass false
+// following a location change, as the server will reject deletions from a source address different
+// from the address on which the LLQ was created.
+
+mDNSlocal void SuspendLLQs(mDNS *m, mDNSBool DeregisterActive)
+ {
+ DNSQuestion *q;
+ LLQ_Info *llq;
+ for (q = m->uDNS_info.ActiveQueries; q; q = q->next)
+ {
+ llq = q->uDNS_info.llq;
+ if (q->LongLived && llq)
+ {
+ if (llq->state == LLQ_GetZoneInfo)
+ {
+ debugf("Marking %##s suspend-deferred", q->qname.c);
+ llq->state = LLQ_SuspendDeferred; // suspend once we're done getting zone info
+ }
+ else if (llq->state < LLQ_Suspended)
+ {
+ if (DeregisterActive && (llq->state == LLQ_Established || llq->state == LLQ_Refresh))
+ { debugf("Deleting LLQ %##s", q->qname.c); sendLLQRefresh(m, q, 0); }
+ debugf("Marking %##s suspended", q->qname.c);
+ llq->state = LLQ_Suspended;
+ ubzero(llq->id, 8);
+ }
+ else if (llq->state == LLQ_Poll) { debugf("Marking %##s suspended-poll", q->qname.c); llq->state = LLQ_SuspendedPoll; }
+ if (llq->NATMap) llq->NATMap = mDNSfalse; // may not need nat mapping if we restart with new route
+ }
+ }
+ CheckForUnreferencedLLQMapping(m);
+ }
+
+mDNSlocal void RestartQueries(mDNS *m)
+ {
+ uDNS_GlobalInfo *u = &m->uDNS_info;
+ DNSQuestion *q;
+ LLQ_Info *llqInfo;
+ mDNSs32 timenow = mDNSPlatformTimeNow(m);
+
+ u->CurrentQuery = u->ActiveQueries;
+ while (u->CurrentQuery)
+ {
+ q = u->CurrentQuery;
+ u->CurrentQuery = u->CurrentQuery->next;
+ llqInfo = q->uDNS_info.llq;
+ q->uDNS_info.RestartTime = timenow;
+ q->uDNS_info.Answered = mDNSfalse;
+ if (q->LongLived)
+ {
+ if (!llqInfo) { LogMsg("Error: RestartQueries - %##s long-lived with NULL info", q->qname.c); continue; }
+ if (llqInfo->state == LLQ_Suspended || llqInfo->state == LLQ_NatMapWait)
+ {
+ llqInfo->ntries = -1;
+ llqInfo->deriveRemovesOnResume = mDNStrue;
+ startLLQHandshake(m, llqInfo, mDNStrue); // we set defer to true since several events that may generate restarts often arrive in rapid succession, and this cuts unnecessary packets
+ }
+ else if (llqInfo->state == LLQ_SuspendDeferred)
+ llqInfo->state = LLQ_GetZoneInfo; // we never finished getting zone data - proceed as usual
+ else if (llqInfo->state == LLQ_SuspendedPoll)
+ {
+ // if we were polling, we may have had bad zone data due to firewall, etc. - refetch
+ llqInfo->ntries = 0;
+ llqInfo->deriveRemovesOnResume = mDNStrue;
+ llqInfo->state = LLQ_GetZoneInfo;
+ startGetZoneData(&q->qname, m, mDNSfalse, mDNStrue, startLLQHandshakeCallback, llqInfo);
+ }
+ }
+ else { q->LastQTime = timenow; q->ThisQInterval = INIT_UCAST_POLL_INTERVAL; } // trigger poll in 1 second (to reduce packet rate when restarts come in rapid succession)
+ }
+ }
+
+mDNSexport void mDNS_UpdateLLQs(mDNS *m)
+ {
+ uDNS_GlobalInfo *u = &m->uDNS_info;
+
+ mDNS_Lock(m);
+ if (u->LLQNatInfo)
+ {
+ DeleteNATPortMapping(m, u->LLQNatInfo, mDNSNULL);
+ FreeNATInfo(m, u->LLQNatInfo); // routine clears u->LLQNatInfo ptr
+ }
+ SuspendLLQs(m, mDNStrue);
+ RestartQueries(m);
+ mDNS_Unlock(m);
+ }
+
+// simplest sleep logic - rather than having sleep states that must be dealt with explicitly in all parts of
+// the code, we simply send a deregistration, and put the service in Refresh state, with a timeout far enough
+// in the future that we'll sleep (or the sleep will be cancelled) before it is retransmitted. Then to wake,
+// we just move up the timers.
+
+
+
+mDNSlocal void SleepRecordRegistrations(mDNS *m)
+ {
+ DNSMessage msg;
+ AuthRecord *rr = m->uDNS_info.RecordRegistrations;
+ mDNSs32 timenow = mDNSPlatformTimeNow(m);
+
+ while (rr)
+ {
+ if (rr->uDNS_info.state == regState_Registered ||
+ rr->uDNS_info.state == regState_Refresh)
+ {
+ mDNSu8 *ptr = msg.data, *end = (mDNSu8 *)&msg + sizeof(DNSMessage);
+ InitializeDNSMessage(&msg.h, newMessageID(&m->uDNS_info), UpdateReqFlags);
+
+ // construct deletion update
+ ptr = putZone(&msg, ptr, end, &rr->uDNS_info.zone, mDNSOpaque16fromIntVal(rr->resrec.rrclass));
+ if (!ptr) { LogMsg("Error: SleepRecordRegistrations - could not put zone"); return; }
+ ptr = putDeletionRecord(&msg, ptr, &rr->resrec);
+ if (!ptr) { LogMsg("Error: SleepRecordRegistrations - could not put deletion record"); return; }
+
+ mDNSSendDNSMessage(m, &msg, ptr, mDNSInterface_Any, &rr->uDNS_info.ns, rr->uDNS_info.port, -1, GetAuthInfoForName(&m->uDNS_info, rr->resrec.name));
+ rr->uDNS_info.state = regState_Refresh;
+ rr->LastAPTime = timenow;
+ rr->ThisAPInterval = 300 * mDNSPlatformOneSecond;
+ }
+ rr = rr->next;
+ }
+ }
+
+mDNSlocal void WakeRecordRegistrations(mDNS *m)
+ {
+ mDNSs32 timenow = mDNSPlatformTimeNow(m);
+ AuthRecord *rr = m->uDNS_info.RecordRegistrations;
+
+ while (rr)
+ {
+ if (rr->uDNS_info.state == regState_Refresh)
+ {
+ // trigger slightly delayed refresh (we usually get this message before kernel is ready to send packets)
+ rr->LastAPTime = timenow;
+ rr->ThisAPInterval = INIT_UCAST_POLL_INTERVAL;
+ }
+ rr = rr->next;
+ }
+ }
+
+mDNSlocal void SleepServiceRegistrations(mDNS *m)
+ {
+ ServiceRecordSet *srs = m->uDNS_info.ServiceRegistrations;
+ while(srs)
+ {
+ uDNS_RegInfo *info = &srs->uDNS_info;
+ NATTraversalInfo *nat = info->NATinfo;
+
+ if (nat)
+ {
+ if (nat->state == NATState_Established || nat->state == NATState_Refresh || nat->state == NATState_Legacy)
+ DeleteNATPortMapping(m, nat, srs);
+ nat->reg.ServiceRegistration = mDNSNULL;
+ srs->uDNS_info.NATinfo = mDNSNULL;
+ FreeNATInfo(m, nat);
+ }
+
+ if (info->state == regState_UpdatePending)
+ {
+ // act as if the update succeeded, since we're about to delete the name anyway
+ AuthRecord *txt = &srs->RR_TXT;
+ uDNS_RegInfo *txtInfo = &txt->uDNS_info;
+ info->state = regState_Registered;
+ // deallocate old RData
+ if (txtInfo->UpdateRDCallback) txtInfo->UpdateRDCallback(m, txt, txtInfo->OrigRData);
+ SetNewRData(&txt->resrec, txtInfo->InFlightRData, txtInfo->InFlightRDLen);
+ txtInfo->OrigRData = mDNSNULL;
+ txtInfo->InFlightRData = mDNSNULL;
+ }
+
+ if (info->state == regState_Registered || info->state == regState_Refresh)
+ {
+ mDNSOpaque16 origid = srs->uDNS_info.id;
+ info->state = regState_DeregPending; // state expected by SendDereg()
+ SendServiceDeregistration(m, srs);
+ info->id = origid;
+ info->state = regState_NoTarget; // when we wake, we'll re-register (and optionally nat-map) once our address record completes
+ srs->RR_SRV.resrec.rdata->u.srv.target.c[0] = 0;
+ }
+ srs = srs->next;
+ }
+ }
+
+mDNSlocal void WakeServiceRegistrations(mDNS *m)
+ {
+ mDNSs32 timenow = mDNSPlatformTimeNow(m);
+ ServiceRecordSet *srs = m->uDNS_info.ServiceRegistrations;
+ while(srs)
+ {
+ if (srs->uDNS_info.state == regState_Refresh)
+ {
+ // trigger slightly delayed refresh (we usually get this message before kernel is ready to send packets)
+ srs->RR_SRV.LastAPTime = timenow;
+ srs->RR_SRV.ThisAPInterval = INIT_UCAST_POLL_INTERVAL;
+ }
+ srs = srs->next;
+ }
+ }
+
+mDNSexport void uDNS_Init(mDNS *const m)
+ {
+ mDNSPlatformMemZero(&m->uDNS_info, sizeof(uDNS_GlobalInfo));
+ m->uDNS_info.nextevent = m->timenow_last + 0x78000000;
+ }
+
+mDNSexport void uDNS_Sleep(mDNS *const m)
+ {
+ SuspendLLQs(m, mDNStrue);
+ SleepServiceRegistrations(m);
+ SleepRecordRegistrations(m);
+ }
+
+mDNSexport void uDNS_Wake(mDNS *const m)
+ {
+ RestartQueries(m);
+ WakeServiceRegistrations(m);
+ WakeRecordRegistrations(m);
+ }
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/uDNS.h b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/uDNS.h
new file mode 100644
index 0000000000..2a6a9edf2b
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/uDNS.h
@@ -0,0 +1,192 @@
+/* -*- 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.
+
+ Change History (most recent first):
+
+$Log: uDNS.h,v $
+Revision 1.32.2.1 2006/08/29 06:24:23 cheshire
+Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
+
+Revision 1.32 2005/07/29 19:46:10 ksekar
+<rdar://problem/4191860> reduce polling period on failed LLQs to 15 minutes
+
+Revision 1.31 2005/03/31 02:19:56 cheshire
+<rdar://problem/4021486> Fix build warnings
+Reviewed by: Scott Herscher
+
+Revision 1.30 2005/03/04 03:00:03 ksekar
+<rdar://problem/4026546> Retransmissions happen too early, causing registrations to conflict with themselves
+
+Revision 1.29 2005/01/11 22:50:53 ksekar
+Fixed constant naming (was using kLLQ_DefLease for update leases)
+
+Revision 1.28 2004/12/22 00:13:49 ksekar
+<rdar://problem/3873993> Change version, port, and polling interval for LLQ
+
+Revision 1.27 2004/11/23 04:06:50 cheshire
+Get rid of floating point constant -- in a small embedded device, bringing in all
+the floating point libraries just to halve an integer value is a bit too heavyweight.
+
+Revision 1.26 2004/11/22 17:49:15 ksekar
+Changed INIT_REFRESH from fraction to decimal
+
+Revision 1.25 2004/11/22 17:16:20 ksekar
+<rdar://problem/3854298> Unicast services don't disappear when you disable all networking
+
+Revision 1.24 2004/11/19 04:24:08 ksekar
+<rdar://problem/3682609> Security: Enforce a "window" on one-shot wide-area queries
+
+Revision 1.23 2004/11/18 18:04:21 ksekar
+Add INIT_REFRESH constant
+
+Revision 1.22 2004/11/15 20:09:24 ksekar
+<rdar://problem/3719050> Wide Area support for Add/Remove record
+
+Revision 1.21 2004/11/11 20:14:55 ksekar
+<rdar://problem/3719574> Wide-Area registrations not deregistered on sleep
+
+Revision 1.20 2004/10/16 00:16:59 cheshire
+<rdar://problem/3770558> Replace IP TTL 255 check with local subnet source address check
+
+Revision 1.19 2004/09/17 01:08:49 cheshire
+Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h
+ The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces
+ declared in that file are ONLY appropriate to single-address-space embedded applications.
+ For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used.
+
+Revision 1.18 2004/09/03 19:23:05 ksekar
+<rdar://problem/3788460>: Need retransmission mechanism for wide-area service registrations
+
+Revision 1.17 2004/09/01 03:59:29 ksekar
+<rdar://problem/3783453>: Conditionally compile out uDNS code on Windows
+
+Revision 1.16 2004/08/25 00:37:27 ksekar
+<rdar://problem/3774635>: Cleanup DynDNS hostname registration code
+
+Revision 1.15 2004/07/30 17:40:06 ksekar
+<rdar://problem/3739115>: TXT Record updates not available for wide-area services
+
+Revision 1.14 2004/07/29 19:27:15 ksekar
+NATPMP Support - minor fixes and cleanup
+
+Revision 1.13 2004/07/29 02:03:35 ksekar
+Delete unused #define and structure field
+
+Revision 1.12 2004/07/26 22:49:30 ksekar
+<rdar://problem/3651409>: Feature #9516: Need support for NATPMP in client
+
+Revision 1.11 2004/06/17 01:13:11 ksekar
+<rdar://problem/3696616>: polling interval too short
+
+Revision 1.10 2004/06/11 05:45:03 ksekar
+<rdar://problem/3682397>: Change SRV names for LLQ/Update port lookups
+
+Revision 1.9 2004/06/01 23:46:50 ksekar
+<rdar://problem/3675149>: DynDNS: dynamically look up LLQ/Update ports
+
+Revision 1.8 2004/05/28 23:42:37 ksekar
+<rdar://problem/3258021>: Feature: DNS server->client notification on record changes (#7805)
+
+Revision 1.7 2004/05/18 23:51:25 cheshire
+Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers
+
+Revision 1.6 2004/03/13 01:57:33 ksekar
+<rdar://problem/3192546>: DynDNS: Dynamic update of service records
+
+Revision 1.5 2004/02/21 08:56:58 bradley
+Wrap prototypes with extern "C" for C++ builds.
+
+Revision 1.4 2004/02/06 23:04:19 ksekar
+Basic Dynamic Update support via mDNS_Register (dissabled via
+UNICAST_REGISTRATION #define)
+
+Revision 1.3 2004/01/24 03:38:27 cheshire
+Fix minor syntactic error: Headers should use "extern" declarations, not "mDNSexport"
+
+Revision 1.2 2004/01/23 23:23:15 ksekar
+Added TCP support for truncated unicast messages.
+
+Revision 1.1 2003/12/13 03:05:27 ksekar
+<rdar://problem/3192548>: DynDNS: Unicast query of service records
+
+
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#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 MIN_UCAST_PERIODIC_EXEC (5 * mDNSPlatformOneSecond)
+#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 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 UPDATE_PORT_NAME "_dns-update._udp."
+#define LLQ_PORT_NAME "_dns-llq._udp"
+#define DEFAULT_UPDATE_LEASE 7200
+
+// Entry points into unicast-specific routines
+
+extern mStatus uDNS_StartQuery(mDNS *const m, DNSQuestion *const question);
+extern mDNSBool uDNS_IsActiveQuery(DNSQuestion *const question, uDNS_GlobalInfo *u); // returns true if OK to call StopQuery
+extern mStatus uDNS_StopQuery(mDNS *const m, DNSQuestion *const question);
+
+extern void uDNS_Init(mDNS *const m);
+extern void uDNS_Sleep(mDNS *const m);
+extern void uDNS_Wake(mDNS *const m);
+#define uDNS_Close uDNS_Sleep
+
+// uDNS_UpdateRecord
+// following fields must be set, and the update validated, upon entry.
+// rr->NewRData
+// rr->newrdlength
+// rr->UpdateCallback
+
+extern mStatus uDNS_AddRecordToService(mDNS *const m, ServiceRecordSet *sr, ExtraResourceRecord *extra);
+extern mStatus uDNS_UpdateRecord(mDNS *m, AuthRecord *rr);
+
+extern mStatus uDNS_RegisterRecord(mDNS *const m, AuthRecord *const rr);
+extern mStatus uDNS_DeregisterRecord(mDNS *const m, AuthRecord *const rr);
+
+extern mStatus uDNS_RegisterService(mDNS *const m, ServiceRecordSet *srs);
+extern mStatus uDNS_DeregisterService(mDNS *const m, ServiceRecordSet *srs);
+
+// 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, const mDNSAddr *const dstaddr,
+ const mDNSIPPort dstport, const mDNSInterfaceID InterfaceID);
+
+extern void uDNS_ReceiveNATMap(mDNS *m, mDNSu8 *pkt, mDNSu16 len);
+
+// returns time of next scheduled event
+extern void uDNS_Execute(mDNS *const m);
+
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif // __UDNS_H_
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/uds_daemon.c b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/uds_daemon.c
new file mode 100644
index 0000000000..4351e0edde
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/uds_daemon.c
@@ -0,0 +1,3695 @@
+/* -*- 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.
+
+ Change History (most recent first):
+
+$Log: uds_daemon.c,v $
+Revision 1.201.2.1 2006/08/29 06:24:36 cheshire
+Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
+
+Revision 1.201 2006/06/29 03:02:47 cheshire
+<rdar://problem/4607042> mDNSResponder NXDOMAIN and CNAME support
+
+Revision 1.200 2006/06/28 08:56:26 cheshire
+Added "_op" to the end of the operation code enum values,
+to differentiate them from the routines with the same names
+
+Revision 1.199 2006/06/28 08:53:39 cheshire
+Added (commented out) debugging messages
+
+Revision 1.198 2006/06/27 20:16:07 cheshire
+Fix code layout
+
+Revision 1.197 2006/05/18 01:32:35 cheshire
+<rdar://problem/4472706> iChat: Lost connection with Bonjour
+(mDNSResponder insufficiently defensive against malformed browsing PTR responses)
+
+Revision 1.196 2006/05/05 07:07:13 cheshire
+<rdar://problem/4538206> mDNSResponder fails when UDS reads deliver partial data
+
+Revision 1.195 2006/04/25 20:56:28 mkrochma
+Added comment about previous checkin
+
+Revision 1.194 2006/04/25 18:29:36 mkrochma
+Workaround for warning: unused variable 'status' when building mDNSPosix
+
+Revision 1.193 2006/03/19 17:14:38 cheshire
+<rdar://problem/4483117> Need faster purging of stale records
+read_rr_from_ipc_msg was not setting namehash and rdatahash
+
+Revision 1.192 2006/03/18 20:58:32 cheshire
+Misplaced curly brace
+
+Revision 1.191 2006/03/10 22:19:43 cheshire
+Update debugging message in resolve_result_callback() to indicate whether event is ADD or RMV
+
+Revision 1.190 2006/03/10 21:56:12 cheshire
+<rdar://problem/4111464> After record update, old record sometimes remains in cache
+When service TXT and SRV record both change, clients with active resolve calls get *two* callbacks, one
+when the TXT data changes, and then immediately afterwards a second callback with the new port number
+This change suppresses the first unneccessary (and confusing) callback
+
+Revision 1.189 2006/01/06 00:56:31 cheshire
+<rdar://problem/4400573> Should remove PID file on exit
+
+Revision 1.188 2005/10/11 22:15:03 cheshire
+<rdar://problem/4296042> Add memory corruption safeguards to uds_daemon.c
+Only compile uds_validatelists() when building for Mac OS X
+
+Revision 1.187 2005/10/11 20:30:27 cheshire
+<rdar://problem/4296042> Add memory corruption safeguards to uds_daemon.c
+
+Revision 1.186 2005/09/12 07:11:53 herscher
+<rdar://problem/4248878> Lazily call RegisterSearchDomains to workaround crashes of several routers. This code is conditionally compiled, and currently is only enabled on Windows platforms.
+
+Revision 1.185 2005/07/29 00:55:10 ksekar
+Removed validation check in uds_validatelists which generated false alarms
+
+Revision 1.184 2005/07/04 22:40:26 cheshire
+Additional debugging code to help catch memory corruption
+
+Revision 1.183 2005/06/13 22:39:11 cheshire
+<rdar://problem/4144870> Missing return statement in handle_enum_request() error handling
+
+Revision 1.182 2005/03/21 00:39:31 shersche
+<rdar://problem/4021486> Fix build warnings on Win32 platform
+
+Revision 1.181 2005/03/20 20:21:32 shersche
+<rdar://problem/4056827> mDNSResponder crashes when incorrect interface index is passed to DNSServiceRegister()
+Text record length and data parameters must be initialized to 0 and NULL to ensure that the service request
+object is cleaned up correctly when encountering an interface index error.
+
+Revision 1.180 2005/03/10 00:13:12 cheshire
+<rdar://problem/4043098> DNSServiceBrowse no longer returning error codes for invalid types
+In handle_browse_request(), mStatus err was being set correctly if an error occurred,
+but the end of the function returned mStatus_NoError intead of err.
+
+Revision 1.179 2005/03/04 02:47:26 ksekar
+<rdar://problem/4026393> SCPreference domains disappear from enumeration when moving out from firewall
+
+Revision 1.178 2005/02/25 19:35:38 ksekar
+<rdar://problem/4023750> Non-local empty string registration failures should not return errors to caller
+
+Revision 1.177 2005/02/25 03:05:41 cheshire
+Change "broken pipe" message to debugf()
+
+Revision 1.176 2005/02/24 18:44:45 ksekar
+<rdar://problem/4018516> Printer Sharing does not get re-registered with wide-area
+
+Revision 1.175 2005/02/21 21:31:25 ksekar
+<rdar://problem/4015162> changed LogMsg to debugf
+
+Revision 1.174 2005/02/20 01:41:17 cheshire
+Fix compiler signed/unsigned warning
+
+Revision 1.173 2005/02/18 01:26:42 cheshire
+<rdar://problem/4012162> "Could not write data to client after 60 seconds" message could be more helpful
+Log additional information about failed client
+
+Revision 1.172 2005/02/18 00:58:35 cheshire
+<rdar://problem/4012162> "Could not write data to client after 60 seconds" message could be more helpful
+
+Revision 1.171 2005/02/18 00:43:12 cheshire
+<rdar://problem/4010245> mDNSResponder should auto-truncate service names that are too long
+
+Revision 1.170 2005/02/16 01:15:02 cheshire
+Improve LogOperation() debugging messages for DNSServiceBrowse and DNSServiceRegister
+
+Revision 1.169 2005/02/08 01:57:14 cheshire
+More detailed error reporting in udsserver_init()
+
+Revision 1.168 2005/02/03 00:44:37 cheshire
+<rdar://problem/3986663> DNSServiceUpdateRecord returns kDNSServiceErr_Invalid when rdlen=0, rdata=NULL
+
+Revision 1.167 2005/02/02 02:19:32 cheshire
+Add comment explaining why unlink(MDNS_UDS_SERVERPATH); fails
+
+Revision 1.166 2005/02/01 19:58:52 ksekar
+Shortened cryptic "broken pipe" syslog message
+
+Revision 1.165 2005/02/01 19:56:47 ksekar
+Moved LogMsg from daemon.c to uds_daemon.c, cleaned up wording
+
+Revision 1.164 2005/01/28 06:07:55 cheshire
+Don't use deliver_error() from within handle_regrecord_request()
+
+Revision 1.163 2005/01/28 01:39:16 cheshire
+Include file descriptor number in "broken pipe" message
+
+Revision 1.162 2005/01/27 23:59:20 cheshire
+Remove extraneous LogMsg
+
+Revision 1.161 2005/01/27 22:57:56 cheshire
+Fix compile errors on gcc4
+
+Revision 1.160 2005/01/27 20:52:11 cheshire
+<rdar://problem/3972566> mDNSResponder leaks sockets for add/update/remove record calls
+
+Revision 1.159 2005/01/27 01:45:25 cheshire
+<rdar://problem/3976147> mDNSResponder should never call exit(1);
+
+Revision 1.158 2005/01/25 17:28:07 ksekar
+<rdar://problem/3971467> Should not return "local" twice for domain enumeration
+
+Revision 1.157 2005/01/21 02:20:39 cheshire
+Fix mistake in LogOperation() format string
+
+Revision 1.156 2005/01/19 19:15:36 ksekar
+Refinement to <rdar://problem/3954575> - Simplify mDNS_PurgeResultsForDomain logic and move into daemon layer
+
+Revision 1.155 2005/01/19 03:00:47 cheshire
+Show Add/Rmv in DNSServiceBrowse LogOperation() message
+
+Revision 1.154 2005/01/15 00:56:42 ksekar
+<rdar://problem/3954575> Unicast services don't disappear when logging
+out of VPN
+
+Revision 1.153 2005/01/14 18:44:28 ksekar
+<rdar://problem/3954609> mDNSResponder is crashing when changing domains
+
+Revision 1.152 2005/01/13 17:16:38 ksekar
+Back out checkin 1.150 - correct fix is on clientstub side
+
+Revision 1.151 2005/01/11 21:06:29 ksekar
+Changed now-benign LogMsg to debugf
+
+Revision 1.150 2005/01/07 23:59:15 ksekar
+<rdar://problem/3942900> dnd-sd shows the wrong port numbers
+
+Revision 1.149 2004/12/20 23:20:35 cheshire
+<rdar://problem/3928361> mDNSResponder crashes repeatedly when printer sharing is enabled
+Make sure to call mDNS_SetupResourceRecord() for all newly created AuthRecords
+
+Revision 1.148 2004/12/20 20:37:35 cheshire
+AllowRemoteQuery not set for the extras in a ServiceRecordSet
+
+Revision 1.147 2004/12/20 00:15:41 cheshire
+Include client file descriptor numbers in udsserver_info() output
+
+Revision 1.146 2004/12/17 05:25:47 cheshire
+<rdar://problem/3925163> Shorten DNS-SD queries to avoid NAT bugs
+
+Revision 1.145 2004/12/16 21:39:46 cheshire
+Include CacheGroup objects in CacheUsed count
+
+Revision 1.144 2004/12/16 21:27:38 ksekar
+Fixed build failures when compiled with verbose debugging messages
+
+Revision 1.143 2004/12/16 20:13:02 cheshire
+<rdar://problem/3324626> Cache memory management improvements
+
+Revision 1.142 2004/12/16 08:07:33 shersche
+Fix compiler error (mixed declarations and code) on Windows
+
+Revision 1.141 2004/12/16 01:56:21 cheshire
+Improve DNSServiceEnumerateDomains syslog message
+
+Revision 1.140 2004/12/14 03:02:10 ksekar
+<rdar://problem/3919016> Rare race condition can cause crash
+
+Revision 1.139 2004/12/13 21:18:45 ksekar
+Include uDNS registrations in CountPeerRegistrations
+
+Revision 1.138 2004/12/13 18:23:18 ksekar
+<rdar://problem/3915805> mDNSResponder error when quitting iChat -
+don't close sockets delivering errors to blocked clients
+
+Revision 1.137 2004/12/13 00:09:22 ksekar
+<rdar://problem/3915805> mDNSResponder error when quitting iChat
+
+Revision 1.136 2004/12/11 01:52:10 cheshire
+<rdar://problem/3785820> Support kDNSServiceFlagsAllowRemoteQuery for registering services too
+
+Revision 1.135 2004/12/10 20:46:37 cheshire
+Change LogOperation message to debugf
+
+Revision 1.134 2004/12/10 13:19:37 cheshire
+Add verbosedebugf() logging message in CountPeerRegistrations()
+
+Revision 1.133 2004/12/10 05:27:26 cheshire
+<rdar://problem/3909147> Guard against multiple autoname services of the same type on the same machine
+
+Revision 1.132 2004/12/10 04:28:28 cheshire
+<rdar://problem/3914406> User not notified of name changes for services using new UDS API
+
+Revision 1.131 2004/12/10 02:09:25 cheshire
+<rdar://problem/3898376> Modify default TTLs
+
+Revision 1.130 2004/12/10 00:55:24 cheshire
+Add full name and type to LogOperation messages for DNSServiceAddRecord/UpdateRecord/RemoveRecord
+
+Revision 1.129 2004/12/09 03:17:23 ksekar
+<rdar://problem/3910435> DomainEnumeration interface index should be zero
+
+Revision 1.128 2004/12/07 21:26:05 ksekar
+<rdar://problem/3908336> DNSServiceRegisterRecord() can crash on deregistration
+
+Revision 1.127 2004/12/07 20:42:34 cheshire
+Add explicit context parameter to mDNS_RemoveRecordFromService()
+
+Revision 1.126 2004/12/07 17:23:55 ksekar
+Fixed LogOperation
+
+Revision 1.125 2004/12/06 21:15:23 ksekar
+<rdar://problem/3884386> mDNSResponder crashed in CheckServiceRegistrations
+
+Revision 1.124 2004/11/30 02:19:14 cheshire
+<rdar://problem/3827971> Raise maxfds.rlim_cur for mDNSResponder
+
+Revision 1.123 2004/11/29 23:50:57 cheshire
+Checkin 1.122 not necessary
+
+Revision 1.122 2004/11/24 17:55:01 ksekar
+Added log message clarifying <rdar://problem/3869241> For unicast operations, verify that service types are legal
+
+Revision 1.121 2004/11/24 04:45:52 cheshire
+Spelling mistake
+
+Revision 1.120 2004/11/24 00:10:44 cheshire
+<rdar://problem/3869241> For unicast operations, verify that service types are legal
+
+Revision 1.119 2004/11/23 23:54:17 ksekar
+<rdar://problem/3890318> Wide-Area DNSServiceRegisterRecord() failures
+can crash mDNSResponder
+
+Revision 1.118 2004/11/23 22:33:01 cheshire
+<rdar://problem/3654910> Remove temporary workaround code for iChat
+
+Revision 1.117 2004/11/23 20:23:10 ksekar
+Fixed LogOperation that causes crash on connected service deregistrations
+
+Revision 1.116 2004/11/23 03:39:47 cheshire
+Let interface name/index mapping capability live directly in JNISupport.c,
+instead of having to call through to the daemon via IPC to get this information.
+
+Revision 1.115 2004/11/13 00:12:53 ksekar
+Fixed some LogOperation printf converstions for debug builds.
+
+Revision 1.114 2004/11/12 18:25:45 shersche
+Tidy up cross platform usleep code fragment.
+
+Revision 1.113 2004/11/12 03:21:41 rpantos
+rdar://problem/3809541 Add DNSSDMapIfIndexToName, DNSSDMapNameToIfIndex.
+
+Revision 1.112 2004/11/11 16:58:32 ksekar
+Removed unused code (previously wrapped in #if 0)
+
+Revision 1.111 2004/11/05 22:47:37 shersche
+Conditionally compile usleep(1000) to be Sleep(1) on Windows
+Submitted by: Pavel Repin <prepin@gmail.com>
+
+Revision 1.110 2004/11/05 19:56:56 ksekar
+<rdar://problem/3862646> We no longer need to browse .Mac domains by
+default - changed #if 0 to more descriptive #ifdef _HAVE_SETDOMAIN_SUPPORT_
+
+Revision 1.109 2004/11/04 03:40:45 cheshire
+More debugging messages
+
+Revision 1.108 2004/11/03 02:25:51 cheshire
+<rdar://problem/3324137> Conflict for Computer Name should update *all* empty string services, not just the one with the conflict
+
+Revision 1.107 2004/11/02 19:39:23 ksekar
+<rdar://problem/3862646> We no longer need to browse .Mac domains by default
+
+Revision 1.106 2004/11/02 02:12:21 cheshire
+<rdar://problem/3839111> Remove unnecessary memory allocations
+
+Revision 1.105 2004/10/28 19:07:19 cheshire
+Add some more debugging checks and improved LogOperation() messages
+
+Revision 1.104 2004/10/26 18:53:15 cheshire
+Avoid unused variable warning
+
+Revision 1.103 2004/10/26 07:15:55 cheshire
+Add file descriptor number to all LogOperation messages
+
+Revision 1.102 2004/10/26 06:11:42 cheshire
+Add improved logging to aid in diagnosis of <rdar://problem/3842714> mDNSResponder crashed
+
+Revision 1.101 2004/10/26 04:31:44 cheshire
+Rename CountSubTypes() as ChopSubTypes()
+
+Revision 1.100 2004/10/26 01:17:48 cheshire
+Use "#if 0" instead of commenting out code
+
+Revision 1.99 2004/10/19 21:33:22 cheshire
+<rdar://problem/3844991> Cannot resolve non-local registrations using the mach API
+Added flag 'kDNSServiceFlagsForceMulticast'. Passing through an interface id for a unicast name
+doesn't force multicast unless you set this flag to indicate explicitly that this is what you want
+
+Revision 1.98 2004/10/14 01:59:33 cheshire
+<rdar://problem/3839208> UDS resolves don't work for uDNS services
+
+Revision 1.97 2004/10/13 00:58:35 cheshire
+<rdar://problem/3832738> Registering a proxy doesn't work
+
+Revision 1.96 2004/09/30 00:25:00 ksekar
+<rdar://problem/3695802> Dynamically update default registration domains on config change
+
+Revision 1.95 2004/09/26 23:20:36 ksekar
+<rdar://problem/3813108> Allow default registrations in multiple wide-area domains
+
+Revision 1.94 2004/09/22 18:27:06 ksekar
+<rdar://problem/3811427> allow DNSServiceAddRecord to pass zero to get
+default record TTL
+
+Revision 1.93 2004/09/22 02:39:44 cheshire
+<rdar://problem/3810757> Allow DNSServiceRegisterRecord to pass zero to get default record TTL
+
+Revision 1.92 2004/09/22 02:34:04 cheshire
+Rename parameter "ttl" to "GetTTL" for clarity
+
+Revision 1.91 2004/09/22 02:25:43 cheshire
+Fix spelling errors
+
+Revision 1.90 2004/09/21 23:40:12 ksekar
+<rdar://problem/3810349> mDNSResponder to return errors on NAT traversal failure
+
+Revision 1.89 2004/09/21 23:29:51 cheshire
+<rdar://problem/3680045> DNSServiceResolve should delay sending packets
+
+Revision 1.88 2004/09/21 23:12:46 cheshire
+Reorder initialization of question fields to match structure order
+
+Revision 1.87 2004/09/21 22:18:33 cheshire
+In SIGINFO output, display a '-' next to records that have the Unique bit set
+
+Revision 1.86 2004/09/21 21:05:11 cheshire
+Move duplicate code out of mDNSMacOSX/daemon.c and mDNSPosix/PosixDaemon.c,
+into mDNSShared/uds_daemon.c
+
+Revision 1.85 2004/09/18 01:11:58 ksekar
+<rdar://problem/3806734> Add a user's default domain to empty-string browse list
+
+Revision 1.84 2004/09/17 01:08:55 cheshire
+Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h
+ The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces
+ declared in that file are ONLY appropriate to single-address-space embedded applications.
+ For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used.
+
+Revision 1.83 2004/09/16 23:26:33 cheshire
+Move version check inside preceeding "if" that checks we have a complete header
+
+Revision 1.82 2004/09/16 23:14:25 cheshire
+Changes for Windows compatibility
+
+Revision 1.81 2004/09/16 21:46:38 ksekar
+<rdar://problem/3665304> Need SPI for LoginWindow to associate a UID with a Wide Area domain
+
+Revision 1.80 2004/09/16 01:58:23 cheshire
+Fix compiler warnings
+
+Revision 1.79 2004/09/16 00:24:49 cheshire
+<rdar://problem/3803162> Fix unsafe use of mDNSPlatformTimeNow()
+
+Revision 1.78 2004/09/15 21:44:20 cheshire
+<rdar://problem/3681031> Randomize initial timenow_adjust value in mDNS_Init
+Show time value in log to help diagnose errors
+
+Revision 1.77 2004/09/15 00:19:18 cheshire
+<rdar://problem/3785823> read_rr_from_ipc_msg should use mDNS_SetupResourceRecord()
+
+Revision 1.76 2004/09/02 06:39:52 cheshire
+Minor textual cleanup for clarity
+
+Revision 1.75 2004/09/02 03:48:47 cheshire
+<rdar://problem/3709039> Disable targeted unicast query support by default
+1. New flag kDNSServiceFlagsAllowRemoteQuery to indicate we want to allow remote queries for this record
+2. New field AllowRemoteQuery in AuthRecord structure
+3. uds_daemon.c sets AllowRemoteQuery if kDNSServiceFlagsAllowRemoteQuery is set
+4. mDNS.c only answers remote queries if AllowRemoteQuery is set
+
+Revision 1.74 2004/08/25 02:32:47 cheshire
+Minor cleanup: replace "&regtype[0]" with "regtype"
+
+Revision 1.73 2004/08/25 02:30:40 cheshire
+<rdar://problem/3588761> Current method of doing subtypes causes name collisions
+
+Revision 1.72 2004/08/14 03:22:42 cheshire
+<rdar://problem/3762579> Dynamic DNS UI <-> mDNSResponder glue
+Add GetUserSpecifiedDDNSName() routine
+Convert ServiceRegDomain to domainname instead of C string
+Replace mDNS_GenerateFQDN/mDNS_GenerateGlobalFQDN with mDNS_SetFQDNs
+
+Revision 1.71 2004/08/11 04:21:21 rpantos
+Fix Windows build.
+
+Revision 1.70 2004/08/11 02:07:00 cheshire
+Remove "mDNS *globalInstance" parameter from udsserver_init()
+Move CheckForDuplicateRegistrations from daemon.c
+<rdar://problem/3501938> No warning when accidentally registering the same service multiple times using socket API
+
+Revision 1.69 2004/08/10 16:14:48 cheshire
+Fix debug builds (oops)
+
+Revision 1.68 2004/08/10 06:24:56 cheshire
+Use types with precisely defined sizes for 'op' and 'reg_index', for better
+compatibility if the daemon and the client stub are built using different compilers
+
+Revision 1.67 2004/07/27 07:14:16 shersche
+make error socket non-blocking after call to connect()
+
+Revision 1.66 2004/07/13 21:24:25 rpantos
+Fix for <rdar://problem/3701120>.
+
+Revision 1.65 2004/06/26 03:17:14 shersche
+implement cross-platform strerror function
+
+Submitted by: herscher
+
+Revision 1.64 2004/06/25 00:26:27 rpantos
+Changes to fix the Posix build on Solaris.
+
+Revision 1.63 2004/06/24 03:43:44 rpantos
+Fix previous checkin so it builds on Windows.
+
+Revision 1.62 2004/06/24 00:57:08 ksekar
+Replaced code acccidentally removed in checkin 1.59.
+
+Revision 1.61 2004/06/19 00:09:39 cheshire
+Remove unused strsep() implementation
+
+Revision 1.60 2004/06/18 19:10:00 cheshire
+<rdar://problem/3588761> Current method of doing subtypes causes name collisions
+
+Revision 1.59 2004/06/18 05:10:31 rpantos
+Changes to allow code to be used on Windows
+
+Revision 1.58 2004/06/15 03:54:08 cheshire
+Include mDNS_TimeNow(&mDNSStorage) in SIGINFO output
+
+Revision 1.57 2004/06/12 01:47:27 ksekar
+<rdar://problem/3690241>: BBEdit crashes when trying to check for newer version
+udsserver_idle compared time in ticks to interval in seconds.
+
+Revision 1.56 2004/06/12 01:35:47 cheshire
+Changes for Windows compatibility
+
+Revision 1.55 2004/06/05 00:04:27 cheshire
+<rdar://problem/3668639>: wide-area domains should be returned in reg. domain enumeration
+
+Revision 1.54 2004/06/01 22:22:52 ksekar
+<rdar://problem/3668635>: wide-area default registrations should be in
+.local too
+
+Revision 1.53 2004/05/28 23:42:37 ksekar
+<rdar://problem/3258021>: Feature: DNS server->client notification on record changes (#7805)
+
+Revision 1.52 2004/05/26 00:39:49 ksekar
+<rdar://problem/3667105>: wide-area DNS-SD servers don't appear in
+Finder
+Use local-only InterfaceID for GetDomains calls for sockets-API
+
+Revision 1.51 2004/05/18 23:51:27 cheshire
+Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers
+
+Revision 1.50 2004/05/14 16:39:47 ksekar
+Browse for iChat locally for now.
+
+Revision 1.49 2004/05/13 21:33:52 ksekar
+Clean up non-local registration control via config file. Force iChat
+registrations to be local for now.
+
+Revision 1.48 2004/05/13 04:13:19 ksekar
+Updated SIGINFO handler for multi-domain browses
+
+Revision 1.47 2004/05/12 22:04:01 ksekar
+Implemented multi-domain browsing by default for uds_daemon.
+
+Revision 1.46 2004/05/06 18:42:58 ksekar
+General dns_sd.h API cleanup, including the following radars:
+<rdar://problem/3592068>: Remove flags with zero value
+<rdar://problem/3479569>: Passing in NULL causes a crash.
+
+Revision 1.45 2004/03/12 08:49:28 cheshire
+#include <sys/socket.h>
+
+Revision 1.44 2004/02/25 01:25:27 ksekar
+<rdar://problem/3569212>: DNSServiceRegisterRecord flags not error-checked
+
+Revision 1.43 2004/02/24 01:46:40 cheshire
+Manually reinstate lost checkin 1.36
+
+Revision 1.42 2004/02/05 19:39:29 cheshire
+Move creation of /var/run/mDNSResponder.pid to uds_daemon.c,
+so that all platforms get this functionality
+
+Revision 1.41 2004/02/03 18:59:02 cheshire
+Change "char *domain" parameter for format_enumeration_reply to "const char *domain"
+
+Revision 1.40 2004/01/28 03:41:00 cheshire
+<rdar://problem/3541946>: Need ability to do targeted queries as well as multicast queries
+
+Revision 1.39 2004/01/25 00:03:21 cheshire
+Change to use mDNSVal16() instead of private PORT_AS_NUM() macro
+
+Revision 1.38 2004/01/19 19:51:46 cheshire
+Fix compiler error (mixed declarations and code) on some versions of Linux
+
+Revision 1.37 2003/12/08 21:11:42 rpantos
+Changes necessary to support mDNSResponder on Linux.
+
+Revision 1.36 2003/12/04 23:40:57 cheshire
+<rdar://problem/3484766>: Security: Crashing bug in mDNSResponder
+Fix some more code that should use buffer size MAX_ESCAPED_DOMAIN_NAME (1005) instead of 256-byte buffers.
+
+Revision 1.35 2003/12/03 19:10:22 ksekar
+<rdar://problem/3498644>: malloc'd data not zero'd
+
+Revision 1.34 2003/12/03 02:00:01 ksekar
+<rdar://problem/3498644>: malloc'd data not zero'd
+
+Revision 1.33 2003/11/22 01:18:46 ksekar
+<rdar://problem/3486646>: config change handler not called for dns-sd services
+
+Revision 1.32 2003/11/20 21:46:12 ksekar
+<rdar://problem/3486635>: leak: DNSServiceRegisterRecord
+
+Revision 1.31 2003/11/20 20:33:05 ksekar
+<rdar://problem/3486635>: leak: DNSServiceRegisterRecord
+
+Revision 1.30 2003/11/20 02:10:55 ksekar
+<rdar://problem/3486643>: cleanup DNSServiceAdd/RemoveRecord
+
+Revision 1.29 2003/11/14 21:18:32 cheshire
+<rdar://problem/3484766>: Security: Crashing bug in mDNSResponder
+Fix code that should use buffer size MAX_ESCAPED_DOMAIN_NAME (1005) instead of 256-byte buffers.
+
+Revision 1.28 2003/11/08 22:18:29 cheshire
+<rdar://problem/3477870>: Don't need to show process ID in *every* mDNSResponder syslog message
+
+Revision 1.27 2003/11/05 22:44:57 ksekar
+<rdar://problem/3335230>: No bounds checking when reading data from client
+Reviewed by: Stuart Cheshire
+
+Revision 1.26 2003/10/23 17:51:04 ksekar
+<rdar://problem/3335216>: handle blocked clients more efficiently
+Changed gettimeofday() to mDNS_TimeNow(&mDNSStorage);
+
+Revision 1.25 2003/10/22 23:37:49 ksekar
+<rdar://problem/3459141>: crash/hang in abort_client
+
+Revision 1.24 2003/10/21 20:59:40 ksekar
+<rdar://problem/3335216>: handle blocked clients more efficiently
+
+Revision 1.23 2003/09/23 02:12:43 cheshire
+Also include port number in list of services registered via new UDS API
+
+Revision 1.22 2003/08/19 16:03:55 ksekar
+<rdar://problem/3380097>: ER: SIGINFO dump should include resolves started by DNSServiceQueryRecord
+Check termination_context for NULL before dereferencing.
+
+Revision 1.21 2003/08/19 05:39:43 cheshire
+<rdar://problem/3380097> SIGINFO dump should include resolves started by DNSServiceQueryRecord
+
+Revision 1.20 2003/08/16 03:39:01 cheshire
+<rdar://problem/3338440> InterfaceID -1 indicates "local only"
+
+Revision 1.19 2003/08/15 20:16:03 cheshire
+<rdar://problem/3366590> mDNSResponder takes too much RPRVT
+We want to avoid touching the rdata pages, so we don't page them in.
+1. RDLength was stored with the rdata, which meant touching the page just to find the length.
+ Moved this from the RData to the ResourceRecord object.
+2. To avoid unnecessarily touching the rdata just to compare it,
+ compute a hash of the rdata and store the hash in the ResourceRecord object.
+
+Revision 1.18 2003/08/15 00:38:00 ksekar
+<rdar://problem/3377005>: Bug: buffer overrun when reading long rdata from client
+
+Revision 1.17 2003/08/14 02:18:21 cheshire
+<rdar://problem/3375491> Split generic ResourceRecord type into two separate types: AuthRecord and CacheRecord
+
+Revision 1.16 2003/08/13 23:58:52 ksekar
+<rdar://problem/3374911>: Bug: UDS Sub-type browsing works, but not sub-type registration
+Fixed pointer increment error, moved subtype reading for-loop for easier error bailout.
+
+Revision 1.15 2003/08/13 17:30:33 ksekar
+<rdar://problem/3374671>: DNSServiceAddRecord doesn't work
+Fixed various problems with handling the AddRecord request and freeing the ExtraResourceRecords.
+
+Revision 1.14 2003/08/12 19:56:25 cheshire
+Update to APSL 2.0
+
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#if defined(_WIN32)
+#include <process.h>
+#define MDNS_LAZY_REGISTER_SEARCH_DOMAINS
+#define dnssd_strerror(X) win32_strerror(X)
+#define usleep(X) Sleep(((X)+999)/1000)
+static char * win32_strerror(int inErrorCode);
+#else
+#include <fcntl.h>
+#include <errno.h>
+#include <sys/ioctl.h>
+#include <sys/types.h>
+#include <sys/time.h>
+#include <sys/resource.h>
+#define dnssd_strerror(X) strerror(X)
+#endif
+
+#include <stdlib.h>
+#include <stdio.h>
+#include "mDNSEmbeddedAPI.h"
+#include "DNSCommon.h"
+#include "uds_daemon.h"
+#include "dns_sd.h"
+#include "dnssd_ipc.h"
+
+// Apple specific configuration functionality, not required for other platforms
+#ifdef __MACOSX__
+#include <sys/ucred.h>
+#ifndef LOCAL_PEERCRED
+#define LOCAL_PEERCRED 0x001 /* retrieve peer credentials */
+#endif // LOCAL_PEERCRED
+#endif //__MACOSX__
+
+#if defined(MDNS_LAZY_REGISTER_SEARCH_DOMAINS)
+extern mStatus dDNS_RegisterSearchDomains( mDNS * const m );
+#endif
+
+// Types and Data Structures
+// ----------------------------------------------------------------------
+
+typedef enum
+ {
+ t_uninitialized,
+ t_morecoming,
+ t_complete,
+ t_error,
+ t_terminated
+ } transfer_state;
+
+typedef void (*req_termination_fn)(void *);
+
+typedef struct registered_record_entry
+ {
+ uint32_t key;
+ AuthRecord *rr;
+ struct registered_record_entry *next;
+ client_context_t client_context;
+ struct request_state *rstate;
+ } 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;
+ 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?
+ mDNSBool rename_on_memfree; // Set on config change when we deregister original name
+ domainlabel name;
+ domainname domain;
+ mDNSBool default_local; // is this the "local." from an empty-string registration?
+ struct request_state *request;
+ dnssd_sock_t sd;
+ AuthRecord *subtypes;
+ ServiceRecordSet srs; // note - must be last field in struct
+ } service_instance;
+
+// A client-created service. May reference several service_info objects if default
+// settings cause registration in multiple domains.
+typedef struct
+ {
+ uint16_t 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;
+ mDNSInterfaceID InterfaceID;
+ service_instance *instances;
+ struct request_state *request;
+ } service_info;
+
+// for multi-domain default browsing
+typedef struct browser_t
+ {
+ DNSQuestion q;
+ domainname domain;
+ struct browser_t *next;
+ } browser_t;
+
+// parent struct for browser instances: list pointer plus metadata
+typedef struct
+ {
+ mDNSBool default_domain;
+ mDNSBool ForceMCast;
+ domainname regtype;
+ mDNSInterfaceID interface_id;
+ struct request_state *rstate;
+ browser_t *browsers;
+ } browser_info_t;
+
+typedef struct
+ {
+ mStatus err; // Note: This field is in NETWORK byte order
+ int nwritten;
+ dnssd_sock_t sd;
+ } undelivered_error_t;
+
+typedef struct request_state
+ {
+ // connection structures
+ dnssd_sock_t sd;
+
+ // state of read (in case message is read over several recv() calls)
+ transfer_state ts;
+ uint32_t hdr_bytes; // bytes of header already read
+ ipc_msg_hdr hdr;
+ uint32_t data_bytes; // bytes of message data already read
+ char *msgbuf; // pointer to data storage to pass to free()
+ char *msgdata; // pointer to data to be read from (may be modified)
+ int bufsize; // size of data storage
+
+ // reply, termination, error, and client context info
+ int no_reply; // don't send asynchronous replies to client
+ int time_blocked; // record time of a blocked client
+ void *client_context; // don't touch this - pointer only valid in client's addr space
+ struct reply_state *replies; // corresponding (active) reply list
+ undelivered_error_t *u_err;
+ void *termination_context;
+ req_termination_fn terminate;
+
+ //!!!KRS toss these pointers in a union
+ // registration context associated with this request (null if not applicable)
+ registered_record_entry *reg_recs; // muliple registrations for a connection-oriented request
+ service_info *service_registration;
+ browser_info_t *browser_info;
+ struct request_state *next;
+ } request_state;
+
+// 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
+ uint32_t 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
+ {
+ // state of the transmission
+ dnssd_sock_t sd;
+ transfer_state ts;
+ uint32_t nwriten;
+ uint32_t len;
+ // context of the reply
+ struct request_state *request; // the request that this answers
+ struct reply_state *next; // if there are multiple unsent replies
+ // pointer into message buffer - allows fields to be changed after message is formatted
+ ipc_msg_hdr *mhdr;
+ reply_hdr *rhdr;
+ char *sdata; // pointer to start of call-specific data
+ // pointer to malloc'd buffer
+ char *msgbuf;
+ } reply_state;
+
+// domain enumeration and resolv calls require 2 mDNSCore calls, so we need separate interconnected
+// structures to handle callbacks
+typedef struct
+ {
+ DNSQuestion question;
+ mDNS_DomainType type;
+ request_state *rstate;
+ } domain_enum_t;
+
+typedef struct
+ {
+ domain_enum_t *all;
+ domain_enum_t *def;
+ request_state *rstate;
+ } enum_termination_t;
+
+typedef struct
+ {
+ request_state *rstate;
+ DNSQuestion qtxt;
+ DNSQuestion qsrv;
+ // const ResourceRecord *txt;
+ // const ResourceRecord *srv;
+ mDNSBool srv;
+ mDNSBool txt;
+ rdataSRV srvdata;
+ mDNSu16 txtlen;
+ mDNSu8 txtdata[AbsoluteMaxDNSMessageData];
+ } resolve_termination_t;
+
+#ifdef _HAVE_SETDOMAIN_SUPPORT_
+typedef struct default_browse_list_t
+ {
+ struct default_browse_list_t *next;
+ uid_t uid;
+ AuthRecord ptr_rec;
+ } default_browse_list_t;
+
+static default_browse_list_t *default_browse_list = NULL;
+#endif // _HAVE_SETDOMAIN_SUPPORT_
+
+// globals
+mDNSexport mDNS mDNSStorage;
+#define gmDNS (&mDNSStorage)
+
+static dnssd_sock_t listenfd = dnssd_InvalidSocket;
+static request_state * all_requests = NULL;
+
+#define MAX_TIME_BLOCKED 60 * mDNSPlatformOneSecond // try to send data to a blocked client for 60 seconds before
+ // terminating connection
+#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
+// private function prototypes
+mDNSlocal void connect_callback(void *info);
+mDNSlocal int read_msg(request_state *rs);
+mDNSlocal int send_msg(reply_state *rs);
+mDNSlocal void abort_request(request_state *rs);
+mDNSlocal void request_callback(void *info);
+mDNSlocal void handle_resolve_request(request_state *rstate);
+mDNSlocal void question_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord);
+mDNSlocal void question_termination_callback(void *context);
+mDNSlocal void handle_browse_request(request_state *request);
+mDNSlocal void browse_termination_callback(void *context);
+mDNSlocal void handle_regservice_request(request_state *request);
+mDNSlocal void regservice_termination_callback(void *context);
+mDNSlocal void regservice_callback(mDNS *const m, ServiceRecordSet *const srs, mStatus result);
+mDNSlocal mStatus handle_add_request(request_state *rstate);
+mDNSlocal mStatus handle_update_request(request_state *rstate);
+mDNSlocal void append_reply(request_state *req, reply_state *rep);
+mDNSlocal int build_domainname_from_strings(domainname *srv, char *name, char *regtype, char *domain);
+mDNSlocal void enum_termination_callback(void *context);
+mDNSlocal void enum_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord);
+mDNSlocal void handle_query_request(request_state *rstate);
+mDNSlocal reply_state *format_enumeration_reply(request_state *rstate, const char *domain, DNSServiceFlags flags, uint32_t ifi, DNSServiceErrorType err);
+mDNSlocal void handle_enum_request(request_state *rstate);
+mDNSlocal mStatus handle_regrecord_request(request_state *rstate);
+mDNSlocal void regrecord_callback(mDNS *const m, AuthRecord * rr, mStatus result);
+mDNSlocal void connected_registration_termination(void *context);
+mDNSlocal void handle_reconfirm_request(request_state *rstate);
+mDNSlocal AuthRecord *read_rr_from_ipc_msg(char *msgbuf, int ttl, int validate_flags);
+mDNSlocal mStatus handle_removerecord_request(request_state *rstate);
+mDNSlocal void reset_connected_rstate(request_state *rstate);
+mDNSlocal int deliver_error(request_state *rstate, mStatus err);
+mDNSlocal int deliver_async_error(request_state *rs, reply_op_t op, mStatus err);
+mDNSlocal transfer_state send_undelivered_error(request_state *rs);
+mDNSlocal reply_state *create_reply(reply_op_t op, size_t datalen, request_state *request);
+mDNSlocal void update_callback(mDNS *const m, AuthRecord *const rr, RData *oldrd);
+mDNSlocal void my_perror(char *errmsg);
+mDNSlocal void unlink_request(request_state *rs);
+mDNSlocal void resolve_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord);
+mDNSlocal void resolve_termination_callback(void *context);
+mDNSlocal int validate_message(request_state *rstate);
+mDNSlocal mStatus remove_extra(request_state *rstate, service_instance *serv);
+mDNSlocal mStatus remove_record(request_state *rstate);
+mDNSlocal void free_service_instance(service_instance *srv);
+mDNSlocal uint32_t dnssd_htonl(uint32_t l);
+mDNSlocal void handle_setdomain_request(request_state *rstate);
+
+// 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
+
+mDNSlocal void LogClientInfo(request_state *req)
+ {
+ void *t = req->termination_context;
+ if (t)
+ {
+ if (req->terminate == regservice_termination_callback)
+ {
+ service_instance *ptr;
+ for (ptr = ((service_info *)t)->instances; ptr; ptr = ptr->next)
+ LogMsgNoIdent("%3d: DNSServiceRegister %##s %u", req->sd, ptr->srs.RR_SRV.resrec.name->c, SRS_PORT(&ptr->srs));
+ }
+ else if (req->terminate == browse_termination_callback)
+ {
+ browser_t *blist;
+ for (blist = req->browser_info->browsers; blist; blist = blist->next)
+ LogMsgNoIdent("%3d: DNSServiceBrowse %##s", req->sd, blist->q.qname.c);
+ }
+ else if (req->terminate == resolve_termination_callback)
+ LogMsgNoIdent("%3d: DNSServiceResolve %##s", req->sd, ((resolve_termination_t *)t)->qsrv.qname.c);
+ else if (req->terminate == question_termination_callback)
+ LogMsgNoIdent("%3d: DNSServiceQueryRecord %##s", req->sd, ((DNSQuestion *) t)->qname.c);
+ else if (req->terminate == enum_termination_callback)
+ LogMsgNoIdent("%3d: DNSServiceEnumerateDomains %##s", req->sd, ((enum_termination_t *) t)->all->question.qname.c);
+ }
+ }
+
+mDNSlocal void FatalError(char *errmsg)
+ {
+ LogMsg("%s: %s", errmsg, dnssd_strerror(dnssd_errno()));
+ *(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
+ }
+
+int udsserver_init(void)
+ {
+ dnssd_sockaddr_t laddr;
+ int ret;
+#if defined(_WIN32)
+ u_long opt = 1;
+#endif
+
+ // 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 ((listenfd = socket(AF_DNSSD, SOCK_STREAM, 0)) == dnssd_InvalidSocket)
+ {
+ my_perror("ERROR: socket(AF_DNSSD, SOCK_STREAM, 0); failed");
+ goto error;
+ }
+
+ bzero(&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
+ strcpy(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 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
+ //
+ if (ioctlsocket(listenfd, FIONBIO, &opt) != 0)
+ #else
+ if (fcntl(listenfd, F_SETFL, O_NONBLOCK) != 0)
+ #endif
+ {
+ my_perror("ERROR: could not set listen socket to non-blocking mode");
+ goto error;
+ }
+
+ if (listen(listenfd, LISTENQ) != 0)
+ {
+ my_perror("ERROR: could not listen on listen socket");
+ goto error;
+ }
+
+ if (mStatus_NoError != udsSupportAddFDToEventLoop(listenfd, connect_callback, (void *) NULL))
+ {
+ my_perror("ERROR: could not add listen socket to event loop");
+ 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
+
+ return 0;
+
+error:
+
+ my_perror("ERROR: udsserver_init");
+ return -1;
+ }
+
+int udsserver_exit(void)
+ {
+ 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;
+ }
+
+mDNSs32 udsserver_idle(mDNSs32 nextevent)
+ {
+ request_state *req = all_requests, *tmp, *prev = NULL;
+ reply_state *fptr;
+ transfer_state result;
+ mDNSs32 now = mDNS_TimeNow(&mDNSStorage);
+
+ while(req)
+ {
+ result = t_uninitialized;
+ if (req->u_err)
+ result = send_undelivered_error(req);
+ if (result != t_error && result != t_morecoming && // don't try to send msg if send_error failed
+ (req->ts == t_complete || req->ts == t_morecoming))
+ {
+ while(req->replies)
+ {
+ if (req->replies->next) req->replies->rhdr->flags |= dnssd_htonl(kDNSServiceFlagsMoreComing);
+ result = send_msg(req->replies);
+ if (result == t_complete)
+ {
+ fptr = req->replies;
+ req->replies = req->replies->next;
+ freeL("udsserver_idle", fptr);
+ req->time_blocked = 0; // reset failure counter after successful send
+ }
+ else if (result == t_terminated || result == t_error)
+ {
+ abort_request(req);
+ break;
+ }
+ else if (result == t_morecoming) break; // client's queues are full, move to next
+ }
+ }
+ if (result == t_morecoming)
+ {
+ if (!req->time_blocked) req->time_blocked = now;
+ debugf("udsserver_idle: client has been blocked for %ld seconds", (now - req->time_blocked) / mDNSPlatformOneSecond);
+ if (now - req->time_blocked >= MAX_TIME_BLOCKED)
+ {
+ LogMsg("Could not write data to client %d after %ld seconds - aborting connection", req->sd, MAX_TIME_BLOCKED / mDNSPlatformOneSecond);
+ LogClientInfo(req);
+ abort_request(req);
+ result = t_terminated;
+ }
+ else if (nextevent - now > mDNSPlatformOneSecond) nextevent = now + mDNSPlatformOneSecond; // try again in a second
+ }
+ if (result == t_terminated || result == t_error)
+ //since we're already doing a list traversal, we unlink the request manually instead of calling unlink_request()
+ {
+ tmp = req;
+ if (prev) prev->next = req->next;
+ if (req == all_requests) all_requests = all_requests->next;
+ req = req->next;
+ freeL("udsserver_idle", tmp);
+ }
+ else
+ {
+ prev = req;
+ req = req->next;
+ }
+ }
+ return nextevent;
+ }
+
+mDNSexport void udsserver_info(mDNS *const m)
+ {
+ mDNSs32 now = mDNS_TimeNow(m);
+ mDNSu32 CacheUsed = 0, CacheActive = 0;
+ mDNSu32 slot;
+ CacheGroup *cg;
+ CacheRecord *rr;
+ request_state *req;
+
+ LogMsgNoIdent("Timenow 0x%08lX (%ld)", (mDNSu32)now, now);
+
+ LogMsgNoIdent("Slt Q TTL U Type if len rdata");
+ 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 (rr = cg->members; rr; rr=rr->next)
+ {
+ mDNSs32 remain = rr->resrec.rroriginalttl - (now - rr->TimeRcvd) / mDNSPlatformOneSecond;
+ CacheUsed++;
+ if (rr->CRActiveQuestion) CacheActive++;
+ LogMsgNoIdent("%3d %s%6ld %s %-6s%-6s%s",
+ slot,
+ rr->CRActiveQuestion ? "*" : " ",
+ remain,
+ (rr->resrec.RecordType & kDNSRecordTypePacketUniqueMask) ? "-" : " ",
+ DNSTypeName(rr->resrec.rrtype),
+ ((NetworkInterfaceInfo *)rr->resrec.InterfaceID)->ifname,
+ CRDisplayString(m, rr));
+ usleep(1000); // Limit rate a little so we don't flood syslog too fast
+ }
+ }
+
+ 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 records; %lu referenced by active questions", CacheUsed, CacheActive);
+
+ for (req = all_requests; req; req=req->next)
+ LogClientInfo(req);
+
+ now = mDNS_TimeNow(m);
+ LogMsgNoIdent("Timenow 0x%08lX (%ld)", (mDNSu32)now, now);
+ }
+
+#if __MACOSX__ && MACOSX_MDNS_MALLOC_DEBUGGING
+mDNSexport void uds_validatelists(void)
+ {
+ request_state *req;
+ for (req = all_requests; req; req=req->next)
+ if (req->sd < 0 && req->sd != -2)
+ LogMemCorruption("UDS request list: %p is garbage (%X)", req, req->sd);
+ }
+#endif
+
+mDNSlocal void rename_service(service_instance *srv)
+ {
+ if (srv->autoname && !SameDomainLabel(srv->name.c, gmDNS->nicelabel.c))
+ {
+ srv->rename_on_memfree = 1;
+ if (mDNS_DeregisterService(gmDNS, &srv->srs)) // If service deregistered already, we can re-register immediately
+ regservice_callback(gmDNS, &srv->srs, mStatus_MemFree);
+ }
+ }
+
+mDNSexport void udsserver_handle_configchange(void)
+ {
+ request_state *req;
+
+ for (req = all_requests; req; req = req->next)
+ {
+ if (req->service_registration)
+ {
+ service_instance *ptr;
+ for (ptr = req->service_registration->instances; ptr; ptr = ptr->next)
+ rename_service(ptr);
+ }
+ }
+ }
+
+mDNSlocal void connect_callback(void *info)
+ {
+ dnssd_sock_t sd;
+ dnssd_socklen_t len;
+ unsigned long optval;
+ dnssd_sockaddr_t cliaddr;
+ request_state *rstate;
+ (void)info; // Unused
+
+ len = (dnssd_socklen_t) sizeof(cliaddr);
+
+ sd = accept(listenfd, (struct sockaddr*) &cliaddr, &len);
+
+ if (sd == dnssd_InvalidSocket)
+ {
+ if (dnssd_errno() == dnssd_EWOULDBLOCK) return;
+ my_perror("ERROR: accept");
+ return;
+ }
+ optval = 1;
+
+#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)
+ {
+ my_perror("ERROR: setsockopt - SO_NOSIGPIPE - aborting client");
+ dnssd_close(sd);
+ return;
+ }
+#endif
+
+#if defined(_WIN32)
+ if (ioctlsocket(sd, FIONBIO, &optval) != 0)
+#else
+ if (fcntl(sd, F_SETFL, O_NONBLOCK) != 0)
+#endif
+ {
+ my_perror("ERROR: fcntl(sd, F_SETFL, O_NONBLOCK) - aborting client");
+ dnssd_close(sd);
+ return;
+ }
+
+ // allocate a request_state struct that will live with the socket
+ rstate = mallocL("connect_callback", sizeof(request_state));
+ if (!rstate) FatalError("ERROR: malloc");
+ bzero(rstate, sizeof(request_state));
+ rstate->ts = t_morecoming;
+ rstate->sd = sd;
+
+ LogOperation("%3d: Adding FD", rstate->sd);
+ if ( mStatus_NoError != udsSupportAddFDToEventLoop( sd, request_callback, rstate))
+ return;
+ rstate->next = all_requests;
+ all_requests = rstate;
+ }
+
+// handler
+mDNSlocal void request_callback(void *info)
+ {
+ request_state *rstate = info;
+ transfer_state result;
+ dnssd_sockaddr_t cliaddr;
+ int dedicated_error_socket;
+#if defined(_WIN32)
+ u_long opt = 1;
+#endif
+
+ result = read_msg(rstate);
+ if (result == t_morecoming)
+ {
+ return;
+ }
+ if (result == t_terminated)
+ {
+ abort_request(rstate);
+ unlink_request(rstate);
+ return;
+ }
+ if (result == t_error)
+ {
+ abort_request(rstate);
+ unlink_request(rstate);
+ return;
+ }
+
+ if (rstate->hdr.version != VERSION)
+ {
+ LogMsg("ERROR: client incompatible with daemon (client version = %d, "
+ "daemon version = %d)\n", rstate->hdr.version, VERSION);
+ abort_request(rstate);
+ unlink_request(rstate);
+ return;
+ }
+
+ if (validate_message(rstate) < 0)
+ {
+ // note that we cannot deliver an error message if validation fails, since the path to the error socket
+ // may be contained in the (invalid) message body for some message types
+ abort_request(rstate);
+ unlink_request(rstate);
+ LogMsg("Invalid message sent by client - may indicate a malicious program running on this machine!");
+ return;
+ }
+
+ // check if client wants silent operation
+ if (rstate->hdr.flags & IPC_FLAGS_NOREPLY) rstate->no_reply = 1;
+
+ dedicated_error_socket = (rstate->hdr.op == reg_record_request || rstate->hdr.op == add_record_request ||
+ rstate->hdr.op == update_record_request || rstate->hdr.op == remove_record_request);
+
+ if (((rstate->hdr.flags & IPC_FLAGS_REUSE_SOCKET) == 0) != dedicated_error_socket)
+ LogMsg("WARNING: client request %d with incorrect flags setting 0x%X", rstate->hdr.op, rstate->hdr.flags);
+
+ // check if primary socket is to be used for synchronous errors, else open new socket
+ if (dedicated_error_socket)
+ {
+ mStatus err = 0;
+ int nwritten;
+ dnssd_sock_t errfd = socket(AF_DNSSD, SOCK_STREAM, 0);
+ if (errfd == dnssd_InvalidSocket)
+ {
+ my_perror("ERROR: socket");
+ abort_request(rstate);
+ unlink_request(rstate);
+ return;
+ }
+
+ //LogOperation("request_callback: Opened dedicated errfd %d", errfd);
+
+ #if defined(USE_TCP_LOOPBACK)
+ {
+ mDNSOpaque16 port;
+ port.b[0] = rstate->msgdata[0];
+ port.b[1] = rstate->msgdata[1];
+ rstate->msgdata += 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(&rstate->msgdata, ctrl_path, 256); // path is first element in message buffer
+ bzero(&cliaddr, sizeof(cliaddr));
+ cliaddr.sun_family = AF_LOCAL;
+ strcpy(cliaddr.sun_path, ctrl_path);
+ }
+ #endif
+ //LogOperation("request_callback: Connecting to “%s”", cliaddr.sun_path);
+ if (connect(errfd, (struct sockaddr *)&cliaddr, sizeof(cliaddr)) < 0)
+ {
+ //LogOperation("request_callback: Couldn't connect to “%s”", cliaddr.sun_path);
+ my_perror("ERROR: connect");
+ abort_request(rstate);
+ unlink_request(rstate);
+ return;
+ }
+#if defined(_WIN32)
+ if (ioctlsocket(errfd, FIONBIO, &opt) != 0)
+#else
+ if (fcntl(errfd, F_SETFL, O_NONBLOCK) != 0)
+#endif
+ {
+ my_perror("ERROR: could not set control socket to non-blocking mode");
+ abort_request(rstate);
+ unlink_request(rstate);
+ return;
+ }
+
+ switch(rstate->hdr.op)
+ {
+ case reg_record_request: err = handle_regrecord_request (rstate); break;
+ case add_record_request: err = handle_add_request (rstate); break;
+ case update_record_request: err = handle_update_request (rstate); break;
+ case remove_record_request: err = handle_removerecord_request(rstate); break;
+ default: LogMsg("%3d: ERROR: udsserver_recv_request - unsupported request type: %d", rstate->sd, rstate->hdr.op);
+ }
+
+ //LogOperation("request_callback: Returning error code %d on socket %d", err, errfd);
+ err = dnssd_htonl(err);
+ nwritten = send(errfd, (dnssd_sockbuf_t) &err, sizeof(err), 0);
+ // On a freshly-created Unix Domain Socket, the kernel should *never* fail to buffer a four-byte write for us.
+ // If not, we don't attempt to handle this failure, but we do log it.
+ if (nwritten < (int)sizeof(err))
+ LogMsg("ERROR: failed to write error response back to caller: %d %d %s",
+ nwritten, dnssd_errno(), dnssd_strerror(dnssd_errno()));
+ //else LogOperation("request_callback: Returned error code %d on socket %d", err, errfd);
+ dnssd_close(errfd);
+ //LogOperation("request_callback: Closed errfd %d", errfd);
+ reset_connected_rstate(rstate); // Reset ready to accept the next request on this pipe
+ }
+ else
+ {
+ switch(rstate->hdr.op)
+ {
+ case resolve_request: handle_resolve_request (rstate); break;
+ case query_request: handle_query_request (rstate); break;
+ case browse_request: handle_browse_request (rstate); break;
+ case reg_service_request: handle_regservice_request(rstate); break;
+ case enumeration_request: handle_enum_request (rstate); break;
+ case reconfirm_record_request: handle_reconfirm_request (rstate); break;
+ case setdomain_request: handle_setdomain_request (rstate); break;
+ default: LogMsg("%3d: ERROR: udsserver_recv_request - unsupported request type: %d", rstate->sd, rstate->hdr.op);
+ }
+ }
+ }
+
+// 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.
+
+// query and resolve calls have separate request handlers that parse the arguments from the client and
+// massage the name parameters appropriately, but the rest of the operations (making the query call,
+// delivering the result to the client, and termination) are identical.
+
+mDNSlocal void handle_query_request(request_state *rstate)
+ {
+ DNSServiceFlags flags;
+ uint32_t ifi;
+ char name[256];
+ uint16_t rrtype, rrclass;
+ char *ptr;
+ mStatus result;
+ mDNSInterfaceID InterfaceID;
+ DNSQuestion *q;
+
+ if (rstate->ts != t_complete)
+ {
+ LogMsg("ERROR: handle_query_request - transfer state != t_complete");
+ goto error;
+ }
+ ptr = rstate->msgdata;
+ if (!ptr)
+ {
+ LogMsg("ERROR: handle_query_request - NULL msgdata");
+ goto error;
+ }
+
+ flags = get_flags(&ptr);
+ ifi = get_long(&ptr);
+ if (get_string(&ptr, name, 256) < 0) goto bad_param;
+ rrtype = get_short(&ptr);
+ rrclass = get_short(&ptr);
+ InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(gmDNS, ifi);
+ if (ifi && !InterfaceID) goto bad_param;
+
+ q = mallocL("DNSQuestion", sizeof(DNSQuestion));
+ if (!q) FatalError("ERROR: handle_query - malloc");
+ bzero(q, sizeof(DNSQuestion));
+
+ q->InterfaceID = InterfaceID;
+ q->Target = zeroAddr;
+ if (!MakeDomainNameFromDNSNameString(&q->qname, name)) { freeL("DNSQuestion", q); goto bad_param; }
+ q->qtype = rrtype;
+ q->qclass = rrclass;
+ q->LongLived = (flags & kDNSServiceFlagsLongLivedQuery) != 0;
+ q->ExpectUnique = mDNSfalse;
+ q->ForceMCast = (flags & kDNSServiceFlagsForceMulticast) != 0;
+ q->ReturnCNAME = (flags & kDNSServiceFlagsReturnCNAME) != 0;
+ q->QuestionCallback = question_result_callback;
+ q->QuestionContext = rstate;
+
+ rstate->termination_context = q;
+ rstate->terminate = question_termination_callback;
+
+ LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) START", rstate->sd, q->qname.c, DNSTypeName(q->qtype));
+ result = mDNS_StartQuery(gmDNS, q);
+ if (result != mStatus_NoError) LogMsg("ERROR: mDNS_StartQuery: %d", (int)result);
+
+ if (result) rstate->terminate = NULL;
+ if (deliver_error(rstate, result) < 0) goto error;
+ return;
+
+bad_param:
+ deliver_error(rstate, mStatus_BadParamErr);
+ rstate->terminate = NULL; // don't try to terminate insuccessful Core calls
+error:
+ abort_request(rstate);
+ unlink_request(rstate);
+ return;
+ }
+
+mDNSlocal void handle_resolve_request(request_state *rstate)
+ {
+ DNSServiceFlags flags;
+ uint32_t interfaceIndex;
+ mDNSInterfaceID InterfaceID;
+ char name[256], regtype[MAX_ESCAPED_DOMAIN_NAME], domain[MAX_ESCAPED_DOMAIN_NAME];
+ char *ptr; // message data pointer
+ domainname fqdn;
+ resolve_termination_t *term;
+ mStatus err;
+
+ if (rstate->ts != t_complete)
+ {
+ LogMsg("ERROR: handle_resolve_request - transfer state != t_complete");
+ abort_request(rstate);
+ unlink_request(rstate);
+ return;
+ }
+
+ // extract the data from the message
+ ptr = rstate->msgdata;
+ if (!ptr)
+ {
+ LogMsg("ERROR: handle_resolve_request - NULL msgdata");
+ abort_request(rstate);
+ unlink_request(rstate);
+ return;
+ }
+ flags = get_flags(&ptr);
+ interfaceIndex = get_long(&ptr);
+ InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(gmDNS, interfaceIndex);
+ if (interfaceIndex && !InterfaceID)
+ { LogMsg("ERROR: handle_resolve_request - Couldn't find InterfaceID for interfaceIndex %d", interfaceIndex); goto bad_param; }
+ if (get_string(&ptr, name, 256) < 0 ||
+ get_string(&ptr, regtype, MAX_ESCAPED_DOMAIN_NAME) < 0 ||
+ get_string(&ptr, domain, MAX_ESCAPED_DOMAIN_NAME) < 0)
+ { LogMsg("ERROR: handle_resolve_request - Couldn't read name/regtype/domain"); goto bad_param; }
+
+ // free memory in rstate since we don't need it anymore
+ freeL("handle_resolve_request", rstate->msgbuf);
+ rstate->msgbuf = NULL;
+
+ if (build_domainname_from_strings(&fqdn, name, regtype, domain) < 0)
+ { LogMsg("ERROR: handle_resolve_request - Couldn't build_domainname_from_strings “%s” “%s” “%s”", name, regtype, domain); goto bad_param; }
+
+ // set up termination info
+ term = mallocL("handle_resolve_request", sizeof(resolve_termination_t));
+ bzero(term, sizeof(*term));
+ if (!term) FatalError("ERROR: malloc");
+
+ // format questions
+ term->qsrv.InterfaceID = InterfaceID;
+ term->qsrv.Target = zeroAddr;
+ memcpy(&term->qsrv.qname, &fqdn, MAX_DOMAIN_NAME);
+ term->qsrv.qtype = kDNSType_SRV;
+ term->qsrv.qclass = kDNSClass_IN;
+ term->qsrv.LongLived = mDNSfalse;
+ term->qsrv.ExpectUnique = mDNStrue;
+ term->qsrv.ForceMCast = mDNSfalse;
+ term->qsrv.QuestionCallback = resolve_result_callback;
+ term->qsrv.QuestionContext = rstate;
+
+ term->qtxt.InterfaceID = InterfaceID;
+ term->qtxt.Target = zeroAddr;
+ memcpy(&term->qtxt.qname, &fqdn, MAX_DOMAIN_NAME);
+ term->qtxt.qtype = kDNSType_TXT;
+ term->qtxt.qclass = kDNSClass_IN;
+ term->qtxt.LongLived = mDNSfalse;
+ term->qtxt.ExpectUnique = mDNStrue;
+ term->qtxt.ForceMCast = mDNSfalse;
+ term->qtxt.QuestionCallback = resolve_result_callback;
+ term->qtxt.QuestionContext = rstate;
+
+ term->rstate = rstate;
+ rstate->termination_context = term;
+ rstate->terminate = resolve_termination_callback;
+
+ // ask the questions
+ LogOperation("%3d: DNSServiceResolve(%##s) START", rstate->sd, term->qsrv.qname.c);
+ err = mDNS_StartQuery(gmDNS, &term->qsrv);
+ if (!err) err = mDNS_StartQuery(gmDNS, &term->qtxt);
+
+ if (err)
+ {
+ freeL("handle_resolve_request", term);
+ rstate->terminate = NULL; // prevent abort_request() from invoking termination callback
+ }
+ if (deliver_error(rstate, err) < 0 || err)
+ {
+ abort_request(rstate);
+ unlink_request(rstate);
+ }
+ return;
+
+bad_param:
+ deliver_error(rstate, mStatus_BadParamErr);
+ abort_request(rstate);
+ unlink_request(rstate);
+ }
+
+mDNSlocal void resolve_termination_callback(void *context)
+ {
+ resolve_termination_t *term = context;
+ request_state *rs;
+
+ if (!term)
+ {
+ LogMsg("ERROR: resolve_termination_callback: double termination");
+ return;
+ }
+ rs = term->rstate;
+ LogOperation("%3d: DNSServiceResolve(%##s) STOP", rs->sd, term->qtxt.qname.c);
+
+ mDNS_StopQuery(gmDNS, &term->qtxt);
+ mDNS_StopQuery(gmDNS, &term->qsrv);
+
+ freeL("resolve_termination_callback", term);
+ rs->termination_context = NULL;
+ }
+
+mDNSlocal void resolve_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
+ {
+ size_t len = 0;
+ char fullname[MAX_ESCAPED_DOMAIN_NAME], target[MAX_ESCAPED_DOMAIN_NAME];
+ char *data;
+ transfer_state result;
+ reply_state *rep;
+ request_state *rs = question->QuestionContext;
+ resolve_termination_t *res = rs->termination_context;
+ (void)m; // Unused
+
+ LogOperation("%3d: DNSServiceResolve(%##s, %s) %s %s",
+ rs->sd, question->qname.c, DNSTypeName(question->qtype), AddRecord ? "ADD" : "RMV", RRDisplayString(m, answer));
+
+ // This code used to do this trick of just keeping a copy of the pointer to
+ // the answer record in the cache, but the unicast query code doesn't currently
+ // put its answer records in the cache, so for now we can't do this.
+
+ if (!AddRecord)
+ {
+ // After unicast query code is updated to store its records in the common cache, use this...
+ // if (answer->rrtype == kDNSType_SRV && res->srv == answer) res->srv = mDNSNULL;
+ // if (answer->rrtype == kDNSType_TXT && res->txt == answer) res->txt = mDNSNULL;
+ // intead of this...
+ if (answer->rrtype == kDNSType_SRV && res->srv && SameRDataBody(answer, (RDataBody *)&res->srvdata))
+ res->srv = mDNSfalse;
+ if (answer->rrtype == kDNSType_TXT && res->txt && answer->rdlength == res->txtlen && SameRDataBody(answer, (RDataBody *)&res->txtdata))
+ res->txt = mDNSfalse;
+ return;
+ }
+
+ // After unicast query code is updated to store its records in the common cache, use this...
+ // if (answer->rrtype == kDNSType_SRV) res->srv = answer;
+ // if (answer->rrtype == kDNSType_TXT) res->txt = answer;
+ // intead of this...
+ if (answer->rrtype == kDNSType_SRV)
+ {
+ res->srvdata = answer->rdata->u.srv;
+ res->srv = mDNStrue;
+ }
+ if (answer->rrtype == kDNSType_TXT)
+ {
+ if (answer->rdlength > AbsoluteMaxDNSMessageData) return;
+ res->txtlen = answer->rdlength;
+ mDNSPlatformMemCopy(answer->rdata->u.data, res->txtdata, res->txtlen);
+ res->txt = mDNStrue;
+ }
+
+ if (!res->txt || !res->srv) return; // only deliver result to client if we have both answers
+
+ ConvertDomainNameToCString(answer->name, fullname);
+ ConvertDomainNameToCString(&res->srvdata.target, target);
+
+ // calculate reply length
+ len += sizeof(DNSServiceFlags);
+ len += sizeof(uint32_t); // interface index
+ len += sizeof(DNSServiceErrorType);
+ len += strlen(fullname) + 1;
+ len += strlen(target) + 1;
+ len += 2 * sizeof(uint16_t); // port, txtLen
+ len += res->txtlen;
+
+ // allocate/init reply header
+ rep = create_reply(resolve_reply_op, len, rs);
+ rep->rhdr->flags = dnssd_htonl(0);
+ rep->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(gmDNS, answer->InterfaceID));
+ rep->rhdr->error = dnssd_htonl(kDNSServiceErr_NoError);
+
+ data = rep->sdata;
+
+ // write reply data to message
+ put_string(fullname, &data);
+ put_string(target, &data);
+ *data++ = res->srvdata.port.b[0];
+ *data++ = res->srvdata.port.b[1];
+ put_short(res->txtlen, &data);
+ put_rdata(res->txtlen, res->txtdata, &data);
+
+ result = send_msg(rep);
+ if (result == t_error || result == t_terminated)
+ {
+ abort_request(rs);
+ unlink_request(rs);
+ freeL("resolve_result_callback", rep);
+ }
+ else if (result == t_complete) freeL("resolve_result_callback", rep);
+ else append_reply(rs, rep);
+ }
+
+// what gets called when a resolve is completed and we need to send the data back to the client
+mDNSlocal void question_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
+ {
+ char *data;
+ char name[MAX_ESCAPED_DOMAIN_NAME];
+ request_state *req = question->QuestionContext;
+ reply_state *rep;
+ size_t len;
+ (void)m; // Unused
+
+ LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) RESULT %s", req->sd, question->qname.c, DNSTypeName(question->qtype), RRDisplayString(m, answer));
+ //mDNS_StopQuery(m, question);
+
+ if (answer->rdlength == 0)
+ {
+ deliver_async_error(req, query_reply_op, kDNSServiceErr_NoSuchRecord);
+ return;
+ }
+
+ // calculate reply data length
+ len = sizeof(DNSServiceFlags);
+ len += 2 * sizeof(uint32_t); // if index + ttl
+ len += sizeof(DNSServiceErrorType);
+ len += 3 * sizeof(uint16_t); // type, class, rdlen
+ len += answer->rdlength;
+ ConvertDomainNameToCString(answer->name, name);
+ len += strlen(name) + 1;
+
+ rep = create_reply(query_reply_op, len, req);
+
+ rep->rhdr->flags = dnssd_htonl(AddRecord ? kDNSServiceFlagsAdd : 0);
+ rep->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(gmDNS, answer->InterfaceID));
+ rep->rhdr->error = dnssd_htonl(kDNSServiceErr_NoError);
+
+ data = rep->sdata;
+
+ put_string(name, &data);
+ put_short(answer->rrtype, &data);
+ put_short(answer->rrclass, &data);
+ put_short(answer->rdlength, &data);
+ put_rdata(answer->rdlength, answer->rdata->u.data, &data);
+ put_long(AddRecord ? answer->rroriginalttl : 0, &data);
+
+ append_reply(req, rep);
+ return;
+ }
+
+mDNSlocal void question_termination_callback(void *context)
+ {
+ DNSQuestion *q = context;
+ LogOperation("%3d: DNSServiceQueryRecord(%##s, %s) STOP", ((request_state *)q->QuestionContext)->sd, q->qname.c, DNSTypeName(q->qtype));
+ mDNS_StopQuery(gmDNS, q); // no need to error check
+ freeL("question_termination_callback", q);
+ }
+
+// 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, mDNSNULL, mDNSNULL);
+ while (*p) p++;
+ p++;
+ if (!MakeDomainNameFromDNSNameString(st[i].resrec.name, p))
+ { freeL("ServiceSubTypes", st); return(mDNSNULL); }
+ }
+ }
+ return(st);
+ }
+
+#ifdef _HAVE_SETDOMAIN_SUPPORT_
+mDNSlocal void free_defdomain(mDNS *const m, AuthRecord *const rr, mStatus result)
+ {
+ (void)m; // unused
+ if (result == mStatus_MemFree) free(rr->RecordContext); // context is the enclosing list structure
+ }
+#endif
+
+mDNSlocal void handle_setdomain_request(request_state *request)
+ {
+ mStatus err = mStatus_NoError;
+ char *ptr;
+ char domainstr[MAX_ESCAPED_DOMAIN_NAME];
+ domainname domain;
+ DNSServiceFlags flags;
+#ifdef _HAVE_SETDOMAIN_SUPPORT_
+ struct xucred xuc;
+ socklen_t xuclen;
+#endif
+
+ if (request->ts != t_complete)
+ {
+ LogMsg("ERROR: handle_setdomain_request - transfer state != t_complete");
+ abort_request(request);
+ unlink_request(request);
+ return;
+ }
+
+ // extract flags/domain from message
+ ptr = request->msgdata;
+ flags = get_flags(&ptr);
+ if (get_string(&ptr, domainstr, MAX_ESCAPED_DOMAIN_NAME) < 0 ||
+ !MakeDomainNameFromDNSNameString(&domain, domainstr))
+ { err = mStatus_BadParamErr; goto end; }
+
+ freeL("handle_setdomain_request", request->msgbuf);
+ request->msgbuf = NULL;
+
+ debugf("%3d: DNSServiceSetDefaultDomainForUser(%##s)", request->sd, domain.c);
+
+#ifdef _HAVE_SETDOMAIN_SUPPORT_
+ // this functionality currently only used for Apple-specific configuration, so we don't burned other platforms by mandating
+ // the existence of this socket option
+ xuclen = sizeof(xuc);
+ if (getsockopt(request->sd, 0, LOCAL_PEERCRED, &xuc, &xuclen))
+ { my_perror("ERROR: getsockopt, LOCAL_PEERCRED"); err = mStatus_UnknownErr; goto end; }
+ if (xuc.cr_version != XUCRED_VERSION) { LogMsg("getsockopt, LOCAL_PEERCRED - bad version"); err = mStatus_UnknownErr; goto end; }
+ LogMsg("Default domain %s %s for UID %d", domainstr, flags & kDNSServiceFlagsAdd ? "set" : "removed", xuc.cr_uid);
+
+ if (flags & kDNSServiceFlagsAdd)
+ {
+ // register a local-only PRT record
+ default_browse_list_t *newelem = malloc(sizeof(default_browse_list_t));
+ if (!newelem) { LogMsg("ERROR: malloc"); err = mStatus_NoMemoryErr; goto end; }
+ mDNS_SetupResourceRecord(&newelem->ptr_rec, mDNSNULL, mDNSInterface_LocalOnly, kDNSType_PTR, 7200, kDNSRecordTypeShared, free_defdomain, newelem);
+ MakeDomainNameFromDNSNameString(&newelem->ptr_rec.resrec.name, mDNS_DomainTypeNames[mDNS_DomainTypeBrowseDefault]);
+ AppendDNSNameString (&newelem->ptr_rec.resrec.name, "local");
+ AssignDomainName(&newelem->ptr_rec.resrec.rdata->u.name, &domain);
+ newelem->uid = xuc.cr_uid;
+ err = mDNS_Register(gmDNS, &newelem->ptr_rec);
+ if (err) free(newelem);
+ else
+ {
+ // link into list
+ newelem->next = default_browse_list;
+ default_browse_list = newelem;
+ }
+
+ }
+ else
+ {
+ // remove - find in list, deregister
+ default_browse_list_t *ptr = default_browse_list, *prev = NULL;
+ while (ptr)
+ {
+ if (SameDomainName(&ptr->ptr_rec.resrec.rdata->u.name, &domain))
+ {
+ if (prev) prev->next = ptr->next;
+ else default_browse_list = ptr->next;
+ err = mDNS_Deregister(gmDNS, &ptr->ptr_rec);
+ break;
+ }
+ prev = ptr;
+ ptr = ptr->next;
+ }
+ if (!ptr) { LogMsg("Attempt to remove nonexistent domain %s for UID %d", domainstr, xuc.cr_uid); err = mStatus_Invalid; }
+ }
+#else
+ err = mStatus_NoError;
+#endif // _HAVE_SETDOMAIN_SUPPORT_
+
+ end:
+ deliver_error(request, err);
+ abort_request(request);
+ unlink_request(request);
+ }
+
+// 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(domainname *servicename, mDNSInterfaceID id, request_state *request, reply_state **rep)
+ {
+ 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(uint32_t); // 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(query_reply_op, len, request);
+ (*rep)->rhdr->flags = dnssd_htonl(0);
+ (*rep)->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(gmDNS, id));
+ (*rep)->rhdr->error = dnssd_htonl(kDNSServiceErr_NoError);
+
+ // Build reply body
+ data = (*rep)->sdata;
+ put_string(namestr, &data);
+ put_string(typestr, &data);
+ put_string(domstr, &data);
+
+ return mStatus_NoError;
+ }
+ }
+
+mDNSlocal void FoundInstance(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
+ {
+ 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) != mStatus_NoError)
+ {
+ 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;
+ }
+
+ LogOperation("%3d: DNSServiceBrowse(%##s, %s) RESULT %s %s",
+ req->sd, question->qname.c, DNSTypeName(question->qtype), AddRecord ? "Add" : "Rmv", RRDisplayString(m, answer));
+
+ if (AddRecord) rep->rhdr->flags |= dnssd_htonl(kDNSServiceFlagsAdd);
+ append_reply(req, rep);
+ }
+
+mDNSlocal mStatus add_domain_to_browser(browser_info_t *info, const domainname *d)
+ {
+ browser_t *b, *p;
+ mStatus err;
+
+ for (p = info->browsers; p; p = p->next)
+ {
+ if (SameDomainName(&p->domain, d))
+ { debugf("add_domain_to_browser - attempt to add domain %##d 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(gmDNS, &b->q, &info->regtype, d, info->interface_id, info->ForceMCast, FoundInstance, info->rstate);
+ if (err)
+ {
+ LogMsg("mDNS_StartBrowse returned %d for type %##s domain %##s", err, info->regtype.c, d->c);
+ freeL("browser_t", b);
+ }
+ else
+ {
+ b->next = info->browsers;
+ info->browsers = b;
+ }
+ return err;
+ }
+
+mDNSlocal void handle_browse_request(request_state *request)
+ {
+ DNSServiceFlags flags;
+ uint32_t interfaceIndex;
+ mDNSInterfaceID InterfaceID;
+ char regtype[MAX_ESCAPED_DOMAIN_NAME], domain[MAX_ESCAPED_DOMAIN_NAME];
+ domainname typedn, d, temp;
+ mDNSs32 NumSubTypes;
+ char *ptr;
+ mStatus err = mStatus_NoError;
+ DNameListElem *search_domain_list, *sdom;
+ browser_info_t *info = NULL;
+
+ if (request->ts != t_complete)
+ {
+ LogMsg("ERROR: handle_browse_request - transfer state != t_complete");
+ abort_request(request);
+ unlink_request(request);
+ return;
+ }
+
+ // extract data from message
+ ptr = request->msgdata;
+ flags = get_flags(&ptr);
+ interfaceIndex = get_long(&ptr);
+ if (get_string(&ptr, regtype, MAX_ESCAPED_DOMAIN_NAME) < 0 ||
+ get_string(&ptr, domain, MAX_ESCAPED_DOMAIN_NAME) < 0)
+ { err = mStatus_BadParamErr; goto error; }
+ freeL("handle_browse_request", request->msgbuf);
+ request->msgbuf = NULL;
+
+ InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(gmDNS, interfaceIndex);
+ if (interfaceIndex && !InterfaceID) { err = mStatus_BadParamErr; goto error; }
+
+#if defined(MDNS_LAZY_REGISTER_SEARCH_DOMAINS)
+ if ( !domain || ( domain[0] == '\0' ) )
+ {
+ dDNS_RegisterSearchDomains( gmDNS );
+ }
+#endif
+
+ typedn.c[0] = 0;
+ NumSubTypes = ChopSubTypes(regtype); // Note: Modifies regtype string to remove trailing subtypes
+ if (NumSubTypes < 0 || NumSubTypes > 1) { err = mStatus_BadParamErr; goto error; }
+ if (NumSubTypes == 1 && !AppendDNSNameString(&typedn, regtype + strlen(regtype) + 1))
+ { err = mStatus_BadParamErr; goto error; }
+
+ if (!regtype[0] || !AppendDNSNameString(&typedn, regtype)) { err = mStatus_BadParamErr; goto error; }
+
+ if (!MakeDomainNameFromDNSNameString(&temp, regtype)) { err = mStatus_BadParamErr; goto error; }
+ if (temp.c[0] > 15 && domain[0] == 0) strcpy(domain, "local."); // For over-long service types, we only allow domain "local"
+
+ // allocate and set up browser info
+ info = mallocL("browser_info_t", sizeof(*info));
+ if (!info) { err = mStatus_NoMemoryErr; goto error; }
+
+ request->browser_info = info;
+ info->ForceMCast = (flags & kDNSServiceFlagsForceMulticast) != 0;
+ info->interface_id = InterfaceID;
+ AssignDomainName(&info->regtype, &typedn);
+ info->rstate = request;
+ info->default_domain = !domain[0];
+ info->browsers = NULL;
+
+ // setup termination context
+ request->termination_context = info;
+ request->terminate = browse_termination_callback;
+
+ LogOperation("%3d: DNSServiceBrowse(\"%##s\", \"%s\") START", request->sd, info->regtype.c, domain);
+ if (domain[0])
+ {
+ if (!MakeDomainNameFromDNSNameString(&d, domain)) { err = mStatus_BadParamErr; goto error; }
+ err = add_domain_to_browser(info, &d);
+ }
+
+ else
+ {
+ search_domain_list = mDNSPlatformGetSearchDomainList();
+ for (sdom = search_domain_list; sdom; sdom = sdom->next)
+ {
+ err = add_domain_to_browser(info, &sdom->name);
+ if (err)
+ {
+ if (SameDomainName(&sdom->name, &localdomain)) break;
+ else err = mStatus_NoError; // suppress errors for non-local "default" domains
+ }
+
+ }
+ mDNS_FreeDNameList(search_domain_list);
+ }
+
+ deliver_error(request, err);
+ return;
+
+error:
+ if (info) freeL("browser_info_t", info);
+ if (request->termination_context) request->termination_context = NULL;
+ deliver_error(request, err);
+ abort_request(request);
+ unlink_request(request);
+ }
+
+mDNSlocal void browse_termination_callback(void *context)
+ {
+ browser_info_t *info = context;
+ browser_t *ptr;
+
+ if (!info) return;
+
+ while(info->browsers)
+ {
+ ptr = info->browsers;
+ info->browsers = ptr->next;
+ LogOperation("%3d: DNSServiceBrowse(%##s) STOP", info->rstate->sd, ptr->q.qname.c);
+ mDNS_StopBrowse(gmDNS, &ptr->q); // no need to error-check result
+ freeL("browse_termination_callback", ptr);
+ }
+
+ info->rstate->termination_context = NULL;
+ freeL("browser_info", info);
+ }
+
+mDNSexport void udsserver_default_browse_domain_changed(const domainname *d, mDNSBool add)
+ {
+ request_state *r;
+
+ for (r = all_requests; r; r = r->next)
+ {
+ browser_info_t *info = r->browser_info;
+
+ if (!info || !info->default_domain) continue;
+ if (add) add_domain_to_browser(info, d);
+ else
+ {
+ browser_t **ptr = &info->browsers;
+ while (*ptr)
+ {
+ if (SameDomainName(&(*ptr)->domain, d))
+ {
+ browser_t *remove = *ptr;
+ *ptr = (*ptr)->next;
+ if (remove->q.LongLived)
+ {
+ // Give goodbyes for known answers.
+ // Note that this a special case where we know that the QuestionCallback function is our own
+ // code (it's FoundInstance), and that callback routine doesn't ever cancel its operation, so we
+ // don't need to guard against the question being cancelled mid-loop the way the mDNSCore routines do.
+ CacheRecord *ka = remove->q.uDNS_info.knownAnswers;
+ while (ka) { remove->q.QuestionCallback(gmDNS, &remove->q, &ka->resrec, mDNSfalse); ka = ka->next; }
+ }
+ mDNS_StopBrowse(gmDNS, &remove->q);
+ freeL("browser_t", remove);
+ return;
+ }
+ ptr = &(*ptr)->next;
+ }
+ LogMsg("Requested removal of default domain %##s not in list for sd %d", d->c, r->sd);
+ }
+ }
+ }
+
+// 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;
+ ServiceRecordSet *s;
+
+ for (rr = m->ResourceRecords; rr; rr=rr->next)
+ if (rr->resrec.rrtype == kDNSType_SRV && SameDomainName(rr->resrec.name, r->name) && !SameRData(&rr->resrec, r))
+ count++;
+
+ for (rr = m->uDNS_info.RecordRegistrations; rr; rr=rr->next)
+ if (rr->uDNS_info.state != regState_Unregistered && rr->resrec.rrtype == kDNSType_SRV && SameDomainName(rr->resrec.name, r->name) && !SameRData(&rr->resrec, r))
+ count++;
+
+ for (s = m->uDNS_info.ServiceRegistrations; s; s = s->next)
+ if (s->uDNS_info.state != regState_Unregistered && SameDomainName(s->RR_SRV.resrec.name, r->name) && !SameRData(&s->RR_SRV.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 = gmDNS->ResourceRecords; rr; rr=rr->next)
+ if (rr->resrec.rrtype == kDNSType_SRV &&
+ rr->resrec.rdata->u.srv.port.NotAnInteger == port.NotAnInteger &&
+ SameDomainName(rr->resrec.name, srv))
+ count++;
+ return(count);
+ }
+
+mDNSlocal mStatus register_service_instance(request_state *request, const domainname *domain)
+ {
+ service_info *info = request->service_registration;
+ service_instance *ptr, *instance;
+ int instance_size;
+ mStatus result;
+
+ for (ptr = info->instances; ptr; ptr = ptr->next)
+ {
+ if (SameDomainName(&ptr->domain, domain))
+ { LogMsg("register_service_instance: domain %##s already registered", domain->c); return mStatus_AlreadyRegistered; }
+ }
+
+ instance_size = sizeof(*instance);
+ if (info->txtlen > sizeof(RDataBody)) instance_size += (info->txtlen - sizeof(RDataBody));
+ instance = mallocL("service_instance", instance_size);
+ if (!instance) { my_perror("ERROR: malloc"); return mStatus_NoMemoryErr; }
+
+ instance->subtypes = AllocateSubTypes(info->num_subtypes, info->type_as_string);
+ if (info->num_subtypes && !instance->subtypes)
+ { free_service_instance(instance); instance = NULL; FatalError("ERROR: malloc"); }
+ instance->request = request;
+ instance->sd = request->sd;
+ instance->autoname = info->autoname;
+ instance->autorename = info->autorename;
+ instance->allowremotequery = info->allowremotequery;
+ instance->rename_on_memfree = 0;
+ instance->name = info->name;
+ AssignDomainName(&instance->domain, domain);
+ instance->default_local = (info->default_domain && SameDomainName(domain, &localdomain));
+ result = mDNS_RegisterService(gmDNS, &instance->srs, &instance->name, &info->type, domain, info->host.c[0] ? &info->host : NULL, info->port,
+ info->txtdata, info->txtlen, instance->subtypes, info->num_subtypes, info->InterfaceID, regservice_callback, instance);
+
+ if (result) free_service_instance(instance);
+ else
+ {
+ instance->next = info->instances;
+ info->instances = instance;
+ }
+ return result;
+ }
+
+mDNSexport void udsserver_default_reg_domain_changed(const domainname *d, mDNSBool add)
+ {
+ request_state *rstate;
+ service_info *info;
+
+ LogMsg("%s registration domain %##s", add ? "Adding" : "Removing", d->c);
+ for (rstate = all_requests; rstate; rstate = rstate->next)
+ {
+ if (rstate->terminate != regservice_termination_callback) continue;
+ info = rstate->service_registration;
+ if (!info) { LogMsg("udsserver_default_reg_domain_changed - NULL service info"); continue; } // this should never happen
+ if (!info->default_domain) continue;
+
+ // valid default registration
+ if (add) register_service_instance(rstate, d);
+ else
+ {
+ // find the instance to remove
+ service_instance *si = rstate->service_registration->instances, *prev = NULL;
+ while (si)
+ {
+ if (SameDomainName(&si->domain, d))
+ {
+ mStatus err;
+ if (prev) prev->next = si->next;
+ else info->instances = si->next;
+ err = mDNS_DeregisterService(gmDNS, &si->srs);
+ if (err)
+ {
+ LogMsg("udsserver_default_reg_domain_changed - mDNS_DeregisterService err %d", err);
+ free_service_instance(si);
+ }
+ break;
+ }
+ prev = si;
+ si = si->next;
+ }
+ if (!si) debugf("udsserver_default_reg_domain_changed - domain %##s not registered", d->c); // normal if registration failed
+ }
+ }
+ }
+
+// service registration
+mDNSlocal void handle_regservice_request(request_state *request)
+ {
+ DNSServiceFlags flags;
+ uint32_t ifi;
+ char name[1024]; // 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 *ptr;
+ domainname d, srv;
+ mStatus result;
+ service_info *service = NULL;
+
+ if (request->ts != t_complete)
+ {
+ LogMsg("ERROR: handle_regservice_request - transfer state != t_complete");
+ abort_request(request);
+ unlink_request(request);
+ return;
+ }
+
+ service = mallocL("service_info", sizeof(*service));
+ if (!service) { my_perror("ERROR: malloc"); result = mStatus_NoMemoryErr; goto finish; }
+
+ service->instances = NULL;
+ service->request = request;
+ service->txtlen = 0;
+ service->txtdata = NULL;
+ request->service_registration = service;
+ request->termination_context = request->service_registration;
+ request->terminate = regservice_termination_callback;
+
+ // extract data from message
+ ptr = request->msgdata;
+ flags = get_flags(&ptr);
+ ifi = get_long(&ptr);
+ service->InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(gmDNS, ifi);
+ if (ifi && !service->InterfaceID)
+ { LogMsg("ERROR: handle_regservice_request - Couldn't find InterfaceID for interfaceIndex %d", ifi); goto bad_param; }
+ if (get_string(&ptr, name, sizeof(name)) < 0 ||
+ get_string(&ptr, service->type_as_string, MAX_ESCAPED_DOMAIN_NAME) < 0 ||
+ get_string(&ptr, domain, MAX_ESCAPED_DOMAIN_NAME) < 0 ||
+ get_string(&ptr, host, MAX_ESCAPED_DOMAIN_NAME) < 0)
+ { LogMsg("ERROR: handle_regservice_request - Couldn't read name/regtype/domain"); goto bad_param; }
+
+ service->port.b[0] = *ptr++;
+ service->port.b[1] = *ptr++;
+
+ service->txtlen = get_short(&ptr);
+ if (service->txtlen)
+ {
+ service->txtdata = mallocL("txtdata", service->txtlen);
+ if (!service->txtdata) { my_perror("ERROR: malloc"); result = mStatus_NoMemoryErr; goto finish; }
+ memcpy(service->txtdata, get_rdata(&ptr, service->txtlen), service->txtlen);
+ }
+ else service->txtdata = NULL;
+
+ // Check for sub-types after the service type
+ service->num_subtypes = ChopSubTypes(service->type_as_string); // Note: Modifies regtype string to remove trailing subtypes
+ if (service->num_subtypes < 0)
+ { LogMsg("ERROR: handle_regservice_request - ChopSubTypes failed %s", service->type_as_string); goto bad_param; }
+
+ // Don't try to construct "domainname t" until *after* ChopSubTypes has worked its magic
+ if (!*service->type_as_string || !MakeDomainNameFromDNSNameString(&service->type, service->type_as_string))
+ { LogMsg("ERROR: handle_regservice_request - service->type_as_string bad %s", service->type_as_string); goto bad_param; }
+
+ if (!name[0])
+ {
+ service->name = (gmDNS)->nicelabel;
+ service->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(&service->name, name))
+ { LogMsg("ERROR: handle_regservice_request - name bad %s", name); goto bad_param; }
+ service->autoname = mDNSfalse;
+ }
+
+ if (*domain)
+ {
+ service->default_domain = mDNSfalse;
+ if (!MakeDomainNameFromDNSNameString(&d, domain))
+ { LogMsg("ERROR: handle_regservice_request - domain bad %s", domain); goto bad_param; }
+ }
+ else
+ {
+ service->default_domain = mDNStrue;
+ MakeDomainNameFromDNSNameString(&d, "local.");
+ }
+
+ if (!ConstructServiceName(&srv, &service->name, &service->type, &d))
+ { LogMsg("ERROR: handle_regservice_request - Couldn't ConstructServiceName from, “%#s” “%##s” “%##s”", service->name.c, service->type.c, d.c); goto bad_param; }
+
+ if (!MakeDomainNameFromDNSNameString(&service->host, host))
+ { LogMsg("ERROR: handle_regservice_request - host bad %s", host); goto bad_param; }
+ service->autorename = (flags & kDNSServiceFlagsNoAutoRename ) == 0;
+ service->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 (service->port.NotAnInteger)
+ {
+ int count = CountExistingRegistrations(&srv, service->port);
+ if (count)
+ LogMsg("Client application registered %d identical instances of service %##s port %u.",
+ count+1, srv.c, mDNSVal16(service->port));
+ }
+
+ LogOperation("%3d: DNSServiceRegister(\"%s\", \"%s\", \"%s\", \"%s\", %u) START",
+ request->sd, name, service->type_as_string, domain, host, mDNSVal16(service->port));
+ result = register_service_instance(request, &d);
+
+ if (!result && !*domain)
+ {
+ DNameListElem *ptr, *def_domains = mDNSPlatformGetRegDomainList();
+ for (ptr = def_domains; ptr; ptr = ptr->next)
+ register_service_instance(request, &ptr->name);
+ // note that we don't report errors for non-local, non-explicit domains
+ mDNS_FreeDNameList(def_domains);
+ }
+
+finish:
+ deliver_error(request, result);
+ if (result != mStatus_NoError)
+ {
+ abort_request(request);
+ unlink_request(request);
+ }
+ else
+ reset_connected_rstate(request); // prepare to receive add/remove messages
+
+ return;
+
+bad_param:
+ //if (service) freeL("service_info", service); Don't think we should do this -- abort_request will free it a second time and crash
+ deliver_error(request, mStatus_BadParamErr);
+ abort_request(request);
+ unlink_request(request);
+ }
+
+// service registration callback performs three duties - frees memory for deregistered services,
+// handles name conflicts, and delivers completed registration information to the client (via
+// process_service_registraion())
+
+mDNSlocal void regservice_callback(mDNS *const m, ServiceRecordSet *const srs, mStatus result)
+ {
+ mStatus err;
+ mDNSBool SuppressError = mDNSfalse;
+ service_instance *instance = srs->ServiceContext;
+ (void)m; // Unused
+ if (!srs) { LogMsg("regservice_callback: srs is NULL %d", result); return; }
+ if (!instance) { LogMsg("regservice_callback: srs->ServiceContext is NULL %d", result); return; }
+
+ if (instance->request && instance->request->service_registration)
+ {
+ service_info *info = instance->request->service_registration;
+ if (info->default_domain && !instance->default_local) SuppressError = mDNStrue;
+ // don't send errors up to client for wide-area, empty-string registrations
+ }
+
+ if (result == mStatus_NoError)
+ LogOperation("%3d: DNSServiceRegister(%##s, %u) REGISTERED ", instance->sd, srs->RR_SRV.resrec.name->c, mDNSVal16(srs->RR_SRV.resrec.rdata->u.srv.port));
+ else if (result == mStatus_MemFree)
+ LogOperation("%3d: DNSServiceRegister(%##s, %u) DEREGISTERED", instance->sd, srs->RR_SRV.resrec.name->c, mDNSVal16(srs->RR_SRV.resrec.rdata->u.srv.port));
+ else if (result == mStatus_NameConflict)
+ LogOperation("%3d: DNSServiceRegister(%##s, %u) NAME CONFLICT", instance->sd, srs->RR_SRV.resrec.name->c, mDNSVal16(srs->RR_SRV.resrec.rdata->u.srv.port));
+ else
+ LogOperation("%3d: DNSServiceRegister(%##s, %u) CALLBACK %d", instance->sd, srs->RR_SRV.resrec.name->c, mDNSVal16(srs->RR_SRV.resrec.rdata->u.srv.port), result);
+
+ if (result == mStatus_NoError)
+ {
+ request_state *req = instance->request;
+ if (instance->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 (!req) LogMsg("ERROR: regservice_callback - null request object");
+ else
+ {
+ reply_state *rep;
+ if (GenerateNTDResponse(srs->RR_SRV.resrec.name, srs->RR_SRV.resrec.InterfaceID, req, &rep) != mStatus_NoError)
+ LogMsg("%3d: regservice_callback: %##s is not valid DNS-SD SRV name", req->sd, srs->RR_SRV.resrec.name->c);
+ else
+ {
+ transfer_state send_result = send_msg(rep);
+ if (send_result == t_error || send_result == t_terminated)
+ { abort_request(req); unlink_request(req); freeL("reply_state", rep); }
+ else if (send_result == t_complete) freeL("regservice_callback", rep);
+ else append_reply(req, rep);
+ }
+ }
+ if (instance->autoname && CountPeerRegistrations(m, srs) == 0)
+ RecordUpdatedNiceLabel(m, 0); // Successfully got new name, tell user immediately
+ return;
+ }
+ else if (result == mStatus_MemFree)
+ {
+ if (instance->rename_on_memfree)
+ {
+ instance->rename_on_memfree = 0;
+ instance->name = gmDNS->nicelabel;
+ err = mDNS_RenameAndReregisterService(gmDNS, srs, &instance->name);
+ if (err) LogMsg("ERROR: regservice_callback - RenameAndReregisterService returned %ld", err);
+ // error should never happen - safest to log and continue
+ }
+ else
+ {
+ free_service_instance(instance);
+ return;
+ }
+ }
+ else if (result == mStatus_NameConflict)
+ {
+ if (instance->autoname && CountPeerRegistrations(m, srs) == 0)
+ {
+ // On conflict for an autoname service, rename and reregister *all* autoname services
+ IncrementLabelSuffix(&m->nicelabel, mDNStrue);
+ m->MainCallback(m, mStatus_ConfigChanged);
+ }
+ else if (instance->autoname || instance->autorename)
+ {
+ mDNS_RenameAndReregisterService(gmDNS, srs, mDNSNULL);
+ return;
+ }
+ else
+ {
+ request_state *rs = instance->request;
+ if (!rs) { LogMsg("ERROR: regservice_callback: received result %ld with a NULL request pointer", result); return; }
+ free_service_instance(instance);
+ if (!SuppressError && deliver_async_error(rs, reg_service_reply_op, result) < 0)
+ {
+ abort_request(rs);
+ unlink_request(rs);
+ }
+ return;
+ }
+ }
+ else
+ {
+ request_state *rs = instance->request;
+ if (!rs) { LogMsg("ERROR: regservice_callback: received result %ld with a NULL request pointer", result); return; }
+ if (result != mStatus_NATTraversal) LogMsg("ERROR: unknown result in regservice_callback: %ld", result);
+ free_service_instance(instance);
+ if (!SuppressError && deliver_async_error(rs, reg_service_reply_op, result) < 0)
+ {
+ abort_request(rs);
+ unlink_request(rs);
+ }
+ return;
+ }
+ }
+
+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; }
+
+ debugf("%##s: MemFree", rr->resrec.name->c);
+ if (rr->resrec.rdata != &rr->rdatastorage)
+ freeL("Extra RData", rr->resrec.rdata);
+ freeL("ExtraResourceRecord", extra);
+ }
+
+mDNSlocal mStatus add_record_to_service(request_state *rstate, service_instance *instance, uint16_t rrtype, uint16_t rdlen, char *rdata, uint32_t ttl)
+ {
+ ServiceRecordSet *srs = &instance->srs;
+ ExtraResourceRecord *extra;
+ mStatus result;
+ int size;
+
+ if (rdlen > sizeof(RDataBody)) size = rdlen;
+ else size = sizeof(RDataBody);
+
+ extra = mallocL("ExtraResourceRecord", sizeof(*extra) - sizeof(RDataBody) + size);
+ if (!extra)
+ {
+ my_perror("ERROR: malloc");
+ return mStatus_NoMemoryErr;
+ }
+
+ bzero(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;
+ memcpy(&extra->r.rdatastorage.u.data, rdata, rdlen);
+
+ result = mDNS_AddRecordToService(gmDNS, srs , extra, &extra->r.rdatastorage, ttl);
+ if (result) { freeL("ExtraResourceRecord", extra); return result; }
+
+ extra->ClientID = rstate->hdr.reg_index;
+ return result;
+ }
+
+mDNSlocal mStatus handle_add_request(request_state *rstate)
+ {
+ uint32_t ttl;
+ uint16_t rrtype, rdlen;
+ char *ptr, *rdata;
+ mStatus result = mStatus_UnknownErr;
+ DNSServiceFlags flags;
+ service_info *srvinfo = rstate->service_registration;
+ service_instance *i;
+
+ if (!srvinfo) { LogMsg("handle_add_request called with NULL service_registration"); return(-1); }
+
+ ptr = rstate->msgdata;
+ flags = get_flags(&ptr);
+ rrtype = get_short(&ptr);
+ rdlen = get_short(&ptr);
+ rdata = get_rdata(&ptr, rdlen);
+ ttl = get_long(&ptr);
+
+ if (!ttl) ttl = DefaultTTLforRRType(rrtype);
+
+ LogOperation("%3d: DNSServiceAddRecord(%##s, %s)", rstate->sd,
+ (srvinfo->instances) ? srvinfo->instances->srs.RR_SRV.resrec.name->c : NULL, DNSTypeName(rrtype));
+
+ for (i = srvinfo->instances; i; i = i->next)
+ {
+ result = add_record_to_service(rstate, i, rrtype, rdlen, rdata, ttl);
+ if (result && i->default_local) break;
+ else result = mStatus_NoError; // suppress non-local default errors
+ }
+
+ return(result);
+ }
+
+mDNSlocal mStatus update_record(AuthRecord *rr, uint16_t rdlen, char *rdata, uint32_t ttl)
+ {
+ int rdsize;
+ RData *newrd;
+ mStatus result;
+
+ if (rdlen > sizeof(RDataBody)) rdsize = rdlen;
+ else rdsize = sizeof(RDataBody);
+ newrd = mallocL("handle_update_request", sizeof(RData) - sizeof(RDataBody) + rdsize);
+ if (!newrd) FatalError("ERROR: malloc");
+ newrd->MaxRDLength = (mDNSu16) rdsize;
+ memcpy(&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; }
+
+ result = mDNS_Update(gmDNS, rr, ttl, rdlen, newrd, update_callback);
+ if (result) { LogMsg("ERROR: mDNS_Update - %ld", result); freeL("handle_update_request", newrd); }
+ return result;
+ }
+
+mDNSlocal mStatus handle_update_request(request_state *rstate)
+ {
+ uint16_t rdlen;
+ char *ptr, *rdata;
+ uint32_t ttl;
+ mStatus result = mStatus_BadReferenceErr;
+ service_info *srvinfo = rstate->service_registration;
+ service_instance *i;
+ AuthRecord *rr = NULL;
+
+ // get the message data
+ ptr = rstate->msgdata;
+ get_flags(&ptr); // flags unused
+ rdlen = get_short(&ptr);
+ rdata = get_rdata(&ptr, rdlen);
+ ttl = get_long(&ptr);
+
+ if (rstate->reg_recs)
+ {
+ // update an individually registered record
+ registered_record_entry *reptr;
+ for (reptr = rstate->reg_recs; reptr; reptr = reptr->next)
+ {
+ if (reptr->key == rstate->hdr.reg_index)
+ {
+ result = update_record(reptr->rr, rdlen, rdata, ttl);
+ goto end;
+ }
+ }
+ result = mStatus_BadReferenceErr;
+ goto end;
+ }
+
+ // update a record from a service record set
+ if (!srvinfo) { result = mStatus_BadReferenceErr; goto end; }
+ for (i = srvinfo->instances; i; i = i->next)
+ {
+ if (rstate->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 == rstate->hdr.reg_index) { rr = &e->r; break; }
+ }
+
+ if (!rr) { result = mStatus_BadReferenceErr; goto end; }
+ result = update_record(rr, rdlen, rdata, ttl);
+ if (result && i->default_local) goto end;
+ else result = mStatus_NoError; // suppress non-local default errors
+ }
+
+end:
+ LogOperation("%3d: DNSServiceUpdateRecord(%##s, %s)", rstate->sd,
+ (srvinfo->instances) ? srvinfo->instances->srs.RR_SRV.resrec.name->c : NULL,
+ rr ? DNSTypeName(rr->resrec.rrtype) : "<NONE>");
+
+ return(result);
+ }
+
+mDNSlocal void update_callback(mDNS *const m, AuthRecord *const rr, RData *oldrd)
+ {
+ (void)m; // Unused
+ if (oldrd != &rr->rdatastorage) freeL("update_callback", oldrd);
+ }
+
+mDNSlocal void free_service_instance(service_instance *srv)
+ {
+ request_state *rstate = srv->request;
+ ExtraResourceRecord *e = srv->srs.Extras, *tmp;
+
+ // clear pointers from parent struct
+ if (rstate)
+ {
+ service_instance *ptr = rstate->service_registration->instances, *prev = NULL;
+ while (ptr)
+ {
+ if (ptr == srv)
+ {
+ if (prev) prev->next = ptr->next;
+ else rstate->service_registration->instances = ptr->next;
+ break;
+ }
+ prev = ptr;
+ ptr = ptr->next;
+ }
+ }
+
+ while(e)
+ {
+ e->r.RecordContext = e;
+ tmp = e;
+ e = e->next;
+ FreeExtraRR(gmDNS, &tmp->r, mStatus_MemFree);
+ }
+
+ if (srv->subtypes) { freeL("regservice_callback", srv->subtypes); srv->subtypes = NULL; }
+ freeL("regservice_callback", srv);
+ }
+
+mDNSlocal void regservice_termination_callback(void *context)
+ {
+ service_info *info = context;
+ service_instance *i, *p;
+ if (!info) { LogMsg("regservice_termination_callback context is NULL"); return; }
+ if (!info->request) { LogMsg("regservice_termination_callback info->request is NULL"); return; }
+ i = info->instances;
+ while (i)
+ {
+ p = i;
+ i = i->next;
+ p->request = NULL; // clear back pointer
+ // only safe to free memory if registration is not valid, i.e. deregister fails (which invalidates p)
+ LogOperation("%3d: DNSServiceRegister(%##s, %u) STOP", info->request->sd, p->srs.RR_SRV.resrec.name->c, mDNSVal16(p->srs.RR_SRV.resrec.rdata->u.srv.port));
+ if (mDNS_DeregisterService(gmDNS, &p->srs)) free_service_instance(p);
+ }
+ info->request->service_registration = NULL; // clear pointer from request back to info
+ if (info->txtdata) { freeL("txtdata", info->txtdata); info->txtdata = NULL; }
+ freeL("service_info", info);
+ }
+
+mDNSlocal mStatus handle_regrecord_request(request_state *rstate)
+ {
+ AuthRecord *rr;
+ registered_record_entry *re;
+ mStatus result;
+
+ if (rstate->ts != t_complete)
+ {
+ LogMsg("ERROR: handle_regrecord_request - transfer state != t_complete");
+ abort_request(rstate);
+ unlink_request(rstate);
+ return(-1);
+ }
+
+ rr = read_rr_from_ipc_msg(rstate->msgdata, 1, 1);
+ if (!rr) return(mStatus_BadParamErr);
+
+ // allocate registration entry, link into list
+ re = mallocL("handle_regrecord_request", sizeof(registered_record_entry));
+ if (!re) FatalError("ERROR: malloc");
+ re->key = rstate->hdr.reg_index;
+ re->rr = rr;
+ re->rstate = rstate;
+ re->client_context = rstate->hdr.client_context;
+ rr->RecordContext = re;
+ rr->RecordCallback = regrecord_callback;
+ re->next = rstate->reg_recs;
+ rstate->reg_recs = re;
+
+ if (!rstate->terminate)
+ {
+ rstate->terminate = connected_registration_termination;
+ rstate->termination_context = rstate;
+ }
+
+ if (rr->resrec.rroriginalttl == 0)
+ rr->resrec.rroriginalttl = DefaultTTLforRRType(rr->resrec.rrtype);
+
+ LogOperation("%3d: DNSServiceRegisterRecord %s", rstate->sd, RRDisplayString(gmDNS, &rr->resrec));
+ result = mDNS_Register(gmDNS, rr);
+ return(result);
+ }
+
+mDNSlocal void regrecord_callback(mDNS *const m, AuthRecord * rr, mStatus result)
+ {
+ registered_record_entry *re = rr->RecordContext;
+ request_state *rstate = re ? re->rstate : NULL;
+ int len;
+ reply_state *reply;
+ transfer_state ts;
+ (void)m; // Unused
+
+ if (!re)
+ {
+ // parent struct alreadt freed by termination callback
+ if (!result) LogMsg("Error: regrecord_callback: successful registration of orphaned record");
+ else
+ {
+ if (result != mStatus_MemFree) LogMsg("regrecord_callback: error %d received after parent termination", result);
+ freeL("regrecord_callback", rr);
+ }
+ return;
+ }
+
+ // format result, add to the list for the request, including the client context in the header
+ len = sizeof(DNSServiceFlags);
+ len += sizeof(uint32_t); //interfaceIndex
+ len += sizeof(DNSServiceErrorType);
+
+ reply = create_reply(reg_record_reply_op, len, rstate);
+ reply->mhdr->client_context = re->client_context;
+ reply->rhdr->flags = dnssd_htonl(0);
+ reply->rhdr->ifi = dnssd_htonl(mDNSPlatformInterfaceIndexfromInterfaceID(gmDNS, rr->resrec.InterfaceID));
+ reply->rhdr->error = dnssd_htonl(result);
+
+ if (result)
+ {
+ // unlink from list, free memory
+ registered_record_entry **ptr = &re->rstate->reg_recs;
+ while (*ptr && (*ptr) != re) ptr = &(*ptr)->next;
+ if (!*ptr) { LogMsg("regrecord_callback - record not in list!"); return; }
+ *ptr = (*ptr)->next;
+ freeL("regrecord_callback", re->rr);
+ re->rr = rr = NULL;
+ freeL("regrecord_callback", re);
+ re = NULL;
+ }
+
+ ts = send_msg(reply);
+
+ if (ts == t_error || ts == t_terminated) { abort_request(rstate); unlink_request(rstate); }
+ else if (ts == t_complete) freeL("regrecord_callback", reply);
+ else if (ts == t_morecoming) append_reply(rstate, reply); // client is blocked, link reply into list
+ }
+
+mDNSlocal void connected_registration_termination(void *context)
+ {
+ int shared;
+ registered_record_entry *fptr, *ptr = ((request_state *)context)->reg_recs;
+ while(ptr)
+ {
+ fptr = ptr;
+ ptr = ptr->next;
+ shared = fptr->rr->resrec.RecordType == kDNSRecordTypeShared;
+ fptr->rr->RecordContext = NULL;
+ mDNS_Deregister(gmDNS, fptr->rr);
+ freeL("connected_registration_termination", fptr);
+ }
+ }
+
+mDNSlocal mStatus handle_removerecord_request(request_state *rstate)
+ {
+ mStatus err = mStatus_BadReferenceErr;
+ char *ptr;
+ service_info *srvinfo = rstate->service_registration;
+
+ ptr = rstate->msgdata;
+ get_flags(&ptr); // flags unused
+
+ if (rstate->reg_recs) err = remove_record(rstate); // remove individually registered record
+ else if (!srvinfo) LogOperation("%3d: DNSServiceRemoveRecord (bad ref)", rstate->sd);
+ else
+ {
+ service_instance *i;
+ LogOperation("%3d: DNSServiceRemoveRecord(%##s)", rstate->sd,
+ (srvinfo->instances) ? srvinfo->instances->srs.RR_SRV.resrec.name->c : NULL);
+ for (i = srvinfo->instances; i; i = i->next)
+ {
+ err = remove_extra(rstate, i);
+ if (err && i->default_local) break;
+ else err = mStatus_NoError; // suppress non-local default errors
+ }
+ }
+
+ return(err);
+ }
+
+// remove a resource record registered via DNSServiceRegisterRecord()
+mDNSlocal mStatus remove_record(request_state *rstate)
+ {
+ int shared;
+ mStatus err = mStatus_UnknownErr;
+ registered_record_entry *e, **ptr = &rstate->reg_recs;
+
+ while(*ptr && (*ptr)->key != rstate->hdr.reg_index) ptr = &(*ptr)->next;
+ if (!*ptr) { LogMsg("DNSServiceRemoveRecord - bad reference"); return mStatus_BadReferenceErr; }
+ e = *ptr;
+ *ptr = e->next; // unlink
+
+ LogOperation("%3d: DNSServiceRemoveRecord(%#s)", rstate->sd, e->rr->resrec.name->c);
+ shared = e->rr->resrec.RecordType == kDNSRecordTypeShared;
+ e->rr->RecordContext = NULL;
+ err = mDNS_Deregister(gmDNS, e->rr);
+ if (err)
+ {
+ LogMsg("ERROR: remove_record, mDNS_Deregister: %ld", err);
+ freeL("remove_record", e->rr);
+ freeL("remove_record", e);
+ }
+ return err;
+ }
+
+mDNSlocal mStatus remove_extra(request_state *rstate, service_instance *serv)
+ {
+ mStatus err = mStatus_BadReferenceErr;
+ ExtraResourceRecord *ptr;
+
+ for (ptr = serv->srs.Extras; ptr; ptr = ptr->next)
+ {
+ if (ptr->ClientID == rstate->hdr.reg_index) // found match
+ return mDNS_RemoveRecordFromService(gmDNS, &serv->srs, ptr, FreeExtraRR, ptr);
+ }
+ return err;
+ }
+
+// domain enumeration
+mDNSlocal void handle_enum_request(request_state *rstate)
+ {
+ DNSServiceFlags flags;
+ uint32_t ifi;
+ mDNSInterfaceID InterfaceID;
+ char *ptr = rstate->msgdata;
+ domain_enum_t *def, *all;
+ enum_termination_t *term;
+ mStatus err;
+ int result;
+
+ if (rstate->ts != t_complete)
+ {
+ LogMsg("ERROR: handle_enum_request - transfer state != t_complete");
+ abort_request(rstate);
+ unlink_request(rstate);
+ return;
+ }
+
+ flags = get_flags(&ptr);
+ ifi = get_long(&ptr);
+ InterfaceID = mDNSPlatformInterfaceIDfromInterfaceIndex(gmDNS, ifi);
+ if (ifi && !InterfaceID)
+ {
+ deliver_error(rstate, mStatus_BadParamErr);
+ abort_request(rstate);
+ unlink_request(rstate);
+ return;
+ }
+
+ // allocate context structures
+ def = mallocL("handle_enum_request", sizeof(domain_enum_t));
+ all = mallocL("handle_enum_request", sizeof(domain_enum_t));
+ term = mallocL("handle_enum_request", sizeof(enum_termination_t));
+ if (!def || !all || !term) FatalError("ERROR: malloc");
+
+#if defined(MDNS_LAZY_REGISTER_SEARCH_DOMAINS)
+ dDNS_RegisterSearchDomains( gmDNS );
+#endif
+
+ // enumeration requires multiple questions, so we must link all the context pointers so that
+ // necessary context can be reached from the callbacks
+ def->rstate = rstate;
+ all->rstate = rstate;
+ term->def = def;
+ term->all = all;
+ term->rstate = rstate;
+ rstate->termination_context = term;
+ rstate->terminate = enum_termination_callback;
+ def->question.QuestionContext = def;
+ def->type = (flags & kDNSServiceFlagsRegistrationDomains) ?
+ mDNS_DomainTypeRegistrationDefault: mDNS_DomainTypeBrowseDefault;
+ all->question.QuestionContext = all;
+ all->type = (flags & kDNSServiceFlagsRegistrationDomains) ?
+ mDNS_DomainTypeRegistration : mDNS_DomainTypeBrowse;
+
+ // 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)", rstate->sd, flags,
+ (flags & kDNSServiceFlagsBrowseDomains ) ? "kDNSServiceFlagsBrowseDomains" :
+ (flags & kDNSServiceFlagsRegistrationDomains) ? "kDNSServiceFlagsRegistrationDomains" : "<<Unknown>>");
+ err = mDNS_GetDomains(gmDNS, &all->question, all->type, NULL, InterfaceID, enum_result_callback, all);
+ if (err == mStatus_NoError)
+ err = mDNS_GetDomains(gmDNS, &def->question, def->type, NULL, InterfaceID, enum_result_callback, def);
+ result = deliver_error(rstate, err); // send error *before* returning local domain
+
+ if (result < 0 || err)
+ {
+ abort_request(rstate);
+ unlink_request(rstate);
+ return;
+ }
+ }
+
+mDNSlocal void enum_result_callback(mDNS *const m, DNSQuestion *question, const ResourceRecord *const answer, mDNSBool AddRecord)
+ {
+ char domain[MAX_ESCAPED_DOMAIN_NAME];
+ domain_enum_t *de = question->QuestionContext;
+ DNSServiceFlags flags = 0;
+ reply_state *reply;
+ (void)m; // Unused
+
+ if (answer->rrtype != kDNSType_PTR) return;
+ if (!AddRecord && de->type != mDNS_DomainTypeBrowse) return;
+
+ if (AddRecord)
+ {
+ flags |= kDNSServiceFlagsAdd;
+ if (de->type == mDNS_DomainTypeRegistrationDefault || de->type == mDNS_DomainTypeBrowseDefault)
+ 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(de->rstate, domain, flags, kDNSServiceInterfaceIndexAny, kDNSServiceErr_NoError);
+ if (!reply)
+ {
+ LogMsg("ERROR: enum_result_callback, format_enumeration_reply");
+ return;
+ }
+ reply->next = NULL;
+ append_reply(de->rstate, reply);
+ return;
+ }
+
+mDNSlocal reply_state *format_enumeration_reply(request_state *rstate, const char *domain, DNSServiceFlags flags, uint32_t ifi, DNSServiceErrorType err)
+ {
+ size_t len;
+ reply_state *reply;
+ char *data;
+
+ len = sizeof(DNSServiceFlags);
+ len += sizeof(uint32_t);
+ len += sizeof(DNSServiceErrorType);
+ len += strlen(domain) + 1;
+
+ reply = create_reply(enumeration_reply_op, len, rstate);
+ reply->rhdr->flags = dnssd_htonl(flags);
+ reply->rhdr->ifi = dnssd_htonl(ifi);
+ reply->rhdr->error = dnssd_htonl(err);
+ data = reply->sdata;
+ put_string(domain, &data);
+ return reply;
+ }
+
+mDNSlocal void enum_termination_callback(void *context)
+ {
+ enum_termination_t *t = context;
+ mDNS *coredata = gmDNS;
+
+ mDNS_StopGetDomains(coredata, &t->all->question);
+ mDNS_StopGetDomains(coredata, &t->def->question);
+ freeL("enum_termination_callback", t->all);
+ freeL("enum_termination_callback", t->def);
+ t->rstate->termination_context = NULL;
+ freeL("enum_termination_callback", t);
+ }
+
+mDNSlocal void handle_reconfirm_request(request_state *rstate)
+ {
+ AuthRecord *rr = read_rr_from_ipc_msg(rstate->msgdata, 0, 0);
+ if (rr)
+ {
+ mStatus status = mDNS_ReconfirmByValue(gmDNS, &rr->resrec);
+ LogOperation(
+ (status == mStatus_NoError) ?
+ "%3d: DNSServiceReconfirmRecord(%s) interface %d initiated" :
+ "%3d: DNSServiceReconfirmRecord(%s) interface %d failed: %d",
+ rstate->sd, RRDisplayString(gmDNS, &rr->resrec),
+ mDNSPlatformInterfaceIndexfromInterfaceID(gmDNS, rr->resrec.InterfaceID), status);
+ status = 0; // Adding this line eliminates a build failure when building mDNSPosix on Tiger
+ }
+ abort_request(rstate);
+ unlink_request(rstate);
+ freeL("handle_reconfirm_request", rr);
+ }
+
+// setup rstate to accept new reg/dereg requests
+mDNSlocal void reset_connected_rstate(request_state *rstate)
+ {
+ rstate->ts = t_morecoming;
+ rstate->hdr_bytes = 0;
+ rstate->data_bytes = 0;
+ if (rstate->msgbuf) freeL("reset_connected_rstate", rstate->msgbuf);
+ rstate->msgbuf = NULL;
+ rstate->bufsize = 0;
+ }
+
+// returns a resource record (allocated w/ malloc) containing the data found in an IPC message
+// data must be in 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(char *msgbuf, int GetTTL, int validate_flags)
+ {
+ char *rdata, name[256];
+ AuthRecord *rr;
+ DNSServiceFlags flags;
+ uint32_t interfaceIndex;
+ uint16_t type, class, rdlen;
+ int storage_size;
+
+ flags = get_flags(&msgbuf);
+ if (validate_flags &&
+ !((flags & kDNSServiceFlagsShared) == kDNSServiceFlagsShared) &&
+ !((flags & kDNSServiceFlagsUnique) == kDNSServiceFlagsUnique))
+ {
+ LogMsg("ERROR: Bad resource record flags (must be kDNSServiceFlagsShared or kDNSServiceFlagsUnique)");
+ return NULL;
+ }
+
+ interfaceIndex = get_long(&msgbuf);
+ if (get_string(&msgbuf, name, 256) < 0)
+ {
+ LogMsg("ERROR: read_rr_from_ipc_msg - get_string");
+ return NULL;
+ }
+ type = get_short(&msgbuf);
+ class = get_short(&msgbuf);
+ rdlen = get_short(&msgbuf);
+
+ if (rdlen > sizeof(RDataBody)) storage_size = rdlen;
+ else storage_size = sizeof(RDataBody);
+
+ rr = mallocL("read_rr_from_ipc_msg", sizeof(AuthRecord) - sizeof(RDataBody) + storage_size);
+ if (!rr) FatalError("ERROR: malloc");
+ bzero(rr, sizeof(AuthRecord)); // ok if oversized rdata not zero'd
+
+ mDNS_SetupResourceRecord(rr, mDNSNULL, mDNSPlatformInterfaceIDfromInterfaceIndex(gmDNS, interfaceIndex),
+ type, 0, (mDNSu8) ((flags & kDNSServiceFlagsShared) ? kDNSRecordTypeShared : kDNSRecordTypeUnique), mDNSNULL, mDNSNULL);
+
+ if (!MakeDomainNameFromDNSNameString(rr->resrec.name, name))
+ {
+ LogMsg("ERROR: bad name: %s", name);
+ freeL("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;
+ rdata = get_rdata(&msgbuf, rdlen);
+ memcpy(rr->resrec.rdata->u.data, rdata, rdlen);
+ if (GetTTL) rr->resrec.rroriginalttl = get_long(&msgbuf);
+ 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;
+ }
+
+// append a reply to the list in a request object
+mDNSlocal void append_reply(request_state *req, reply_state *rep)
+ {
+ reply_state *ptr;
+
+ if (!req->replies) req->replies = rep;
+ else
+ {
+ ptr = req->replies;
+ while (ptr->next) ptr = ptr->next;
+ ptr->next = rep;
+ }
+ rep->next = NULL;
+ }
+
+// read_msg may be called any time when the transfer state (rs->ts) is t_morecoming.
+// returns the current state of the request (morecoming, error, complete, terminated.)
+// if there is no data on the socket, the socket will be closed and t_terminated will be returned
+mDNSlocal int read_msg(request_state *rs)
+ {
+ uint32_t nleft;
+ int nread;
+ char buf[4]; // dummy for death notification
+
+ if (rs->ts == t_terminated || rs->ts == t_error)
+ {
+ LogMsg("ERROR: read_msg called with transfer state terminated or error");
+ rs->ts = t_error;
+ return t_error;
+ }
+
+ if (rs->ts == t_complete)
+ { // this must be death or something is wrong
+ nread = recv(rs->sd, buf, 4, 0);
+ if (!nread) { rs->ts = t_terminated; return t_terminated; }
+ if (nread < 0) goto rerror;
+ LogMsg("ERROR: read data from a completed request.");
+ rs->ts = t_error;
+ return t_error;
+ }
+
+ if (rs->ts != t_morecoming)
+ {
+ LogMsg("ERROR: read_msg called with invalid transfer state (%d)", rs->ts);
+ rs->ts = t_error;
+ return t_error;
+ }
+
+ if (rs->hdr_bytes < sizeof(ipc_msg_hdr))
+ {
+ nleft = sizeof(ipc_msg_hdr) - rs->hdr_bytes;
+ nread = recv(rs->sd, (char *)&rs->hdr + rs->hdr_bytes, nleft, 0);
+ if (nread == 0) { rs->ts = t_terminated; return t_terminated; }
+ if (nread < 0) goto rerror;
+ rs->hdr_bytes += nread;
+ if (rs->hdr_bytes == sizeof(ipc_msg_hdr))
+ {
+ ConvertHeaderBytes(&rs->hdr);
+ if (rs->hdr.version != VERSION)
+ {
+ LogMsg("ERROR: read_msg - client version 0x%08X does not match daemon version 0x%08X", rs->hdr.version, VERSION);
+ rs->ts = t_error;
+ return t_error;
+ }
+ }
+ if (rs->hdr_bytes > sizeof(ipc_msg_hdr))
+ {
+ LogMsg("ERROR: read_msg - read too many header bytes");
+ rs->ts = t_error;
+ return t_error;
+ }
+ }
+
+ // only read data if header is complete
+ if (rs->hdr_bytes == sizeof(ipc_msg_hdr))
+ {
+ if (rs->hdr.datalen == 0) // ok in removerecord requests
+ {
+ rs->ts = t_complete;
+ rs->msgbuf = NULL;
+ return t_complete;
+ }
+
+ if (!rs->msgbuf) // allocate the buffer first time through
+ {
+ rs->msgbuf = mallocL("read_msg", rs->hdr.datalen + MSG_PAD_BYTES);
+ if (!rs->msgbuf)
+ {
+ my_perror("ERROR: malloc");
+ rs->ts = t_error;
+ return t_error;
+ }
+ rs->msgdata = rs->msgbuf;
+ bzero(rs->msgbuf, rs->hdr.datalen + MSG_PAD_BYTES);
+ }
+ nleft = rs->hdr.datalen - rs->data_bytes;
+ nread = recv(rs->sd, rs->msgbuf + rs->data_bytes, nleft, 0);
+ if (nread == 0) { rs->ts = t_terminated; return t_terminated; }
+ if (nread < 0) goto rerror;
+ rs->data_bytes += nread;
+ if (rs->data_bytes > rs->hdr.datalen)
+ {
+ LogMsg("ERROR: read_msg - read too many data bytes");
+ rs->ts = t_error;
+ return t_error;
+ }
+ }
+
+ if (rs->hdr_bytes == sizeof(ipc_msg_hdr) && rs->data_bytes == rs->hdr.datalen)
+ rs->ts = t_complete;
+ else rs->ts = t_morecoming;
+
+ return rs->ts;
+
+rerror:
+ if (dnssd_errno() == dnssd_EWOULDBLOCK || dnssd_errno() == dnssd_EINTR) return t_morecoming;
+ my_perror("ERROR: read_msg");
+ rs->ts = t_error;
+ return t_error;
+ }
+
+mDNSlocal int send_msg(reply_state *rs)
+ {
+ ssize_t nwriten;
+
+ if (!rs->msgbuf)
+ {
+ LogMsg("ERROR: send_msg called with NULL message buffer");
+ return t_error;
+ }
+
+ if (rs->request->no_reply) //!!!KRS this behavior should be optimized if it becomes more common
+ {
+ rs->ts = t_complete;
+ freeL("send_msg", rs->msgbuf);
+ return t_complete;
+ }
+
+ ConvertHeaderBytes(rs->mhdr);
+ nwriten = send(rs->sd, rs->msgbuf + rs->nwriten, rs->len - rs->nwriten, 0);
+ ConvertHeaderBytes(rs->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)
+ {
+ debugf("%3d: broken pipe", rs->sd);
+ rs->ts = t_terminated;
+ rs->request->ts = t_terminated;
+ return t_terminated;
+ }
+ else
+#endif
+ {
+ my_perror("ERROR: send\n");
+ rs->ts = t_error;
+ return t_error;
+ }
+ }
+ }
+ rs->nwriten += nwriten;
+
+ if (rs->nwriten == rs->len)
+ {
+ rs->ts = t_complete;
+ freeL("send_msg", rs->msgbuf);
+ }
+ return rs->ts;
+ }
+
+mDNSlocal reply_state *create_reply(reply_op_t op, size_t datalen, request_state *request)
+ {
+ reply_state *reply;
+ int totallen;
+
+ if ((unsigned)datalen < sizeof(reply_hdr))
+ {
+ LogMsg("ERROR: create_reply - data length less than lenght of required fields");
+ return NULL;
+ }
+
+ totallen = (int) (datalen + sizeof(ipc_msg_hdr));
+ reply = mallocL("create_reply", sizeof(reply_state));
+ if (!reply) FatalError("ERROR: malloc");
+ bzero(reply, sizeof(reply_state));
+ reply->ts = t_morecoming;
+ reply->sd = request->sd;
+ reply->request = request;
+ reply->len = totallen;
+ reply->msgbuf = mallocL("create_reply", totallen);
+ if (!reply->msgbuf) FatalError("ERROR: malloc");
+ bzero(reply->msgbuf, totallen);
+ reply->mhdr = (ipc_msg_hdr *)reply->msgbuf;
+ reply->rhdr = (reply_hdr *)(reply->msgbuf + sizeof(ipc_msg_hdr));
+ reply->sdata = reply->msgbuf + sizeof(ipc_msg_hdr) + sizeof(reply_hdr);
+ reply->mhdr->version = VERSION;
+ reply->mhdr->op = op;
+ reply->mhdr->datalen = totallen - sizeof(ipc_msg_hdr);
+ return reply;
+ }
+
+mDNSlocal int deliver_error(request_state *rstate, mStatus err)
+ {
+ int nwritten = -1;
+ undelivered_error_t *undeliv;
+
+ err = dnssd_htonl(err);
+ nwritten = send(rstate->sd, (dnssd_sockbuf_t) &err, sizeof(mStatus), 0);
+ if (nwritten < (int)sizeof(mStatus))
+ {
+ if (dnssd_errno() == dnssd_EINTR || dnssd_errno() == dnssd_EWOULDBLOCK)
+ nwritten = 0;
+ if (nwritten < 0)
+ {
+ my_perror("ERROR: send - unable to deliver error to client");
+ return(-1);
+ }
+ else
+ {
+ //client blocked - store result and come backr
+ undeliv = mallocL("deliver_error", sizeof(undelivered_error_t));
+ if (!undeliv) FatalError("ERROR: malloc");
+ undeliv->err = err;
+ undeliv->nwritten = nwritten;
+ undeliv->sd = rstate->sd;
+ rstate->u_err = undeliv;
+ return 0;
+ }
+ }
+ return 0;
+ }
+
+// returns 0 on success, -1 if send is incomplete, or on terminal failure (request is aborted)
+mDNSlocal transfer_state send_undelivered_error(request_state *rs)
+ {
+ int nwritten;
+
+ nwritten = send(rs->u_err->sd, (char *)(&rs->u_err->err) + rs->u_err->nwritten, sizeof(mStatus) - rs->u_err->nwritten, 0);
+ if (nwritten < 0)
+ {
+ if (dnssd_errno() == dnssd_EINTR || dnssd_errno() == dnssd_EWOULDBLOCK)
+ nwritten = 0;
+ else
+ {
+ my_perror("ERROR: send - unable to deliver error to client\n");
+ return t_error;
+ }
+ }
+ if ((unsigned int)(nwritten + rs->u_err->nwritten) >= sizeof(mStatus))
+ {
+ freeL("send_undelivered_error", rs->u_err);
+ rs->u_err = NULL;
+ return t_complete;
+ }
+ rs->u_err->nwritten += nwritten;
+ return t_morecoming;
+ }
+
+// send bogus data along with an error code to the app callback
+// returns 0 on success (linking reply into list of not fully delivered),
+// -1 on failure (request should be aborted)
+mDNSlocal int deliver_async_error(request_state *rs, reply_op_t op, mStatus err)
+ {
+ int len;
+ reply_state *reply;
+ transfer_state ts;
+
+ if (rs->no_reply) return 0;
+ len = 256; // long enough for any reply handler to read all args w/o buffer overrun
+ reply = create_reply(op, len, rs);
+ reply->rhdr->error = dnssd_htonl(err);
+ ts = send_msg(reply);
+ if (ts == t_error || ts == t_terminated)
+ {
+ freeL("deliver_async_error", reply);
+ return -1;
+ }
+ else if (ts == t_complete) freeL("deliver_async_error", reply);
+ else if (ts == t_morecoming) append_reply(rs, reply); // client is blocked, link reply into list
+ return 0;
+ }
+
+mDNSlocal void abort_request(request_state *rs)
+ {
+ reply_state *rep, *ptr;
+
+ if (rs->terminate) rs->terminate(rs->termination_context); // terminate field may not be set yet
+ if (rs->msgbuf) freeL("abort_request", rs->msgbuf);
+ LogOperation("%3d: Removing FD", rs->sd);
+ udsSupportRemoveFDFromEventLoop(rs->sd); // Note: This also closes file descriptor rs->sd for us
+
+ // 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
+ rs->sd = -2;
+
+ // free pending replies
+ rep = rs->replies;
+ while(rep)
+ {
+ if (rep->msgbuf) freeL("abort_request", rep->msgbuf);
+ ptr = rep;
+ rep = rep->next;
+ freeL("abort_request", ptr);
+ }
+
+ if (rs->u_err)
+ {
+ freeL("abort_request", rs->u_err);
+ rs->u_err = NULL;
+ }
+ }
+
+mDNSlocal void unlink_request(request_state *rs)
+ {
+ request_state *ptr;
+
+ if (rs == all_requests)
+ {
+ all_requests = all_requests->next;
+ freeL("unlink_request", rs);
+ return;
+ }
+ for(ptr = all_requests; ptr->next; ptr = ptr->next)
+ if (ptr->next == rs)
+ {
+ ptr->next = rs->next;
+ freeL("unlink_request", rs);
+ return;
+ }
+ }
+
+//hack to search-replace perror's to LogMsg's
+mDNSlocal void my_perror(char *errmsg)
+ {
+ LogMsg("%s: %s", errmsg, dnssd_strerror(dnssd_errno()));
+ }
+
+// check that the message delivered by the client is sufficiently long to extract the required data from the buffer
+// without overrunning it.
+// returns 0 on success, -1 on error.
+
+mDNSlocal int validate_message(request_state *rstate)
+ {
+ uint32_t min_size;
+
+ switch(rstate->hdr.op)
+ {
+ case resolve_request: min_size = sizeof(DNSServiceFlags) + // flags
+ sizeof(uint32_t) + // interface
+ (3 * sizeof(char)); // name, regtype, domain
+ break;
+ case query_request: min_size = sizeof(DNSServiceFlags) + // flags
+ sizeof(uint32_t) + // interface
+ sizeof(char) + // fullname
+ (2 * sizeof(uint16_t)); // type, class
+ break;
+ case browse_request: min_size = sizeof(DNSServiceFlags) + // flags
+ sizeof(uint32_t) + // interface
+ (2 * sizeof(char)); // regtype, domain
+ break;
+ case reg_service_request: min_size = sizeof(DNSServiceFlags) + // flags
+ sizeof(uint32_t) + // interface
+ (4 * sizeof(char)) + // name, type, domain, host
+ (2 * sizeof(uint16_t)); // port, textlen
+ break;
+ case enumeration_request: min_size = sizeof(DNSServiceFlags) + // flags
+ sizeof(uint32_t); // interface
+ break;
+ case reg_record_request: min_size = sizeof(DNSServiceFlags) + // flags
+ sizeof(uint32_t) + // interface
+ sizeof(char) + // fullname
+ (3 * sizeof(uint16_t)) + // type, class, rdlen
+ sizeof(uint32_t); // ttl
+ break;
+ case add_record_request: min_size = sizeof(DNSServiceFlags) + // flags
+ (2 * sizeof(uint16_t)) + // type, rdlen
+ sizeof(uint32_t); // ttl
+ break;
+ case update_record_request: min_size = sizeof(DNSServiceFlags) + // flags
+ sizeof(uint16_t) + // rdlen
+ sizeof(uint32_t); // ttl
+ break;
+ case remove_record_request: min_size = sizeof(DNSServiceFlags); // flags
+ break;
+ case reconfirm_record_request: min_size=sizeof(DNSServiceFlags) + // flags
+ sizeof(uint32_t) + // interface
+ sizeof(char) + // fullname
+ (3 * sizeof(uint16_t)); // type, class, rdlen
+ break;
+ case setdomain_request: min_size = sizeof(DNSServiceFlags) + sizeof(char); // flags + domain
+ break;
+ default:
+ LogMsg("ERROR: validate_message - unsupported request type: %d", rstate->hdr.op);
+ return -1;
+ }
+
+ return (rstate->data_bytes >= min_size ? 0 : -1);
+
+ }
+
+mDNSlocal uint32_t dnssd_htonl(uint32_t l)
+ {
+ uint32_t ret;
+ char * data;
+
+ data = (char*) &ret;
+
+ put_long(l, &data);
+
+ return ret;
+ }
+
+#if defined(_WIN32)
+
+mDNSlocal 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
diff --git a/usr/src/cmd/cmd-inet/usr.lib/mdnsd/uds_daemon.h b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/uds_daemon.h
new file mode 100644
index 0000000000..9dec0288b7
--- /dev/null
+++ b/usr/src/cmd/cmd-inet/usr.lib/mdnsd/uds_daemon.h
@@ -0,0 +1,118 @@
+/* -*- 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
+
+ Change History (most recent first):
+
+$Log: uds_daemon.h,v $
+Revision 1.15 2006/08/14 23:24:57 cheshire
+Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
+
+Revision 1.14 2005/01/27 17:48:39 cheshire
+Added comment about CFSocketInvalidate closing the underlying socket
+
+Revision 1.13 2004/12/10 05:27:26 cheshire
+<rdar://problem/3909147> Guard against multiple autoname services of the same type on the same machine
+
+Revision 1.12 2004/12/10 04:28:28 cheshire
+<rdar://problem/3914406> User not notified of name changes for services using new UDS API
+
+Revision 1.11 2004/12/06 21:15:23 ksekar
+<rdar://problem/3884386> mDNSResponder crashed in CheckServiceRegistrations
+
+Revision 1.10 2004/10/26 04:31:44 cheshire
+Rename CountSubTypes() as ChopSubTypes()
+
+Revision 1.9 2004/09/30 00:25:00 ksekar
+<rdar://problem/3695802> Dynamically update default registration domains on config change
+
+Revision 1.8 2004/09/21 21:05:11 cheshire
+Move duplicate code out of mDNSMacOSX/daemon.c and mDNSPosix/PosixDaemon.c,
+into mDNSShared/uds_daemon.c
+
+Revision 1.7 2004/09/17 01:08:55 cheshire
+Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h
+ The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces
+ declared in that file are ONLY appropriate to single-address-space embedded applications.
+ For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used.
+
+Revision 1.6 2004/08/11 01:58:49 cheshire
+Remove "mDNS *globalInstance" parameter from udsserver_init()
+
+Revision 1.5 2004/06/18 04:44:58 rpantos
+Use platform layer for socket types
+
+Revision 1.4 2004/06/12 00:51:58 cheshire
+Changes for Windows compatibility
+
+Revision 1.3 2004/01/25 00:03:21 cheshire
+Change to use mDNSVal16() instead of private PORT_AS_NUM() macro
+
+Revision 1.2 2004/01/24 08:46:26 bradley
+Added InterfaceID<->Index platform interfaces since they are now used by all platforms for the DNS-SD APIs.
+
+Revision 1.1 2003/12/08 21:11:42 rpantos;
+Changes necessary to support mDNSResponder on Linux.
+
+*/
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#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(void);
+
+// takes the next scheduled event time, does idle work, and returns the updated nextevent time
+extern mDNSs32 udsserver_idle(mDNSs32 nextevent);
+
+extern void udsserver_info(mDNS *const m); // print out info about current state
+
+extern void udsserver_handle_configchange(void);
+
+extern int udsserver_exit(void); // should be called prior to app exit
+
+extern void udsserver_default_reg_domain_changed(const domainname *d, mDNSBool add);
+extern void udsserver_default_browse_domain_changed(const domainname *d, mDNSBool add);
+
+/* Routines that uds_daemon expects to link against: */
+
+typedef void (*udsEventCallback)(void *context);
+
+extern mStatus udsSupportAddFDToEventLoop(dnssd_sock_t fd, udsEventCallback callback, void *context);
+extern mStatus udsSupportRemoveFDFromEventLoop(dnssd_sock_t fd); // Note: This also CLOSES the file descriptor as well
+
+// RecordUpdatedNiceLabel() can be a no-op on platforms that don't care about updating the machine's
+// global default service name (was OS X calls the "Computer Name") in response to name conflicts.
+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 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);
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.h b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.h
index e9274319f2..28464d5a4c 100644
--- a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.h
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop.h
@@ -204,7 +204,7 @@ extern char *getportname(int, in_port_t);
extern void interpret_arp(int, struct arphdr *, int);
extern void interpret_bparam(int, int, int, int, int, char *, int);
-extern void interpret_dns(int, int, const uchar_t *, int);
+extern void interpret_dns(int, int, const uchar_t *, int, int);
extern void interpret_mount(int, int, int, int, int, char *, int);
extern void interpret_nfs(int, int, int, int, int, char *, int);
extern void interpret_nfs3(int, int, int, int, int, char *, int);
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_dns.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_dns.c
index 736b4dd52b..309d24a429 100644
--- a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_dns.c
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_dns.c
@@ -67,7 +67,7 @@ static size_t print_domain_name(char *line, const uchar_t *header,
const uchar_t *data, const uchar_t *data_end);
void
-interpret_dns(int flags, int proto, const uchar_t *data, int len)
+interpret_dns(int flags, int proto, const uchar_t *data, int len, int port)
{
typedef HEADER dns_header;
dns_header header;
@@ -76,12 +76,25 @@ interpret_dns(int flags, int proto, const uchar_t *data, int len)
ushort_t count;
const uchar_t *rrp; /* Resource Record Pointer. */
const uchar_t *data_end;
+ const char *protostr;
+ char *protopfxstr;
+ char *protohdrstr;
if (proto == IPPROTO_TCP) {
/* not supported now */
return;
}
+ if (port == IPPORT_DOMAIN) {
+ protostr = "DNS";
+ protopfxstr = "DNS: ";
+ protohdrstr = "DNS Header";
+ } else {
+ protostr = "MDNS";
+ protopfxstr = "MDNS: ";
+ protohdrstr = "MDNS Header";
+ }
+
/* We need at least the header in order to parse a packet. */
if (sizeof (dns_header) > len) {
return;
@@ -100,7 +113,8 @@ interpret_dns(int flags, int proto, const uchar_t *data, int len)
if (flags & F_SUM) {
line = get_sum_line();
- line += sprintf(line, "DNS %c ", header.qr ? 'R' : 'C');
+ line += sprintf(line, "%s %c ",
+ protostr, header.qr ? 'R' : 'C');
if (header.qr) {
/* answer */
@@ -135,7 +149,7 @@ interpret_dns(int flags, int proto, const uchar_t *data, int len)
}
}
if (flags & F_DTAIL) {
- show_header("DNS: ", "DNS Header", sizeof (dns_header));
+ show_header(protopfxstr, protohdrstr, sizeof (dns_header));
show_space();
if (header.qr) {
/* answer */
@@ -348,6 +362,15 @@ print_question(char *line, const uchar_t *header, const uchar_t *data,
GETINT16(type, data);
GETINT16(cls, data);
+ /*
+ * Multicast DNS re-uses the top bit of the class field
+ * in the question and answer sections. Unicast DNS only
+ * uses 1 (Internet), 3 and 4. Hence it is safe. The top
+ * order bit is always cleared here to display the rrclass in case
+ * of Multicast DNS packets.
+ */
+ cls = cls & 0x7fff;
+
if (detail) {
(void) snprintf(get_line(0, 0), get_line_remain(),
DNS_INDENT "Class: %u (%s)",
@@ -470,10 +493,10 @@ print_answer(char *line, const uchar_t *header, const uchar_t *data,
*/
if ((data_end - data) <
((ptrdiff_t)(sizeof (size)
- + sizeof (xrcode)
- + sizeof (ver)
- + sizeof (cls) /* zero */
- + sizeof (rdlen)))) {
+ + sizeof (xrcode)
+ + sizeof (ver)
+ + sizeof (cls) /* zero */
+ + sizeof (rdlen)))) {
return (data_end - data_bak);
}
@@ -533,13 +556,22 @@ print_answer(char *line, const uchar_t *header, const uchar_t *data,
*/
if ((data_end - data) <
((ptrdiff_t)(sizeof (cls)
- + sizeof (ttl)
- + sizeof (rdlen)))) {
+ + sizeof (ttl)
+ + sizeof (rdlen)))) {
return (data_end - data_bak);
}
GETINT16(cls, data);
+ /*
+ * Multicast DNS re-uses the top bit of the class field
+ * in the question and answer sections. Unicast DNS only
+ * uses 1 (Internet), 3 and 4. Hence it is safe. The top
+ * order bit is always cleared here to display the rrclass in case
+ * of Multicast DNS packets.
+ */
+ cls = cls & 0x7fff;
+
if (detail) {
(void) snprintf(get_line(0, 0), get_line_remain(),
DNS_INDENT "Class: %d (%s)", cls,
diff --git a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rport.c b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rport.c
index f79202abab..83bb72f519 100644
--- a/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rport.c
+++ b/usr/src/cmd/cmd-inet/usr.sbin/snoop/snoop_rport.c
@@ -55,6 +55,7 @@ static const struct porttable pt_udp[] = {
{ IPPORT_TIMESERVER, "TIME" },
{ IPPORT_NAMESERVER, "NAME" },
{ IPPORT_DOMAIN, "DNS" },
+ { IPPORT_MDNS, "MDNS" },
{ IPPORT_BOOTPS, "BOOTPS" },
{ IPPORT_BOOTPC, "BOOTPC" },
{ IPPORT_TFTP, "TFTP" },
@@ -307,8 +308,8 @@ interpret_syslog(int flags, char dir, int port, const char *syslogstr,
priostrlen, syslogstr, bogus ? "" : " ",
facilstr, pristr);
(void) snprintf(get_line(0, 0), get_line_remain(),
- "\"%s\"",
- show_string(syslogstr, dlen, 60));
+ "\"%s\"",
+ show_string(syslogstr, dlen, 60));
show_trailer();
}
}
@@ -354,9 +355,10 @@ interpret_reserved(int flags, int proto, in_port_t src, in_port_t dst,
which = dst;
}
- if ((dst == IPPORT_DOMAIN || src == IPPORT_DOMAIN) &&
+ if ((dst == IPPORT_DOMAIN || src == IPPORT_DOMAIN ||
+ dst == IPPORT_MDNS || src == IPPORT_MDNS) &&
proto != IPPROTO_TCP) {
- interpret_dns(flags, proto, (uchar_t *)data, dlen);
+ interpret_dns(flags, proto, (uchar_t *)data, dlen, which);
return (1);
}
@@ -435,9 +437,9 @@ interpret_reserved(int flags, int proto, in_port_t src, in_port_t dst,
if (flags & F_SUM) {
(void) snprintf(get_sum_line(), MAXLINE,
- "%s %c port=%d %s",
- pn, dir, port,
- show_string(data, dlen, 20));
+ "%s %c port=%d %s",
+ pn, dir, port,
+ show_string(data, dlen, 20));
}
if (flags & F_DTAIL) {
@@ -446,8 +448,8 @@ interpret_reserved(int flags, int proto, in_port_t src, in_port_t dst,
show_header(pbuff, hbuff, dlen);
show_space();
(void) snprintf(get_line(0, 0), get_line_remain(),
- "\"%s\"",
- show_string(data, dlen, 60));
+ "\"%s\"",
+ show_string(data, dlen, 60));
show_trailer();
}
return (1);
diff --git a/usr/src/cmd/netfiles/nsswitch.dns b/usr/src/cmd/netfiles/nsswitch.dns
index c545f0b57b..ff4bafcc0c 100644
--- a/usr/src/cmd/netfiles/nsswitch.dns
+++ b/usr/src/cmd/netfiles/nsswitch.dns
@@ -18,10 +18,10 @@
#
# CDDL HEADER END
#
-# Copyright 2006 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
-# ident "%Z%%M% %I% %E% SMI"
+#ident "%Z%%M% %I% %E% SMI"
#
# /etc/nsswitch.dns:
@@ -39,12 +39,13 @@ passwd: files
group: files
# You must also set up the /etc/resolv.conf file for DNS name
-# server lookup. See resolv.conf(4).
-hosts: files dns
+# server lookup. See resolv.conf(4). For lookup via mdns
+# svc:/network/dns/multicast:default must also be enabled. See mdnsd(1M)
+hosts: files dns mdns
# Note that IPv4 addresses are searched for in all of the ipnodes databases
# before searching the hosts databases.
-ipnodes: files dns
+ipnodes: files dns mdns
networks: files
protocols: files
diff --git a/usr/src/cmd/nscd/nscd_cfgdef.h b/usr/src/cmd/nscd/nscd_cfgdef.h
index 2d0fa99926..73722ab972 100644
--- a/usr/src/cmd/nscd/nscd_cfgdef.h
+++ b/usr/src/cmd/nscd/nscd_cfgdef.h
@@ -122,23 +122,25 @@ nscd_cfg_id_t _nscd_cfg_nsw_src[] = {
{ 1, "ldap" },
{ 2, "nis" },
{ 3, "nisplus" },
- { 4, "dns" },
- { 5, "compat" },
- { 6, "user" },
+ { 4, "mdns" },
+ { 5, "dns" },
+ { 6, "compat" },
+ { 7, "user" },
{ -1, NULL }
};
/*
* name service related smf service table
* (the order of the services should match the order of the source
- * listed above, 0: files, 1: ldap, 2: nis, 3: nisplus. dns is
- * not needed)
+ * listed above, 0: files, 1: ldap, 2: nis, 3: nisplus, 4: mdns.
+ * dns is not needed)
*/
nscd_cfg_id_t _nscd_cfg_smf_services[] = {
{ 0, "svc:/system/name-service-cache:default"},
{ 1, "svc:/network/ldap/client:default" },
{ 2, "svc:/network/nis/client:default" },
{ 3, "svc:/network/rpc/bind:default" },
+ { 4, "svc:/network/dns/multicast:default" },
{ -1, NULL }
};
diff --git a/usr/src/cmd/nscd/nscd_switch.c b/usr/src/cmd/nscd/nscd_switch.c
index 365fffa084..311bc60aaa 100644
--- a/usr/src/cmd/nscd/nscd_switch.c
+++ b/usr/src/cmd/nscd/nscd_switch.c
@@ -494,80 +494,112 @@ get_gss_func(void **func_p)
return (NSCD_SUCCESS);
}
+/*
+ * get_dns_funcs returns pointers to gethostbyname functions in the
+ * dynamically loaded nss_dns & nss_mdns modules that return host
+ * lookup results along with the TTL value in the DNS resource
+ * records. The dnsi parameter indicates whether the lookup database
+ * is hosts(0) or ipnodes(1). The srcname parameter identifies the DNS
+ * module: dns/mdns and the function returns the address of the specific
+ * gethostbyname function in func_p variable.
+ */
static nscd_rc_t
-get_dns_funcs(int dnsi, void **func_p)
+get_dns_funcs(int dnsi, nss_status_t (**func_p)(), const char *srcname)
{
char *me = "get_dns_funcs";
- static void *handle = NULL;
- static mutex_t func_lock = DEFAULTMUTEX;
- void *sym;
- char *func_name[2] = { "_nss_get_dns_hosts_name",
- "_nss_get_dns_ipnodes_name" };
- static void *func[2] = {NULL, NULL};
+ int si;
+ static void *handle[2] = { NULL, NULL };
+ static mutex_t func_lock[2] = { DEFAULTMUTEX, DEFAULTMUTEX };
+ void *sym[2];
+ static void *func[2][2] = {{NULL, NULL}, {NULL, NULL}};
+ static const char *lib[2] = { "nss_dns.so.1", "nss_mdns.so.1" };
+ static const char *func_name[2][2] =
+ {{ "_nss_get_dns_hosts_name", "_nss_get_dns_ipnodes_name" },
+ { "_nss_get_mdns_hosts_name", "_nss_get_mdns_ipnodes_name" }};
+
+ /* source index: 0 = dns, 1 = mdns */
+ if (strcmp(srcname, "dns") == 0)
+ si = 0;
+ else
+ si = 1;
- if (handle != NULL && dnsi > 0 && func[dnsi] != NULL) {
- (void) memcpy(func_p, &func[dnsi], sizeof (void *));
+ /*
+ * function index (func[si][dnsi]):
+ * [0,0] = dns/hosts, [0,1] = dns/ipnodes,
+ * [1,0] = mdns/hosts, [1,1] = mdns/ipnodes
+ */
+
+ if (handle[si] != NULL && dnsi >= 0 && func[si][dnsi] != NULL) {
+ *func_p = (nss_status_t (*)()) func[si][dnsi];
return (NSCD_SUCCESS);
}
- (void) mutex_lock(&func_lock);
+ (void) mutex_lock(&func_lock[si]);
/* close the handle if requested */
if (dnsi < 0) {
- if (handle != NULL) {
- (void) dlclose(handle);
- func[0] = NULL;
- func[1] = NULL;
+ if (handle[si] != NULL) {
+ (void) dlclose(handle[si]);
+ func[si][0] = NULL;
+ func[si][1] = NULL;
}
- (void) mutex_unlock(&func_lock);
+ (void) mutex_unlock(&func_lock[si]);
return (NSCD_SUCCESS);
}
- if (handle != NULL && func[dnsi] != NULL) {
- (void) memcpy(func_p, &func[dnsi], sizeof (void *));
- (void) mutex_unlock(&func_lock);
+ if (handle[si] != NULL && func[si][dnsi] != NULL) {
+ *func_p = (nss_status_t (*)()) func[si][dnsi];
+ (void) mutex_unlock(&func_lock[si]);
return (NSCD_SUCCESS);
}
- if (handle == NULL) {
- handle = dlopen("nss_dns.so.1", RTLD_LAZY);
- if (handle == NULL) {
+ if (handle[si] == NULL) {
+ handle[si] = dlopen(lib[si], RTLD_LAZY);
+ if (handle[si] == NULL) {
_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_ERROR)
- (me, "unable to dlopen nss_dns.so.1\n");
- (void) mutex_unlock(&func_lock);
+ (me, "unable to dlopen %s\n", lib[si]);
+ (void) mutex_unlock(&func_lock[si]);
return (NSCD_CFG_DLOPEN_ERROR);
}
}
- if ((sym = dlsym(handle, func_name[dnsi])) == NULL) {
+ if ((sym[si] = dlsym(handle[si], func_name[si][dnsi])) == NULL) {
_NSCD_LOG(NSCD_LOG_SWITCH_ENGINE, NSCD_LOG_LEVEL_ERROR)
- (me, "unable to find symbol %s\n", func_name[dnsi]);
- (void) mutex_unlock(&func_lock);
+ (me, "unable to find symbol %s\n", func_name[si][dnsi]);
+ (void) mutex_unlock(&func_lock[si]);
return (NSCD_CFG_DLSYM_ERROR);
} else {
- (void) memcpy(func_p, &sym, sizeof (void *));
- (void) memcpy(&func[dnsi], &sym, sizeof (void *));
+ *func_p = (nss_status_t (*)()) sym[si];
+ func[si][dnsi] = sym[si];
}
- (void) mutex_unlock(&func_lock);
+ (void) mutex_unlock(&func_lock[si]);
return (NSCD_SUCCESS);
}
static nss_status_t
-search_dns_withttl(nscd_sw_return_t *swret, char *srcname, int dnsi)
+search_dns_withttl(nscd_sw_return_t *swret, const char *srcname, int dnsi)
{
nss_status_t (*func)();
nss_status_t res = NSS_UNAVAIL;
nscd_rc_t rc;
swret->noarg = 0;
- if (strcmp(srcname, "dns") != 0)
+ if (strcmp(srcname, "dns") != 0 && strcmp(srcname, "mdns") != 0)
return (NSS_ERROR);
- rc = get_dns_funcs(dnsi, (void **)&func);
- if (rc == NSCD_SUCCESS)
+ rc = get_dns_funcs(dnsi, &func, srcname);
+ if (rc == NSCD_SUCCESS) {
+ /*
+ * data_len in the packed buf header may be changed
+ * by the dns or mdns backend, reset it just in
+ * case
+ */
+ ((nss_pheader_t *)swret->pbuf)->data_len =
+ swret->datalen;
res = (func)(NULL, &swret->pbuf, &swret->pbufsiz);
+ }
return (res);
}
@@ -1279,6 +1311,7 @@ nss_psearch(void *buffer, size_t length)
(void) memcpy(&pbuf->nscdpriv, &swrp, sizeof (swrp));
swret.pbuf = buffer;
swret.pbufsiz = length;
+ swret.datalen = pbuf->data_len;
/*
* use the generic nscd_initf for all database lookups
diff --git a/usr/src/cmd/nscd/nscd_switch.h b/usr/src/cmd/nscd/nscd_switch.h
index add7a6a26e..3e2e1ba926 100644
--- a/usr/src/cmd/nscd/nscd_switch.h
+++ b/usr/src/cmd/nscd/nscd_switch.h
@@ -201,6 +201,7 @@ typedef struct {
int errnum; /* errno from the backend */
int noarg; /* if set, backend does not use the arg structure */
int fallback; /* if set, may need to fall back to main nscd */
+ int datalen; /* pbuf->data_len (backend may change it) */
} nscd_sw_return_t;
/*
diff --git a/usr/src/lib/Makefile b/usr/src/lib/Makefile
index 44a056019e..f509c72a1f 100644
--- a/usr/src/lib/Makefile
+++ b/usr/src/lib/Makefile
@@ -103,6 +103,7 @@ SUBDIRS += \
libdlpi \
libeti \
libcrypt \
+ libdns_sd \
libefi \
libfstyp \
libwanboot \
@@ -345,6 +346,7 @@ HDRSUBDIRS= \
libdhcpsvc \
libdhcputil \
libdisasm \
+ libdns_sd \
libdtrace \
libdtrace_jni \
libeti \
diff --git a/usr/src/lib/libdns_sd/Makefile b/usr/src/lib/libdns_sd/Makefile
new file mode 100644
index 0000000000..21a4e1aa1d
--- /dev/null
+++ b/usr/src/lib/libdns_sd/Makefile
@@ -0,0 +1,59 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+include ../Makefile.lib
+
+HDR = dns_sd.h
+HDRDIR = common
+SUBDIRS = $(MACH)
+$(BUILD64)SUBDIRS += $(MACH64)
+
+all := TARGET = all
+clean := TARGET = clean
+clobber := TARGET = clobber
+install := TARGET = install
+lint := TARGET = lint
+
+.KEEP_STATE:
+
+all install: install_h $(SUBDIRS) .WAIT java
+
+clean clobber: $(SUBDIRS)
+
+ROOTHDRDIR= $(ROOT)/usr/include
+ROOTHDRS= $(HDR:%=$(ROOTHDRDIR)/%)
+
+install_h: $(ROOTHDRS)
+
+check:
+
+lint: $(SUBDIRS)
+
+$(SUBDIRS) java: FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
+
+include ../Makefile.targ
diff --git a/usr/src/lib/libdns_sd/Makefile.com b/usr/src/lib/libdns_sd/Makefile.com
new file mode 100644
index 0000000000..4b6fe00f9b
--- /dev/null
+++ b/usr/src/lib/libdns_sd/Makefile.com
@@ -0,0 +1,51 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+
+LIBRARY = libdns_sd.a
+VERS = .1
+OBJECTS = dnssd_clientlib.o dnssd_clientstub.o dnssd_ipc.o
+
+include ../../Makefile.lib
+
+LIBS = $(DYNLIB) $(LINTLIB)
+$(LINTLIB):= SRCS = $(SRCDIR)/$(LINTSRC)
+
+SRCDIR = ../common
+
+LDLIBS += -lsocket -lc
+
+C99MODE = $(C99_ENABLE)
+CFLAGS += -erroff=E_ASSIGNMENT_TYPE_MISMATCH
+CPPFLAGS += -I$(SRCDIR) -DNOT_HAVE_SA_LEN
+
+.PARALLEL = $(OBJECTS)
+.KEEP_STATE:
+
+lint: lintcheck
+
+all: $(LIBS)
+
+include ../../Makefile.targ
diff --git a/usr/src/lib/libdns_sd/README b/usr/src/lib/libdns_sd/README
new file mode 100644
index 0000000000..a7deae9108
--- /dev/null
+++ b/usr/src/lib/libdns_sd/README
@@ -0,0 +1,65 @@
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+
+Multicast DNS and Service Discovery support in Solaris using the
+Apple Bonjour source code (v107.6). Apple Bonjour source can be
+downloaded from:
+ http://developer.apple.com/networking/bonjour/download/
+The following components are integrated from the Apple Bonjour
+source in Solaris:
+ libdns_sd: usr/src/lib/libdns_sd <dns_sd.h>
+ libjdns_sd: usr/src/lib/libdns_sd/java/common
+ dnssd.jar: usr/src/lib/libdns_sd/java/com (incl. examples)
+ mdnsd: usr/src/cmd/cmd-inet/usr.lib/mdnsd
+ dns-sd: usr/src/cmd/cmd-inet/usr.bin/dns-sd.c
+
+Following fixes have been made to the Apple Bonjour source
+integrated in Solaris:
+* 64-bit support by adding pad bytes in ipc_msg_hdr_struct
+* 64-bit support in libjdns_sd, dnssd.jar (JNISupport.c, DNSSD.java)
+* mdnsd switches to user 'noaccess' and not 'nobody' after init
+* Fixes to support IPv6 (mDNSPosix.c, mDNSUNP.c)
+* Fix error raised when uDNS.c is compiled with Sun Studio compiler
+* Fix in dnssd_clientstub.c to not check errno when recvmsg returns 0
+* mDNSDebug.c modified to not send msgs directly to console when
+ syslog call returns an error. Logs the messages at LOG_INFO level
+ and not LOG_ERR
+
+In addition the project introduces the following changes:
+* A new nss_mdns module is introduced to use Multicast DNS (mdns)
+ for resolving link-local hostnames and is located at:
+ usr/src/lib/nsswitch/mdns
+* snoop updated to decode mDNS packets
+* updated /etc/services to include mdns
+* <netinet/in.h> updated to include mdns
+* svc:/network/dns/multicast:default introduced to manage mDNS daemon
+* solaris.smf.manage.mdns & solaris.smf.value.mdns authorizations
+ to modify nss_mdns configuration in svc:/network/dns/multicast:default
+ Both authorizations added in network management execution profile.
+* Default nsswitch.dns includes mdns as source for hosts & ipnodes
+* nscd daemon updated to support mdns
+* SUNWdsdu and SUNWdsdr packages deliver all the new mDNS
+ service discovery components.
diff --git a/usr/src/lib/libdns_sd/amd64/Makefile b/usr/src/lib/libdns_sd/amd64/Makefile
new file mode 100644
index 0000000000..2fa1329472
--- /dev/null
+++ b/usr/src/lib/libdns_sd/amd64/Makefile
@@ -0,0 +1,30 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+include ../Makefile.com
+include ../../Makefile.lib.64
+
+CFLAGS64 += -erroff=E_ASSIGNMENT_TYPE_MISMATCH
+install: all $(ROOTLIBS64) $(ROOTLINKS64) $(ROOTLINT64)
diff --git a/usr/src/lib/libdns_sd/common/dns_sd.h b/usr/src/lib/libdns_sd/common/dns_sd.h
new file mode 100644
index 0000000000..7ffe6da198
--- /dev/null
+++ b/usr/src/lib/libdns_sd/common/dns_sd.h
@@ -0,0 +1,1725 @@
+/* -*- 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.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifndef _DNS_SD_H
+#define _DNS_SD_H
+
+#ifdef __cplusplus
+ extern "C" {
+#endif
+
+/* 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)
+typedef UINT8 uint8_t;
+typedef INT8 int8_t;
+typedef UINT16 uint16_t;
+typedef INT16 int16_t;
+typedef UINT32 uint32_t;
+typedef INT32 int32_t;
+
+/* Windows has its own differences */
+#elif defined(_WIN32)
+#include <windows.h>
+#define _UNUSED
+#define bzero(a, b) memset(a, 0, b)
+#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>
+#include <strings.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;
+
+/* General flags used in functions defined below */
+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.
+ * 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.
+ */
+
+ kDNSServiceFlagsAdd = 0x2,
+ kDNSServiceFlagsDefault = 0x4,
+ /* 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.
+ */
+
+ 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.
+ */
+
+ kDNSServiceFlagsReturnCNAME = 0x800
+ /* Flag for returning CNAME records in the DNSServiceQueryRecord call. CNAME records are
+ * normally followed without indicating to the client that there was a CNAME record.
+ */
+ };
+
+/*
+ * 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. */
+ 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 (experimentatl) */
+ kDNSServiceType_OPT = 41, /* EDNS0 option (meta-RR) */
+ 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
+ /* 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 1005
+
+/*
+ * Notes on DNS Name Escaping
+ * -- or --
+ * "Why is kDNSServiceMaxDomainName 1005, when the maximum legal domain name is 255 bytes?"
+ *
+ * All strings used in DNS-SD are UTF-8 strings.
+ * With few exceptions, most are also escaped using standard 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-14 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 255 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.
+ */
+
+#define kDNSServiceInterfaceIndexAny 0
+#define kDNSServiceInterfaceIndexLocalOnly ( (uint32_t) -1 )
+
+
+typedef uint32_t DNSServiceFlags;
+typedef int32_t DNSServiceErrorType;
+
+
+/*********************************************************************************************
+ *
+ * Unix Domain Socket access, DNSServiceRef deallocation, and data processing functions
+ *
+ *********************************************************************************************/
+
+
+/* DNSServiceRefSockFD()
+ *
+ * Access underlying Unix domain socket for an initialized DNSServiceRef.
+ * The DNS Service Discovery implmementation 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
+ * run loop source, or in a select() loop: when data is available for reading on the socket,
+ * DNSServiceProcessResult() should be called, 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. Without using these
+ * constructs, DNSServiceProcessResult() will block until the response from the daemon arrives.
+ * The client is responsible for ensuring that the data on the socket is processed in a timely
+ * fashion - the daemon may terminate its connection with a client that does not clear its
+ * socket buffer.
+ *
+ * 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 succeses (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: Currently unused, reserved for future use.
+ *
+ * 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-14 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>.
+ *
+ * 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 succeses (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,
+ 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. Pass 0 to use a 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.
+ *
+ * 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".
+ *
+ * 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 succeses (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: Currently unused, reserved for future use.
+ *
+ * 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,
+ 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: Currently ignored, reserved for future use.
+ *
+ * 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 succeses (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 */
+ );
+
+
+/*********************************************************************************************
+ *
+ * Special Purpose Calls (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
+ * DNSServiceRefDealloocate()).
+ *
+ * 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. Pass 0 to use a 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 succeses (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 */
+ );
+
+
+/* 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: The resource record's time to live, in seconds.
+ *
+ * context: The context pointer that was passed to the callout.
+ *
+ */
+
+typedef void (DNSSD_API *DNSServiceQueryRecordReply)
+ (
+ 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
+ );
+
+
+/* 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: 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 succeses (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 */
+ );
+
+
+/* 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.
+ *
+ * Parameters:
+ *
+ * flags: Currently unused, reserved for future use.
+ *
+ * interfaceIndex: If non-zero, specifies the interface of the record in question.
+ * Passing 0 causes all instances of this record to be 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
+ );
+
+
+/*********************************************************************************************
+ *
+ * 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 (1005) 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 0 on success, -1 on error.
+ *
+ */
+
+int DNSSD_API DNSServiceConstructFullName
+ (
+ char *fullName,
+ const char *service, /* may be NULL */
+ const char *regtype,
+ const char *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
+ * 8 characters or less (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);
+ * ...
+ * bcopy(val1ptr, myval1, len1);
+ * bcopy(val2ptr, myval2, 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 bcopy()
+ * 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.
+ *
+ * index: 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
+ * 8 characters or less. 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 index,
+ uint16_t keyBufLen,
+ char *key,
+ uint8_t *valueLen,
+ const void **value
+ );
+
+#ifdef __APPLE_API_PRIVATE
+
+/*
+ * Mac OS X specific functionality
+ * 3rd party clients of this API should not depend on future support or availability of this routine
+ */
+
+/* DNSServiceSetDefaultDomainForUser()
+ *
+ * Set the default domain for the caller's UID. Future browse and registration
+ * calls by this user that do not specify an explicit domain will browse and
+ * register in this wide-area domain in addition to .local. In addition, this
+ * domain will be returned as a Browse domain via domain enumeration calls.
+ *
+ *
+ * Parameters:
+ *
+ * flags: Pass kDNSServiceFlagsAdd to add a domain for a user. Call without
+ * this flag set to clear a previously added domain.
+ *
+ * domain: The domain to be used for the caller's UID.
+ *
+ * return value: Returns kDNSServiceErr_NoError on succeses, otherwise returns
+ * an error code indicating the error that occurred
+ */
+
+DNSServiceErrorType DNSSD_API DNSServiceSetDefaultDomainForUser
+ (
+ DNSServiceFlags flags,
+ const char *domain
+ );
+
+#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 DNS_SD_CompileTimeAssertionChecks
+ {
+ char assert0[(sizeof(union _TXTRecordRef_t) == 16) ? 1 : -1];
+ };
+
+#ifdef __cplusplus
+ }
+#endif
+
+#endif /* _DNS_SD_H */
diff --git a/usr/src/lib/libdns_sd/common/dnssd_clientlib.c b/usr/src/lib/libdns_sd/common/dnssd_clientlib.c
new file mode 100644
index 0000000000..b5a45c8b63
--- /dev/null
+++ b/usr/src/lib/libdns_sd/common/dnssd_clientlib.c
@@ -0,0 +1,373 @@
+/* -*- 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.
+
+ Change History (most recent first):
+
+$Log: dnssd_clientlib.c,v $
+Revision 1.11 2006/08/14 23:05:53 cheshire
+Added "tab-width" emacs header line
+
+Revision 1.10 2005/04/06 02:06:56 shersche
+Add DNSSD_API macro to TXTRecord API calls
+
+Revision 1.9 2004/10/06 02:22:19 cheshire
+Changed MacRoman copyright symbol (should have been UTF-8 in any case :-) to ASCII-compatible "(c)"
+
+Revision 1.8 2004/10/01 22:15:55 rpantos
+rdar://problem/3824265: Replace APSL in client lib with BSD license.
+
+Revision 1.7 2004/06/26 03:16:34 shersche
+clean up warning messages on Win32 platform
+
+Submitted by: herscher
+
+Revision 1.6 2004/06/12 01:09:45 cheshire
+To be callable from the broadest range of clients on Windows (e.g. Visual Basic, C#, etc.)
+API routines have to be declared as "__stdcall", instead of the C default, "__cdecl"
+
+Revision 1.5 2004/05/25 18:29:33 cheshire
+Move DNSServiceConstructFullName() from dnssd_clientstub.c to dnssd_clientlib.c,
+so that it's also accessible to dnssd_clientshim.c (single address space) clients.
+
+Revision 1.4 2004/05/25 17:08:55 cheshire
+Fix compiler warning (doesn't make sense for function return type to be const)
+
+Revision 1.3 2004/05/21 21:41:35 cheshire
+Add TXT record building and parsing APIs
+
+Revision 1.2 2004/05/20 22:22:21 cheshire
+Enable code that was bracketed by "#if 0"
+
+Revision 1.1 2004/03/12 21:30:29 cheshire
+Build a System-Context Shared Library from mDNSCore, for the benefit of developers
+like Muse Research who want to be able to use mDNS/DNS-SD from GPL-licensed code.
+
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#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)
+#endif
+
+/*********************************************************************************************
+ *
+ * Supporting Functions
+ *
+ *********************************************************************************************/
+
+#define mdnsIsDigit(X) ((X) >= '0' && (X) <= '9')
+
+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] && !strncmp(key, (char*)x+1, *keylen))
+ if (*keylen == x[0] || x[1+*keylen] == '=') return(x);
+ }
+ return(NULL);
+ }
+
+/*********************************************************************************************
+ *
+ * General Utility Functions
+ *
+ *********************************************************************************************/
+
+int DNSSD_API DNSServiceConstructFullName
+ (
+ char *fullName,
+ const char *service, /* may be NULL */
+ const char *regtype,
+ const char *domain
+ )
+ {
+ unsigned long len;
+ unsigned char c;
+ char *fn = fullName;
+ const char *s = service;
+ const char *r = regtype;
+ const char *d = domain;
+
+ if (service)
+ {
+ while(*s)
+ {
+ c = (unsigned char)*s++;
+ if (c == '.' || (c == '\\')) *fn++ = '\\'; // escape dot and backslash literals
+ else if (c <= ' ') // escape non-printable characters
+ {
+ *fn++ = '\\';
+ *fn++ = (char) ('0' + (c / 100));
+ *fn++ = (char) ('0' + (c / 10) % 10);
+ c = (unsigned char)('0' + (c % 10));
+ }
+ *fn++ = (char)c;
+ }
+ *fn++ = '.';
+ }
+
+ if (!regtype) return -1;
+ len = (unsigned long) strlen(regtype);
+ if (DomainEndsInDot(regtype)) len--;
+ if (len < 6) return -1; // regtype must be at least "x._udp" or "x._tcp"
+ if (strncmp((regtype + len - 4), "_tcp", 4) && strncmp((regtype + len - 4), "_udp", 4)) return -1;
+ while(*r) *fn++ = *r++;
+ if (!DomainEndsInDot(regtype)) *fn++ = '.';
+
+ if (!domain || !domain[0]) return -1;
+ while(*d) *fn++ = *d++;
+ if (!DomainEndsInDot(domain)) *fn++ = '.';
+ *fn = '\0';
+ return 0;
+ }
+
+/*********************************************************************************************
+ *
+ * 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 dnssd_clientlib_CompileTimeAssertionCheck
+ {
+ 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 index,
+ 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<index) { 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);
+ }
diff --git a/usr/src/lib/libdns_sd/common/dnssd_clientstub.c b/usr/src/lib/libdns_sd/common/dnssd_clientstub.c
new file mode 100644
index 0000000000..2a22a0e22f
--- /dev/null
+++ b/usr/src/lib/libdns_sd/common/dnssd_clientstub.c
@@ -0,0 +1,1249 @@
+/* -*- 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.
+
+ Change History (most recent first):
+
+$Log: dnssd_clientstub.c,v $
+Revision 1.53 2006/09/07 04:43:12 herscher
+Fix compile error on Win32 platform by moving inclusion of syslog.h
+
+Revision 1.52 2006/08/15 23:04:21 mkrochma
+<rdar://problem/4090354> Client should be able to specify service name w/o callback
+
+Revision 1.51 2006/07/24 23:45:55 cheshire
+<rdar://problem/4605276> DNSServiceReconfirmRecord() should return error code
+
+Revision 1.50 2006/06/28 08:22:27 cheshire
+<rdar://problem/4605264> dnssd_clientstub.c needs to report unlink failures in syslog
+
+Revision 1.49 2006/06/28 07:58:59 cheshire
+Minor textual tidying
+
+Revision 1.48 2005/06/30 18:01:00 shersche
+<rdar://problem/4096913> Clients shouldn't wait ten seconds to connect to mDNSResponder
+
+Revision 1.47 2005/03/31 02:19:56 cheshire
+<rdar://problem/4021486> Fix build warnings
+Reviewed by: Scott Herscher
+
+Revision 1.46 2005/03/21 00:39:31 shersche
+<rdar://problem/4021486> Fix build warnings on Win32 platform
+
+Revision 1.45 2005/02/01 01:25:06 shersche
+Define sleep() to be Sleep() for Windows compatibility
+
+Revision 1.44 2005/01/27 22:57:56 cheshire
+Fix compile errors on gcc4
+
+Revision 1.43 2005/01/27 00:02:29 cheshire
+<rdar://problem/3947461> Handle case where client runs before daemon has finished launching
+
+Revision 1.42 2005/01/11 02:01:02 shersche
+Use dnssd_close() rather than close() for Windows compatibility
+
+Revision 1.41 2004/12/23 17:34:26 ksekar
+<rdar://problem/3931319> Calls leak sockets if mDNSResponder is not running
+
+Revision 1.40 2004/11/23 03:39:47 cheshire
+Let interface name/index mapping capability live directly in JNISupport.c,
+instead of having to call through to the daemon via IPC to get this information.
+
+Revision 1.39 2004/11/12 03:22:00 rpantos
+rdar://problem/3809541 Add DNSSDMapIfIndexToName, DNSSDMapNameToIfIndex.
+
+Revision 1.38 2004/11/02 02:51:23 cheshire
+<rdar://problem/3526342> Remove overly-restrictive flag checks
+
+Revision 1.37 2004/10/14 01:43:35 cheshire
+Fix opaque port passing problem
+
+Revision 1.36 2004/10/06 02:22:19 cheshire
+Changed MacRoman copyright symbol (should have been UTF-8 in any case :-) to ASCII-compatible "(c)"
+
+Revision 1.35 2004/10/01 22:15:55 rpantos
+rdar://problem/3824265: Replace APSL in client lib with BSD license.
+
+Revision 1.34 2004/09/17 22:36:13 cheshire
+Add comment explaining that deliver_request frees the message it sends
+
+Revision 1.33 2004/09/17 01:17:31 ksekar
+Remove double-free of msg header, freed automatically by deliver_request()
+
+Revision 1.32 2004/09/17 01:08:55 cheshire
+Renamed mDNSClientAPI.h to mDNSEmbeddedAPI.h
+ The name "mDNSClientAPI.h" is misleading to new developers looking at this code. The interfaces
+ declared in that file are ONLY appropriate to single-address-space embedded applications.
+ For clients on general-purpose computers, the interfaces defined in dns_sd.h should be used.
+
+Revision 1.31 2004/09/16 23:37:19 cheshire
+Free hdr before returning
+
+Revision 1.30 2004/09/16 23:14:24 cheshire
+Changes for Windows compatibility
+
+Revision 1.29 2004/09/16 21:46:38 ksekar
+<rdar://problem/3665304> Need SPI for LoginWindow to associate a UID with a Wide Area domain
+
+Revision 1.28 2004/08/11 17:10:04 cheshire
+Fix signed/unsigned warnings
+
+Revision 1.27 2004/08/11 00:54:16 cheshire
+Change "hdr->op.request_op" to just "hdr->op"
+
+Revision 1.26 2004/07/26 06:07:27 shersche
+fix bugs when using an error socket to communicate with the daemon
+
+Revision 1.25 2004/07/26 05:54:02 shersche
+DNSServiceProcessResult() returns NoError if socket read returns EWOULDBLOCK
+
+Revision 1.24 2004/07/20 06:46:21 shersche
+<rdar://problem/3730123> fix endless loop in read_all() if recv returns 0
+Bug #: 3730123
+
+Revision 1.23 2004/06/29 00:48:38 cheshire
+Don't use "MSG_WAITALL"; it returns "Invalid argument" on some Linux versions;
+use an explicit while() loop instead.
+
+Revision 1.22 2004/06/26 03:16:34 shersche
+clean up warning messages on Win32 platform
+
+Submitted by: herscher
+
+Revision 1.21 2004/06/18 04:53:56 rpantos
+Use platform layer for socket types. Introduce USE_TCP_LOOPBACK. Remove dependency on mDNSEmbeddedAPI.h.
+
+Revision 1.20 2004/06/12 00:50:22 cheshire
+Changes for Windows compatibility
+
+Revision 1.19 2004/05/25 18:29:33 cheshire
+Move DNSServiceConstructFullName() from dnssd_clientstub.c to dnssd_clientlib.c,
+so that it's also accessible to dnssd_clientshim.c (single address space) clients.
+
+Revision 1.18 2004/05/18 23:51:27 cheshire
+Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers
+
+Revision 1.17 2004/05/06 18:42:58 ksekar
+General dns_sd.h API cleanup, including the following radars:
+<rdar://problem/3592068>: Remove flags with zero value
+<rdar://problem/3479569>: Passing in NULL causes a crash.
+
+Revision 1.16 2004/03/12 22:00:37 cheshire
+Added: #include <sys/socket.h>
+
+Revision 1.15 2004/01/20 18:36:29 ksekar
+Propagated Libinfo fix for <rdar://problem/3483971>: SU:
+DNSServiceUpdateRecord() doesn't allow you to update the TXT record
+into TOT mDNSResponder.
+
+Revision 1.14 2004/01/19 22:39:17 cheshire
+Don't use "MSG_WAITALL"; it makes send() return "Invalid argument" on Linux;
+use an explicit while() loop instead. (In any case, this should only make a difference
+with non-blocking sockets, which we don't use on the client side right now.)
+
+Revision 1.13 2004/01/19 21:46:52 cheshire
+Fix compiler warning
+
+Revision 1.12 2003/12/23 20:46:47 ksekar
+<rdar://problem/3497428>: sync dnssd files between libinfo & mDNSResponder
+
+Revision 1.11 2003/12/08 21:11:42 rpantos
+Changes necessary to support mDNSResponder on Linux.
+
+Revision 1.10 2003/10/13 23:50:53 ksekar
+Updated dns_sd clientstub files to bring copies in synch with
+top-of-tree Libinfo: A memory leak in dnssd_clientstub.c is fixed,
+and comments in dns_sd.h are improved.
+
+Revision 1.9 2003/08/15 21:30:39 cheshire
+Bring up to date with LibInfo version
+
+Revision 1.8 2003/08/13 23:54:52 ksekar
+Bringing dnssd_clientstub.c up to date with Libinfo, per radar 3376640
+
+Revision 1.7 2003/08/12 19:56:25 cheshire
+Update to APSL 2.0
+
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include <errno.h>
+#include <stdlib.h>
+
+#include "dnssd_ipc.h"
+
+#if defined(_WIN32)
+
+#include <winsock2.h>
+#include <windows.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;
+
+#else
+
+#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
+
+#define CTL_PATH_PREFIX "/tmp/dnssd_clippath."
+// error socket (if needed) is named "dnssd_clipath.[pid].xxx:n" where xxx are the
+// last 3 digits of the time (in seconds) and n is the 6-digit microsecond time
+
+// general utility functions
+typedef struct _DNSServiceRef_t
+ {
+ dnssd_sock_t sockfd; // connected socket between client and daemon
+ uint32_t op; // request_op_t or reply_op_t
+ process_reply_callback process_reply;
+ void *app_callback;
+ void *app_context;
+ uint32_t max_index; //largest assigned record index - 0 if no additl. recs registered
+ } _DNSServiceRef_t;
+
+typedef struct _DNSRecordRef_t
+ {
+ void *app_context;
+ DNSServiceRegisterRecordReply app_callback;
+ DNSRecordRef recref;
+ uint32_t record_index; // index is unique to the ServiceDiscoveryRef
+ DNSServiceRef sdr;
+ } _DNSRecordRef_t;
+
+// exported functions
+
+// write len bytes. return 0 on success, -1 on error
+static int write_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 (send(sd, buf, len, MSG_WAITALL) != len) return -1;
+ while (len)
+ {
+ ssize_t num_written = send(sd, buf, len, 0);
+ if (num_written < 0 || num_written > len) return -1;
+ buf += num_written;
+ len -= num_written;
+ }
+ return 0;
+ }
+
+// read len bytes. return 0 on success, -1 on error
+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);
+ if ((num_read == -1) && (errno == EINTR))
+ continue;
+ if ((num_read < 0) || (num_read > len)) return -1;
+ // Return error -2 when no data received and errno is not set
+ if (num_read == 0) return -2;
+ buf += num_read;
+ len -= num_read;
+ }
+ return 0;
+ }
+
+/* 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. reuse_socket should be non-zero
+ * for calls that can receive an immediate error return value on their primary socket.
+ * 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 reuse_socket)
+ {
+ char *msg = NULL;
+ ipc_msg_hdr *hdr;
+ int datalen;
+#if !defined(USE_TCP_LOOPBACK)
+ char ctrl_path[256];
+#endif
+
+ if (!reuse_socket)
+ {
+#if defined(USE_TCP_LOOPBACK)
+ *len += 2; // Allocate space for two-byte port number
+#else
+ struct timeval time;
+ if (gettimeofday(&time, NULL) < 0) return NULL;
+ sprintf(ctrl_path, "%s%d-%.3lx-%.6lu", CTL_PATH_PREFIX, (int)getpid(),
+ (unsigned long)(time.tv_sec & 0xFFF), (unsigned long)(time.tv_usec));
+ *len += strlen(ctrl_path) + 1;
+#endif
+ }
+
+ datalen = (int) *len;
+ *len += sizeof(ipc_msg_hdr);
+
+ // write message to buffer
+ msg = malloc(*len);
+ if (!msg) return NULL;
+
+ bzero(msg, *len);
+ hdr = (void *)msg;
+ hdr->datalen = datalen;
+ hdr->version = VERSION;
+ hdr->op = op;
+ if (reuse_socket) hdr->flags |= IPC_FLAGS_REUSE_SOCKET;
+ *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 (!reuse_socket) put_short(0, data_start);
+#else
+ if (!reuse_socket) put_string(ctrl_path, data_start);
+#endif
+ return hdr;
+ }
+
+ // return a connected service ref (deallocate with DNSServiceRefDeallocate)
+static DNSServiceRef connect_to_server(void)
+ {
+ dnssd_sockaddr_t saddr;
+ DNSServiceRef sdr;
+ int NumTries = 0;
+
+#if defined(_WIN32)
+ if (!g_initWinsock)
+ {
+ WSADATA wsaData;
+ DNSServiceErrorType err;
+
+ g_initWinsock = 1;
+
+ err = WSAStartup( MAKEWORD( 2, 2 ), &wsaData );
+
+ if (err != 0) return NULL;
+ }
+
+ // <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(_DNSServiceRef_t));
+ if (!sdr) return(NULL);
+ sdr->sockfd = socket(AF_DNSSD, SOCK_STREAM, 0);
+ if (sdr->sockfd == dnssd_InvalidSocket) { free(sdr); return NULL; }
+#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);
+#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);
+ sdr->sockfd = dnssd_InvalidSocket;
+ free(sdr);
+ return NULL;
+ }
+ }
+ return sdr;
+ }
+
+static DNSServiceErrorType deliver_request(void *msg, DNSServiceRef sdr, int reuse_sd)
+ {
+ ipc_msg_hdr *hdr = msg;
+ uint32_t datalen = hdr->datalen;
+ dnssd_sockaddr_t caddr, daddr; // (client and daemon address structs)
+ char *const data = (char *)msg + sizeof(ipc_msg_hdr);
+ dnssd_sock_t listenfd = dnssd_InvalidSocket, errsd = dnssd_InvalidSocket;
+ int ret;
+ dnssd_socklen_t len = (dnssd_socklen_t) sizeof(caddr);
+ DNSServiceErrorType err = kDNSServiceErr_Unknown;
+
+ if (!hdr || sdr->sockfd < 0) return kDNSServiceErr_Unknown;
+
+ if (!reuse_sd)
+ {
+ // setup temporary error socket
+ if ((listenfd = socket(AF_DNSSD, SOCK_STREAM, 0)) < 0)
+ goto cleanup;
+ bzero(&caddr, sizeof(caddr));
+
+#if defined(USE_TCP_LOOPBACK)
+ {
+ union { uint16_t s; u_char b[2]; } port;
+ caddr.sin_family = AF_INET;
+ caddr.sin_port = 0;
+ caddr.sin_addr.s_addr = inet_addr(MDNS_TCP_SERVERADDR);
+ ret = bind(listenfd, (struct sockaddr*) &caddr, sizeof(caddr));
+ if (ret < 0) goto cleanup;
+ if (getsockname(listenfd, (struct sockaddr*) &caddr, &len) < 0) goto cleanup;
+ listen(listenfd, 1);
+ 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
+ }
+#else
+ {
+ mode_t mask = umask(0);
+ 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
+ //syslog(LOG_WARNING, "deliver_request: creating UDS: %s\n", data);
+ strcpy(caddr.sun_path, data);
+ ret = bind(listenfd, (struct sockaddr *)&caddr, sizeof(caddr));
+ umask(mask);
+ if (ret < 0) goto cleanup;
+ listen(listenfd, 1);
+ }
+#endif
+ }
+
+ ConvertHeaderBytes(hdr);
+ //syslog(LOG_WARNING, "deliver_request writing %ld bytes\n", datalen + sizeof(ipc_msg_hdr));
+ //syslog(LOG_WARNING, "deliver_request name is %s\n", (char *)msg + sizeof(ipc_msg_hdr));
+ if (write_all(sdr->sockfd, msg, datalen + sizeof(ipc_msg_hdr)) < 0)
+ goto cleanup;
+ free(msg);
+ msg = NULL;
+
+ if (reuse_sd) errsd = sdr->sockfd;
+ else
+ {
+ //syslog(LOG_WARNING, "deliver_request: accept\n");
+ len = sizeof(daddr);
+ errsd = accept(listenfd, (struct sockaddr *)&daddr, &len);
+ //syslog(LOG_WARNING, "deliver_request: accept returned %d\n", errsd);
+ if (errsd < 0) goto cleanup;
+ }
+
+ if (read_all(errsd, (char*)&err, (int)sizeof(err)) < 0)
+ err = kDNSServiceErr_Unknown;
+ else
+ err = ntohl(err);
+
+ //syslog(LOG_WARNING, "deliver_request: retrieved error code %d\n", err);
+
+cleanup:
+ if (!reuse_sd)
+ {
+ if (listenfd > 0) dnssd_close(listenfd);
+ if (errsd > 0) dnssd_close(errsd);
+#if !defined(USE_TCP_LOOPBACK)
+ // syslog(LOG_WARNING, "deliver_request: removing UDS: %s\n", data);
+ if (unlink(data) != 0)
+ syslog(LOG_WARNING, "WARNING: unlink(\"%s\") failed errno %d (%s)", data, errno, strerror(errno));
+ // else syslog(LOG_WARNING, "deliver_request: removed UDS: %s\n", data);
+#endif
+ }
+ if (msg) free(msg);
+ return err;
+ }
+
+int DNSSD_API DNSServiceRefSockFD(DNSServiceRef sdRef)
+ {
+ if (!sdRef) return -1;
+ return (int) sdRef->sockfd;
+ }
+
+// 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)
+ {
+ ipc_msg_hdr hdr;
+ char *data;
+ int rderr;
+
+ if (!sdRef || sdRef->sockfd < 0 || !sdRef->process_reply)
+ return kDNSServiceErr_BadReference;
+
+ rderr = read_all(sdRef->sockfd, (void *)&hdr, sizeof(hdr));
+ if (rderr < 0) {
+ // 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. Can check errno when error
+ // code returned is -1
+ if ((rderr == -1) && (dnssd_errno() == dnssd_EWOULDBLOCK))
+ return kDNSServiceErr_NoError;
+ return kDNSServiceErr_Unknown;
+ }
+ ConvertHeaderBytes(&hdr);
+ if (hdr.version != VERSION)
+ return kDNSServiceErr_Incompatible;
+ data = malloc(hdr.datalen);
+ if (!data) return kDNSServiceErr_NoMemory;
+ if (read_all(sdRef->sockfd, data, hdr.datalen) < 0)
+ return kDNSServiceErr_Unknown;
+ sdRef->process_reply(sdRef, &hdr, data);
+ free(data);
+ return kDNSServiceErr_NoError;
+ }
+
+void DNSSD_API DNSServiceRefDeallocate(DNSServiceRef sdRef)
+ {
+ if (!sdRef) return;
+ if (sdRef->sockfd > 0) dnssd_close(sdRef->sockfd);
+ free(sdRef);
+ }
+
+static void handle_resolve_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data)
+ {
+ DNSServiceFlags flags;
+ char fullname[kDNSServiceMaxDomainName];
+ char target[kDNSServiceMaxDomainName];
+ uint16_t txtlen;
+ union { uint16_t s; u_char b[2]; } port;
+ uint32_t ifi;
+ DNSServiceErrorType err;
+ unsigned char *txtrecord;
+ int str_error = 0;
+ (void)hdr; //unused
+
+ flags = get_flags(&data);
+ ifi = get_long(&data);
+ err = get_error_code(&data);
+ if (get_string(&data, fullname, kDNSServiceMaxDomainName) < 0) str_error = 1;
+ if (get_string(&data, target, kDNSServiceMaxDomainName) < 0) str_error = 1;
+ port.b[0] = *data++;
+ port.b[1] = *data++;
+ txtlen = get_short(&data);
+ txtrecord = (unsigned char *)get_rdata(&data, txtlen);
+
+ if (!err && str_error) err = kDNSServiceErr_Unknown;
+ ((DNSServiceResolveReply)sdr->app_callback)(sdr, flags, ifi, err, fullname, target, port.s, txtlen, txtrecord, sdr->app_context);
+ }
+
+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 *msg = NULL, *ptr;
+ size_t len;
+ ipc_msg_hdr *hdr;
+ DNSServiceRef sdr;
+ DNSServiceErrorType err;
+
+ if (!sdRef) return kDNSServiceErr_BadParam;
+ *sdRef = NULL;
+
+ if (!name || !regtype || !domain || !callBack) return kDNSServiceErr_BadParam;
+
+ // 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, 1);
+ if (!hdr) goto error;
+ msg = (void *)hdr;
+
+ put_flags(flags, &ptr);
+ put_long(interfaceIndex, &ptr);
+ put_string(name, &ptr);
+ put_string(regtype, &ptr);
+ put_string(domain, &ptr);
+
+ sdr = connect_to_server();
+ if (!sdr) goto error;
+ err = deliver_request(msg, sdr, 1);
+ if (err)
+ {
+ DNSServiceRefDeallocate(sdr);
+ return err;
+ }
+ sdr->op = resolve_request;
+ sdr->process_reply = handle_resolve_response;
+ sdr->app_callback = callBack;
+ sdr->app_context = context;
+ *sdRef = sdr;
+
+ return err;
+
+error:
+ if (msg) free(msg);
+ if (*sdRef) { free(*sdRef); *sdRef = NULL; }
+ return kDNSServiceErr_Unknown;
+ }
+
+static void handle_query_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data)
+ {
+ DNSServiceFlags flags;
+ uint32_t interfaceIndex, ttl;
+ DNSServiceErrorType errorCode;
+ char name[kDNSServiceMaxDomainName];
+ uint16_t rrtype, rrclass, rdlen;
+ char *rdata;
+ int str_error = 0;
+ (void)hdr;//Unused
+
+ flags = get_flags(&data);
+ interfaceIndex = get_long(&data);
+ errorCode = get_error_code(&data);
+ if (get_string(&data, name, kDNSServiceMaxDomainName) < 0) str_error = 1;
+ rrtype = get_short(&data);
+ rrclass = get_short(&data);
+ rdlen = get_short(&data);
+ rdata = get_rdata(&data, rdlen);
+ ttl = get_long(&data);
+
+ if (!errorCode && str_error) errorCode = kDNSServiceErr_Unknown;
+ ((DNSServiceQueryRecordReply)sdr->app_callback)(sdr, flags, interfaceIndex, errorCode, name, rrtype, rrclass,
+ rdlen, rdata, ttl, sdr->app_context);
+ return;
+ }
+
+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 *msg = NULL, *ptr;
+ size_t len;
+ ipc_msg_hdr *hdr;
+ DNSServiceRef sdr;
+ DNSServiceErrorType err;
+
+ if (!sdRef) return kDNSServiceErr_BadParam;
+ *sdRef = 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, 1);
+ if (!hdr) goto error;
+ msg = (void *)hdr;
+
+ put_flags(flags, &ptr);
+ put_long(interfaceIndex, &ptr);
+ put_string(name, &ptr);
+ put_short(rrtype, &ptr);
+ put_short(rrclass, &ptr);
+
+ sdr = connect_to_server();
+ if (!sdr) goto error;
+ err = deliver_request(msg, sdr, 1);
+ if (err)
+ {
+ DNSServiceRefDeallocate(sdr);
+ return err;
+ }
+
+ sdr->op = query_request;
+ sdr->process_reply = handle_query_response;
+ sdr->app_callback = callBack;
+ sdr->app_context = context;
+ *sdRef = sdr;
+ return err;
+
+error:
+ if (msg) free(msg);
+ if (*sdRef) { free(*sdRef); *sdRef = NULL; }
+ return kDNSServiceErr_Unknown;
+ }
+
+static void handle_browse_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data)
+ {
+ DNSServiceFlags flags;
+ uint32_t interfaceIndex;
+ DNSServiceErrorType errorCode;
+ char replyName[256], replyType[kDNSServiceMaxDomainName],
+ replyDomain[kDNSServiceMaxDomainName];
+ int str_error = 0;
+ (void)hdr;//Unused
+
+ flags = get_flags(&data);
+ interfaceIndex = get_long(&data);
+ errorCode = get_error_code(&data);
+ if (get_string(&data, replyName, 256) < 0) str_error = 1;
+ if (get_string(&data, replyType, kDNSServiceMaxDomainName) < 0) str_error = 1;
+ if (get_string(&data, replyDomain, kDNSServiceMaxDomainName) < 0) str_error = 1;
+ if (!errorCode && str_error) errorCode = kDNSServiceErr_Unknown;
+ ((DNSServiceBrowseReply)sdr->app_callback)(sdr, flags, interfaceIndex, errorCode, replyName, replyType, replyDomain, sdr->app_context);
+ }
+
+DNSServiceErrorType DNSSD_API DNSServiceBrowse
+ (
+ DNSServiceRef *sdRef,
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ const char *regtype,
+ const char *domain,
+ DNSServiceBrowseReply callBack,
+ void *context
+ )
+ {
+ char *msg = NULL, *ptr;
+ size_t len;
+ ipc_msg_hdr *hdr;
+ DNSServiceRef sdr;
+ DNSServiceErrorType err;
+
+ if (!sdRef) return kDNSServiceErr_BadParam;
+ *sdRef = 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, 1);
+ if (!hdr) goto error;
+ msg = (char *)hdr;
+ put_flags(flags, &ptr);
+ put_long(interfaceIndex, &ptr);
+ put_string(regtype, &ptr);
+ put_string(domain, &ptr);
+
+ sdr = connect_to_server();
+ if (!sdr) goto error;
+ err = deliver_request(msg, sdr, 1);
+ if (err)
+ {
+ DNSServiceRefDeallocate(sdr);
+ return err;
+ }
+ sdr->op = browse_request;
+ sdr->process_reply = handle_browse_response;
+ sdr->app_callback = callBack;
+ sdr->app_context = context;
+ *sdRef = sdr;
+ return err;
+
+error:
+ if (msg) free(msg);
+ if (*sdRef) { free(*sdRef); *sdRef = NULL; }
+ return kDNSServiceErr_Unknown;
+ }
+
+DNSServiceErrorType DNSSD_API DNSServiceSetDefaultDomainForUser
+ (
+ DNSServiceFlags flags,
+ const char *domain
+ )
+ {
+ DNSServiceRef sdr;
+ DNSServiceErrorType err;
+ char *ptr = NULL;
+ size_t len = sizeof(flags) + strlen(domain) + 1;
+ ipc_msg_hdr *hdr = create_hdr(setdomain_request, &len, &ptr, 1);
+
+ if (!hdr) return kDNSServiceErr_Unknown;
+ put_flags(flags, &ptr);
+ put_string(domain, &ptr);
+
+ sdr = connect_to_server();
+ if (!sdr) { free(hdr); return kDNSServiceErr_Unknown; }
+ err = deliver_request((char *)hdr, sdr, 1); // deliver_request frees the message for us
+ DNSServiceRefDeallocate(sdr);
+ return err;
+ }
+
+
+static void handle_regservice_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data)
+ {
+ DNSServiceFlags flags;
+ uint32_t interfaceIndex;
+ DNSServiceErrorType errorCode;
+ char name[256], regtype[kDNSServiceMaxDomainName], domain[kDNSServiceMaxDomainName];
+ int str_error = 0;
+ (void)hdr;//Unused
+
+ flags = get_flags(&data);
+ interfaceIndex = get_long(&data);
+ errorCode = get_error_code(&data);
+ if (get_string(&data, name, 256) < 0) str_error = 1;
+ if (get_string(&data, regtype, kDNSServiceMaxDomainName) < 0) str_error = 1;
+ if (get_string(&data, domain, kDNSServiceMaxDomainName) < 0) str_error = 1;
+ if (!errorCode && str_error) errorCode = kDNSServiceErr_Unknown;
+ ((DNSServiceRegisterReply)sdr->app_callback)(sdr, flags, errorCode, name, regtype, domain, sdr->app_context);
+ }
+
+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 *msg = NULL, *ptr;
+ size_t len;
+ ipc_msg_hdr *hdr;
+ DNSServiceRef sdr;
+ DNSServiceErrorType err;
+ union { uint16_t s; u_char b[2]; } port = { PortInNetworkByteOrder };
+
+ if (!sdRef) return kDNSServiceErr_BadParam;
+ *sdRef = NULL;
+
+ if (!name) name = "";
+ if (!regtype) return kDNSServiceErr_BadParam;
+ if (!domain) domain = "";
+ if (!host) host = "";
+ if (!txtRecord) txtRecord = (void*)"";
+
+ // auto-name must also have auto-rename
+ if (!name[0] && (flags & kDNSServiceFlagsNoAutoRename))
+ return kDNSServiceErr_BadParam;
+
+ // no callback must have auto-rename
+ if (!callBack && (flags & kDNSServiceFlagsNoAutoRename)) return kDNSServiceErr_BadParam;
+
+ 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, 1);
+ if (!hdr) goto error;
+ if (!callBack) hdr->flags |= IPC_FLAGS_NOREPLY;
+ msg = (char *)hdr;
+ put_flags(flags, &ptr);
+ put_long(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_short(txtLen, &ptr);
+ put_rdata(txtLen, txtRecord, &ptr);
+
+ sdr = connect_to_server();
+ if (!sdr) goto error;
+ err = deliver_request(msg, sdr, 1);
+ if (err)
+ {
+ DNSServiceRefDeallocate(sdr);
+ return err;
+ }
+
+ sdr->op = reg_service_request;
+ sdr->process_reply = callBack ? handle_regservice_response : NULL;
+ sdr->app_callback = callBack;
+ sdr->app_context = context;
+ *sdRef = sdr;
+
+ return err;
+
+error:
+ if (msg) free(msg);
+ if (*sdRef) { free(*sdRef); *sdRef = NULL; }
+ return kDNSServiceErr_Unknown;
+ }
+
+static void handle_enumeration_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data)
+ {
+ DNSServiceFlags flags;
+ uint32_t interfaceIndex;
+ DNSServiceErrorType err;
+ char domain[kDNSServiceMaxDomainName];
+ int str_error = 0;
+ (void)hdr;//Unused
+
+ flags = get_flags(&data);
+ interfaceIndex = get_long(&data);
+ err = get_error_code(&data);
+ if (get_string(&data, domain, kDNSServiceMaxDomainName) < 0) str_error = 1;
+ if (!err && str_error) err = kDNSServiceErr_Unknown;
+ ((DNSServiceDomainEnumReply)sdr->app_callback)(sdr, flags, interfaceIndex, err, domain, sdr->app_context);
+ }
+
+DNSServiceErrorType DNSSD_API DNSServiceEnumerateDomains
+ (
+ DNSServiceRef *sdRef,
+ DNSServiceFlags flags,
+ uint32_t interfaceIndex,
+ DNSServiceDomainEnumReply callBack,
+ void *context
+ )
+ {
+ char *msg = NULL, *ptr;
+ size_t len;
+ ipc_msg_hdr *hdr;
+ DNSServiceRef sdr;
+ DNSServiceErrorType err;
+ int f1 = (flags & kDNSServiceFlagsBrowseDomains) != 0;
+ int f2 = (flags & kDNSServiceFlagsRegistrationDomains) != 0;
+ if (f1 + f2 != 1) return kDNSServiceErr_BadParam;
+
+ if (!sdRef) return kDNSServiceErr_BadParam;
+ *sdRef = NULL;
+
+ len = sizeof(DNSServiceFlags);
+ len += sizeof(uint32_t);
+
+ hdr = create_hdr(enumeration_request, &len, &ptr, 1);
+ if (!hdr) goto error;
+ msg = (void *)hdr;
+
+ put_flags(flags, &ptr);
+ put_long(interfaceIndex, &ptr);
+
+ sdr = connect_to_server();
+ if (!sdr) goto error;
+ err = deliver_request(msg, sdr, 1);
+ if (err)
+ {
+ DNSServiceRefDeallocate(sdr);
+ return err;
+ }
+
+ sdr->op = enumeration_request;
+ sdr->process_reply = handle_enumeration_response;
+ sdr->app_callback = callBack;
+ sdr->app_context = context;
+ *sdRef = sdr;
+ return err;
+
+error:
+ if (msg) free(msg);
+ if (*sdRef) { free(*sdRef); *sdRef = NULL; }
+ return kDNSServiceErr_Unknown;
+ }
+
+static void handle_regrecord_response(DNSServiceRef sdr, ipc_msg_hdr *hdr, char *data)
+ {
+ DNSServiceFlags flags;
+ uint32_t interfaceIndex;
+ DNSServiceErrorType errorCode;
+ DNSRecordRef rref = hdr->client_context.context;
+
+ if (sdr->op != connection)
+ {
+ rref->app_callback(rref->sdr, rref, 0, kDNSServiceErr_Unknown, rref->app_context);
+ return;
+ }
+ flags = get_flags(&data);
+ interfaceIndex = get_long(&data);
+ errorCode = get_error_code(&data);
+
+ rref->app_callback(rref->sdr, rref, flags, errorCode, rref->app_context);
+ }
+
+DNSServiceErrorType DNSSD_API DNSServiceCreateConnection(DNSServiceRef *sdRef)
+ {
+ if (!sdRef) return kDNSServiceErr_BadParam;
+ *sdRef = connect_to_server();
+ if (!*sdRef)
+ return kDNSServiceErr_Unknown;
+ (*sdRef)->op = connection;
+ (*sdRef)->process_reply = handle_regrecord_response;
+ return 0;
+ }
+
+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 *msg = NULL, *ptr;
+ size_t len;
+ ipc_msg_hdr *hdr = NULL;
+ DNSServiceRef tmp = NULL;
+ DNSRecordRef rref = NULL;
+ int f1 = (flags & kDNSServiceFlagsShared) != 0;
+ int f2 = (flags & kDNSServiceFlagsUnique) != 0;
+ if (f1 + f2 != 1) return kDNSServiceErr_BadParam;
+
+ if (!sdRef || sdRef->op != connection || sdRef->sockfd < 0)
+ 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, 0);
+ if (!hdr) goto error;
+ msg = (char *)hdr;
+ put_flags(flags, &ptr);
+ put_long(interfaceIndex, &ptr);
+ put_string(fullname, &ptr);
+ put_short(rrtype, &ptr);
+ put_short(rrclass, &ptr);
+ put_short(rdlen, &ptr);
+ put_rdata(rdlen, rdata, &ptr);
+ put_long(ttl, &ptr);
+
+ rref = malloc(sizeof(_DNSRecordRef_t));
+ if (!rref) goto error;
+ rref->app_context = context;
+ rref->app_callback = callBack;
+ rref->record_index = sdRef->max_index++;
+ rref->sdr = sdRef;
+ *RecordRef = rref;
+ hdr->client_context.context = rref;
+ hdr->reg_index = rref->record_index;
+
+ return deliver_request(msg, sdRef, 0);
+
+error:
+ if (rref) free(rref);
+ if (tmp) free(tmp);
+ if (hdr) free(hdr);
+ return kDNSServiceErr_Unknown;
+ }
+
+//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;
+
+ if (!sdRef || (sdRef->op != reg_service_request) || !RecordRef)
+ 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, 0);
+ if (!hdr) return kDNSServiceErr_Unknown;
+ put_flags(flags, &ptr);
+ put_short(rrtype, &ptr);
+ put_short(rdlen, &ptr);
+ put_rdata(rdlen, rdata, &ptr);
+ put_long(ttl, &ptr);
+
+ rref = malloc(sizeof(_DNSRecordRef_t));
+ if (!rref) goto error;
+ rref->app_context = NULL;
+ rref->app_callback = NULL;
+ rref->record_index = sdRef->max_index++;
+ rref->sdr = sdRef;
+ *RecordRef = rref;
+ hdr->client_context.context = rref;
+ hdr->reg_index = rref->record_index;
+ return deliver_request((char *)hdr, sdRef, 0);
+
+error:
+ if (hdr) free(hdr);
+ if (rref) free(rref);
+ if (*RecordRef) *RecordRef = NULL;
+ return kDNSServiceErr_Unknown;
+}
+
+//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) return kDNSServiceErr_BadReference;
+
+ len += sizeof(uint16_t);
+ len += rdlen;
+ len += sizeof(uint32_t);
+ len += sizeof(DNSServiceFlags);
+
+ hdr = create_hdr(update_record_request, &len, &ptr, 0);
+ if (!hdr) return kDNSServiceErr_Unknown;
+ hdr->reg_index = RecordRef ? RecordRef->record_index : TXT_RECORD_INDEX;
+ put_flags(flags, &ptr);
+ put_short(rdlen, &ptr);
+ put_rdata(rdlen, rdata, &ptr);
+ put_long(ttl, &ptr);
+ return deliver_request((char *)hdr, sdRef, 0);
+ }
+
+DNSServiceErrorType DNSSD_API DNSServiceRemoveRecord
+ (
+ DNSServiceRef sdRef,
+ DNSRecordRef RecordRef,
+ DNSServiceFlags flags
+ )
+ {
+ ipc_msg_hdr *hdr;
+ size_t len = 0;
+ char *ptr;
+ DNSServiceErrorType err;
+
+ if (!sdRef || !RecordRef || !sdRef->max_index)
+ return kDNSServiceErr_BadReference;
+
+ len += sizeof(flags);
+ hdr = create_hdr(remove_record_request, &len, &ptr, 0);
+ if (!hdr) return kDNSServiceErr_Unknown;
+ hdr->reg_index = RecordRef->record_index;
+ put_flags(flags, &ptr);
+ err = deliver_request((char *)hdr, sdRef, 0);
+ if (!err) 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;
+ DNSServiceRef tmp;
+
+ len = sizeof(DNSServiceFlags);
+ len += sizeof(uint32_t);
+ len += strlen(fullname) + 1;
+ len += 3 * sizeof(uint16_t);
+ len += rdlen;
+ tmp = connect_to_server();
+ if (!tmp) return(kDNSServiceErr_Unknown);
+ hdr = create_hdr(reconfirm_record_request, &len, &ptr, 1);
+ if (!hdr) return(kDNSServiceErr_Unknown);
+
+ put_flags(flags, &ptr);
+ put_long(interfaceIndex, &ptr);
+ put_string(fullname, &ptr);
+ put_short(rrtype, &ptr);
+ put_short(rrclass, &ptr);
+ put_short(rdlen, &ptr);
+ put_rdata(rdlen, rdata, &ptr);
+ ConvertHeaderBytes(hdr);
+ write_all(tmp->sockfd, (char *)hdr, (int) len);
+ free(hdr);
+ DNSServiceRefDeallocate(tmp);
+ return(kDNSServiceErr_NoError);
+ }
+
diff --git a/usr/src/lib/libdns_sd/common/dnssd_ipc.c b/usr/src/lib/libdns_sd/common/dnssd_ipc.c
new file mode 100644
index 0000000000..92e76bab0c
--- /dev/null
+++ b/usr/src/lib/libdns_sd/common/dnssd_ipc.c
@@ -0,0 +1,135 @@
+/* -*- 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.
+
+ Change History (most recent first):
+
+$Log: dnssd_ipc.c,v $
+Revision 1.16 2006/08/14 23:05:53 cheshire
+Added "tab-width" emacs header line
+
+Revision 1.15 2005/01/27 22:57:56 cheshire
+Fix compile errors on gcc4
+
+Revision 1.14 2004/10/06 02:22:20 cheshire
+Changed MacRoman copyright symbol (should have been UTF-8 in any case :-) to ASCII-compatible "(c)"
+
+Revision 1.13 2004/10/01 22:15:55 rpantos
+rdar://problem/3824265: Replace APSL in client lib with BSD license.
+
+Revision 1.12 2004/09/16 23:14:24 cheshire
+Changes for Windows compatibility
+
+Revision 1.11 2004/06/18 04:56:09 rpantos
+casting goodness
+
+Revision 1.10 2004/06/12 01:08:14 cheshire
+Changes for Windows compatibility
+
+Revision 1.9 2004/05/18 23:51:27 cheshire
+Tidy up all checkin comments to use consistent "<rdar://problem/xxxxxxx>" format for bug numbers
+
+Revision 1.8 2003/11/05 22:44:57 ksekar
+<rdar://problem/3335230>: No bounds checking when reading data from client
+Reviewed by: Stuart Cheshire
+
+Revision 1.7 2003/08/12 19:56:25 cheshire
+Update to APSL 2.0
+
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "dnssd_ipc.h"
+
+void put_long(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_long(char **ptr)
+ {
+ 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_short(uint16_t s, char **ptr)
+ {
+ (*ptr)[0] = (char)((s >> 8) & 0xFF);
+ (*ptr)[1] = (char)((s ) & 0xFF);
+ *ptr += sizeof(uint16_t);
+ }
+
+uint16_t get_short(char **ptr)
+ {
+ 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(char **ptr, char *buffer, int buflen)
+ {
+ int overrun = (int)strlen(*ptr) < buflen ? 0 : -1;
+ strncpy(buffer, *ptr, buflen - 1);
+ buffer[buflen - 1] = '\0';
+ *ptr += strlen(buffer) + 1;
+ return overrun;
+ }
+
+void put_rdata(const int rdlen, const unsigned char *rdata, char **ptr)
+ {
+ memcpy(*ptr, rdata, rdlen);
+ *ptr += rdlen;
+ }
+
+char *get_rdata(char **ptr, int rdlen)
+ {
+ char *rd = *ptr;
+ *ptr += rdlen;
+ return rd;
+ }
+
+void ConvertHeaderBytes(ipc_msg_hdr *hdr)
+ {
+ hdr->version = htonl(hdr->version);
+ hdr->datalen = htonl(hdr->datalen);
+ hdr->flags = htonl(hdr->flags);
+ hdr->op = htonl(hdr->op );
+ hdr->reg_index = htonl(hdr->reg_index);
+ }
diff --git a/usr/src/lib/libdns_sd/common/dnssd_ipc.h b/usr/src/lib/libdns_sd/common/dnssd_ipc.h
new file mode 100644
index 0000000000..2f76591f9a
--- /dev/null
+++ b/usr/src/lib/libdns_sd/common/dnssd_ipc.h
@@ -0,0 +1,253 @@
+/* -*- 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.
+
+ Change History (most recent first):
+
+$Log: dnssd_ipc.h,v $
+Revision 1.23 2006/08/14 23:05:53 cheshire
+Added "tab-width" emacs header line
+
+Revision 1.22 2006/06/28 08:56:26 cheshire
+Added "_op" to the end of the operation code enum values,
+to differentiate them from the routines with the same names
+
+Revision 1.21 2005/09/29 06:38:13 herscher
+Remove #define MSG_WAITALL on Windows. We don't use this macro anymore, and it's presence causes warnings to be emitted when compiling against the latest Microsoft Platform SDK.
+
+Revision 1.20 2005/03/21 00:39:31 shersche
+<rdar://problem/4021486> Fix build warnings on Win32 platform
+
+Revision 1.19 2005/02/02 02:25:22 cheshire
+<rdar://problem/3980388> /var/run/mDNSResponder should be /var/run/mdnsd on Linux
+
+Revision 1.18 2005/01/27 22:57:56 cheshire
+Fix compile errors on gcc4
+
+Revision 1.17 2004/11/23 03:39:47 cheshire
+Let interface name/index mapping capability live directly in JNISupport.c,
+instead of having to call through to the daemon via IPC to get this information.
+
+Revision 1.16 2004/11/12 03:21:41 rpantos
+rdar://problem/3809541 Add DNSSDMapIfIndexToName, DNSSDMapNameToIfIndex.
+
+Revision 1.15 2004/10/06 02:22:20 cheshire
+Changed MacRoman copyright symbol (should have been UTF-8 in any case :-) to ASCII-compatible "(c)"
+
+Revision 1.14 2004/10/01 22:15:55 rpantos
+rdar://problem/3824265: Replace APSL in client lib with BSD license.
+
+Revision 1.13 2004/09/16 23:14:25 cheshire
+Changes for Windows compatibility
+
+Revision 1.12 2004/09/16 21:46:38 ksekar
+<rdar://problem/3665304> Need SPI for LoginWindow to associate a UID with a Wide Area domain
+
+Revision 1.11 2004/08/10 06:24:56 cheshire
+Use types with precisely defined sizes for 'op' and 'reg_index', for better
+compatibility if the daemon and the client stub are built using different compilers
+
+Revision 1.10 2004/07/07 17:39:25 shersche
+Change MDNS_SERVERPORT from 5533 to 5354.
+
+Revision 1.9 2004/06/25 00:26:27 rpantos
+Changes to fix the Posix build on Solaris.
+
+Revision 1.8 2004/06/18 04:56:51 rpantos
+Add layer for platform code
+
+Revision 1.7 2004/06/12 01:08:14 cheshire
+Changes for Windows compatibility
+
+Revision 1.6 2003/08/12 19:56:25 cheshire
+Update to APSL 2.0
+
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#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_EWOULDBLOCK WSAEWOULDBLOCK
+# define dnssd_EINTR WSAEINTR
+# define dnssd_sock_t SOCKET
+# define dnssd_socklen_t int
+# define dnssd_sockbuf_t const char*
+# define dnssd_close(sock) closesocket(sock)
+# define dnssd_errno() WSAGetLastError()
+# define ssize_t int
+# define getpid _getpid
+#else
+# include <sys/types.h>
+# include <unistd.h>
+# include <sys/un.h>
+# include <string.h>
+# include <stdio.h>
+# include <stdlib.h>
+# include <sys/stat.h>
+# include <sys/socket.h>
+# include <netinet/in.h>
+# define dnssd_InvalidSocket -1
+# define dnssd_EWOULDBLOCK EWOULDBLOCK
+# define dnssd_EINTR EINTR
+# define dnssd_EPIPE EPIPE
+# define dnssd_sock_t int
+# define dnssd_socklen_t unsigned int
+# define dnssd_sockbuf_t const char*
+# define dnssd_close(sock) close(sock)
+# define dnssd_errno() errno
+#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
+
+
+//#define UDSDEBUG // verbose debug output
+
+// 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
+#define IPC_FLAGS_REUSE_SOCKET 2 // set flag if synchronous errors are to be sent via the primary socket
+ // (if not set, first string in message buffer must be path to error socket
+
+typedef enum
+ {
+ connection = 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
+ } 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
+ } reply_op_t;
+
+typedef struct ipc_msg_hdr_struct ipc_msg_hdr;
+
+// client stub callback to process message from server and deliver results to
+// client application
+
+typedef void (*process_reply_callback)
+ (
+ DNSServiceRef sdr,
+ ipc_msg_hdr *hdr,
+ char *msg
+ );
+
+// allow 64-bit client to interoperate w/ 32-bit daemon
+typedef union
+ {
+ void *context;
+ uint32_t ptr64[2];
+ } client_context_t;
+
+typedef struct ipc_msg_hdr_struct
+ {
+ uint32_t version;
+ uint32_t datalen;
+ uint32_t 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 DNSServiceConnect(). 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())
+ uint32_t padbytes;
+ } ipc_msg_hdr_struct;
+
+// it is advanced to point to the next field, or the end of the message
+// 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_long(const uint32_t l, char **ptr);
+uint32_t get_long(char **ptr);
+
+void put_short(uint16_t s, char **ptr);
+uint16_t get_short(char **ptr);
+
+#define put_flags put_long
+#define get_flags get_long
+
+#define put_error_code put_long
+#define get_error_code get_long
+
+int put_string(const char *str, char **ptr);
+int get_string(char **ptr, char *buffer, int buflen);
+
+void put_rdata(const int rdlen, const unsigned char *rdata, char **ptr);
+char *get_rdata(char **ptr, int rdlen); // return value is rdata pointed to by *ptr -
+ // rdata is not copied from buffer.
+
+void ConvertHeaderBytes(ipc_msg_hdr *hdr);
+
+#endif // DNSSD_IPC_H
diff --git a/usr/src/lib/libdns_sd/common/llib-ldns_sd b/usr/src/lib/libdns_sd/common/llib-ldns_sd
new file mode 100644
index 0000000000..464e3b9a6d
--- /dev/null
+++ b/usr/src/lib/libdns_sd/common/llib-ldns_sd
@@ -0,0 +1,32 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+/* LINTLIBRARY */
+/* PROTOLIB1 */
+
+#include <dns_sd.h>
diff --git a/usr/src/lib/libdns_sd/common/mapfile-vers b/usr/src/lib/libdns_sd/common/mapfile-vers
new file mode 100644
index 0000000000..012b730ca1
--- /dev/null
+++ b/usr/src/lib/libdns_sd/common/mapfile-vers
@@ -0,0 +1,56 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+SUNW_1.1 {
+ global:
+ DNSServiceAddRecord;
+ DNSServiceBrowse;
+ DNSServiceConstructFullName;
+ DNSServiceCreateConnection;
+ DNSServiceEnumerateDomains;
+ DNSServiceProcessResult;
+ DNSServiceQueryRecord;
+ DNSServiceReconfirmRecord;
+ DNSServiceRefDeallocate;
+ DNSServiceRefSockFD;
+ DNSServiceRegister;
+ DNSServiceRegisterRecord;
+ DNSServiceRemoveRecord;
+ DNSServiceResolve;
+ DNSServiceUpdateRecord;
+ TXTRecordContainsKey;
+ TXTRecordCreate;
+ TXTRecordDeallocate;
+ TXTRecordGetBytesPtr;
+ TXTRecordGetCount;
+ TXTRecordGetItemAtIndex;
+ TXTRecordGetLength;
+ TXTRecordGetValuePtr;
+ TXTRecordRemoveValue;
+ TXTRecordSetValue;
+ local:
+ *;
+};
diff --git a/usr/src/lib/libdns_sd/i386/Makefile b/usr/src/lib/libdns_sd/i386/Makefile
new file mode 100644
index 0000000000..9c1be5bf3a
--- /dev/null
+++ b/usr/src/lib/libdns_sd/i386/Makefile
@@ -0,0 +1,28 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/libdns_sd/java/Makefile b/usr/src/lib/libdns_sd/java/Makefile
new file mode 100644
index 0000000000..5b015fc86e
--- /dev/null
+++ b/usr/src/lib/libdns_sd/java/Makefile
@@ -0,0 +1,47 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+include $(SRC)/lib/Makefile.lib
+
+POFILE = libjdns_sd.po
+
+SUBDIRS = $(MACH)
+$(BUILD64)SUBDIRS += $(MACH64)
+
+all := TARGET= all
+clean := TARGET= clean
+clobber := TARGET= clobber
+install := TARGET= install
+lint := TARGET= lint
+
+.KEEP_STATE:
+
+all install clean clobber lint: com/apple .WAIT $(SUBDIRS)
+
+com/apple $(SUBDIRS): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
diff --git a/usr/src/lib/libdns_sd/java/Makefile.com b/usr/src/lib/libdns_sd/java/Makefile.com
new file mode 100644
index 0000000000..f66463ac2f
--- /dev/null
+++ b/usr/src/lib/libdns_sd/java/Makefile.com
@@ -0,0 +1,57 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+LIBRARY= libjdns_sd.a
+VERS= .1
+
+OBJECTS= JNISupport.o
+
+include $(SRC)/lib/Makefile.lib
+
+LIBS = $(DYNLIB)
+
+SRCDIR = ../common
+
+C99MODE = $(C99_ENABLE)
+CPPFLAGS += -I$(JAVA_ROOT)/include -I$(JAVA_ROOT)/include/solaris
+CPPFLAGS += -I../com/apple/dnssd
+CPPFLAGS += -D_REENTRANT
+
+LDLIBS += -lc -lsocket -ldns_sd
+
+CLEANFILES= $(LINTOUT) $(LINTLIB)
+
+LINTLIB =
+
+.KEEP_STATE:
+
+lint: lintcheck
+
+include $(SRC)/lib/Makefile.targ
+
+pics/%.o: ../common/%.c
+ $(COMPILE.c) -o $@ $<
+ $(POST_PROCESS_O)
diff --git a/usr/src/lib/libdns_sd/java/amd64/Makefile b/usr/src/lib/libdns_sd/java/amd64/Makefile
new file mode 100644
index 0000000000..b645e1caed
--- /dev/null
+++ b/usr/src/lib/libdns_sd/java/amd64/Makefile
@@ -0,0 +1,32 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+include ../Makefile.com
+include ../../../Makefile.lib.64
+
+all: $(LIBS)
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64)
diff --git a/usr/src/lib/libdns_sd/java/com/apple/Makefile b/usr/src/lib/libdns_sd/java/com/apple/Makefile
new file mode 100644
index 0000000000..8cfdc2e941
--- /dev/null
+++ b/usr/src/lib/libdns_sd/java/com/apple/Makefile
@@ -0,0 +1,47 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+include Makefile.com
+
+SUBDIRS = dnssd
+
+all:= TARGET = all
+clean:= TARGET = clean
+clobber:= TARGET = clobber
+install:= TARGET = install
+
+.KEEP_STATE:
+
+all clean install clobber: ${SUBDIRS}
+
+${SUBDIRS}: FRC
+ cd $@; pwd; $(MAKE) $(TARGET)
+
+lint:
+
+.WAIT:
+
+FRC:
diff --git a/usr/src/lib/libdns_sd/java/com/apple/Makefile.com b/usr/src/lib/libdns_sd/java/com/apple/Makefile.com
new file mode 100644
index 0000000000..e699ff14a4
--- /dev/null
+++ b/usr/src/lib/libdns_sd/java/com/apple/Makefile.com
@@ -0,0 +1,31 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+include $(SRC)/lib/Makefile.lib
+
+ROOTDNSSDJAVAHOME = $(ROOT)/usr/share/lib/java
+
+.KEEP_STATE:
diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/BaseListener.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/BaseListener.java
new file mode 100644
index 0000000000..689cb8ba57
--- /dev/null
+++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/BaseListener.java
@@ -0,0 +1,51 @@
+/* -*- Mode: Java; 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.
+
+ Change History (most recent first):
+
+$Log: BaseListener.java,v $
+Revision 1.3 2006/08/14 23:25:08 cheshire
+Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
+
+Revision 1.2 2004/04/30 21:48:27 rpantos
+Change line endings for CVS.
+
+Revision 1.1 2004/04/30 16:29:35 rpantos
+First checked in.
+
+ident "%Z%%M% %I% %E% SMI"
+
+ */
+
+
+package com.apple.dnssd;
+
+
+/** A base class for DNSSD listeners. */
+
+public interface BaseListener
+{
+ /** Called to report DNSSD operation failures.<P>
+
+ @param service
+ The service that encountered the failure.
+ <P>
+ @param errorCode
+ Indicates the failure that occurred. See {@link DNSSDException} for error codes.
+ */
+ void operationFailed( DNSSDService service, int errorCode);
+}
+
diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/BrowseListener.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/BrowseListener.java
new file mode 100644
index 0000000000..0ae9b938ba
--- /dev/null
+++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/BrowseListener.java
@@ -0,0 +1,88 @@
+/* -*- Mode: Java; 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.
+
+ Change History (most recent first):
+
+$Log: BrowseListener.java,v $
+Revision 1.3 2006/08/14 23:25:08 cheshire
+Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
+
+Revision 1.2 2004/04/30 21:48:27 rpantos
+Change line endings for CVS.
+
+Revision 1.1 2004/04/30 16:29:35 rpantos
+First checked in.
+
+ident "%Z%%M% %I% %E% SMI"
+
+ */
+
+
+package com.apple.dnssd;
+
+
+/** A listener that receives results from {@link DNSSD#browse}. */
+
+public interface BrowseListener extends BaseListener
+{
+ /** Called to report discovered services.<P>
+
+ @param browser
+ The active browse service.
+ <P>
+ @param flags
+ Possible values are DNSSD.MORE_COMING.
+ <P>
+ @param ifIndex
+ The interface on which the service is advertised. This index should be passed
+ to {@link DNSSD#resolve} when resolving the service.
+ <P>
+ @param serviceName
+ The service name discovered.
+ <P>
+ @param regType
+ The registration type, as passed in to DNSSD.browse().
+ <P>
+ @param domain
+ The domain in which the service was discovered.
+ */
+ void serviceFound( DNSSDService browser, int flags, int ifIndex,
+ String serviceName, String regType, String domain);
+
+ /** Called to report services which have been deregistered.<P>
+
+ @param browser
+ The active browse service.
+ <P>
+ @param flags
+ Possible values are DNSSD.MORE_COMING.
+ <P>
+ @param ifIndex
+ The interface on which the service is advertised.
+ <P>
+ @param serviceName
+ The service name which has deregistered.
+ <P>
+ @param regType
+ The registration type, as passed in to DNSSD.browse().
+ <P>
+ @param domain
+ The domain in which the service was discovered.
+ */
+ void serviceLost( DNSSDService browser, int flags, int ifIndex,
+ String serviceName, String regType, String domain);
+}
+
diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSRecord.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSRecord.java
new file mode 100644
index 0000000000..6caf3a7791
--- /dev/null
+++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSRecord.java
@@ -0,0 +1,68 @@
+/* -*- Mode: Java; 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.
+
+ Change History (most recent first):
+
+$Log: DNSRecord.java,v $
+Revision 1.3 2006/08/14 23:25:08 cheshire
+Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
+
+Revision 1.2 2004/12/11 03:00:59 rpantos
+<rdar://problem/3907498> Java DNSRecord API should be cleaned up
+
+Revision 1.1 2004/04/30 16:32:34 rpantos
+First checked in.
+
+
+ident "%Z%%M% %I% %E% SMI"
+
+ */
+
+
+package com.apple.dnssd;
+
+
+/**
+ Reference to a record returned by {@link DNSSDRegistration#addRecord}.<P>
+
+ Note: client is responsible for serializing access to these objects if
+ they are shared between concurrent threads.
+*/
+
+public interface DNSRecord
+{
+ /** Update a registered resource record.<P>
+ The record must either be the primary txt record of a service registered via DNSSD.register(),
+ or a record added to a registered service via addRecord().<P>
+
+ @param flags
+ Currently unused, reserved for future use.
+ <P>
+ @param rData
+ The new rdata to be contained in the updated resource record.
+ <P>
+ @param ttl
+ The time to live of the updated resource record, in seconds.
+ */
+ void update( int flags, byte[] rData, int ttl)
+ throws DNSSDException;
+
+ /** Remove a registered resource record.<P>
+ */
+ void remove()
+ throws DNSSDException;
+}
+
diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSD.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSD.java
new file mode 100644
index 0000000000..9841c9492b
--- /dev/null
+++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSD.java
@@ -0,0 +1,897 @@
+/* -*- Mode: Java; 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.
+
+ Change History (most recent first):
+
+$Log: DNSSD.java,v $
+Revision 1.11 2006/08/14 23:25:08 cheshire
+Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
+
+Revision 1.10 2006/06/20 23:05:55 rpantos
+<rdar://problem/3839132> Java needs to implement DNSServiceRegisterRecord equivalent
+
+Revision 1.9 2005/10/26 01:52:24 cheshire
+<rdar://problem/4316286> Race condition in Java code (doesn't work at all on Linux)
+
+Revision 1.8 2005/07/11 01:55:21 cheshire
+<rdar://problem/4175511> Race condition in Java API
+
+Revision 1.7 2005/07/05 13:01:52 cheshire
+<rdar://problem/4169791> If mDNSResponder daemon is stopped, Java API spins, burning CPU time
+
+Revision 1.6 2005/07/05 00:02:25 cheshire
+Add missing comma
+
+Revision 1.5 2005/07/04 21:13:47 cheshire
+Add missing error message strings
+
+Revision 1.4 2004/12/11 03:00:59 rpantos
+<rdar://problem/3907498> Java DNSRecord API should be cleaned up
+
+Revision 1.3 2004/11/12 03:23:08 rpantos
+rdar://problem/3809541 implement getIfIndexForName, getNameForIfIndex.
+
+Revision 1.2 2004/05/20 17:43:18 cheshire
+Fix invalid UTF-8 characters in file
+
+Revision 1.1 2004/04/30 16:32:34 rpantos
+First checked in.
+
+
+ This file declares and implements DNSSD, the central Java factory class
+ for doing DNS Service Discovery. It includes the mostly-abstract public
+ interface, as well as the Apple* implementation subclasses.
+ */
+
+/*
+ * ident "%Z%%M% %I% %E% SMI"
+ */
+
+package com.apple.dnssd;
+
+
+/**
+ DNSSD provides access to DNS Service Discovery features of ZeroConf networking.<P>
+
+ It is a factory class that is used to invoke registration and discovery-related
+ operations. Most operations are non-blocking; clients are called back through an interface
+ with the result of an operation. Callbacks are made from a separate worker thread.<P>
+
+ For example, in this program<P>
+ <PRE><CODE>
+ class MyClient implements BrowseListener {
+ void lookForWebServers() {
+ myBrowser = DNSSD.browse("_http_.tcp", this);
+ }
+
+ public void serviceFound(DNSSDService browser, int flags, int ifIndex,
+ String serviceName, String regType, String domain) {}
+ ...
+ }</CODE></PRE>
+ <CODE>MyClient.serviceFound()</CODE> would be called for every HTTP server discovered in the
+ default browse domain(s).
+*/
+
+abstract public class DNSSD
+{
+ /** Flag indicates to a {@link BrowseListener} that another result is
+ queued. Applications should not update their UI to display browse
+ results if the MORE_COMING flag is set; they will be called at least once
+ more with the flag clear.
+ */
+ public static final int MORE_COMING = ( 1 << 0 );
+
+ /** If flag is set in a {@link DomainListener} callback, indicates that the result is the default domain. */
+ public static final int DEFAULT = ( 1 << 2 );
+
+ /** If flag is set, a name conflict will trigger an exception when registering non-shared records.<P>
+ A name must be explicitly specified when registering a service if this bit is set
+ (i.e. the default name may not not be used).
+ */
+ public static final int NO_AUTO_RENAME = ( 1 << 3 );
+
+ /** If flag is set, allow multiple records with this name on the network (e.g. PTR records)
+ when registering individual records on a {@link DNSSDRegistration}.
+ */
+ public static final int SHARED = ( 1 << 4 );
+
+ /** If flag is set, records with this name must be unique on the network (e.g. SRV records). */
+ public static final int UNIQUE = ( 1 << 5 );
+
+ /** Set flag when calling enumerateDomains() to restrict results to domains recommended for browsing. */
+ public static final int BROWSE_DOMAINS = ( 1 << 6 );
+ /** Set flag when calling enumerateDomains() to restrict results to domains recommended for registration. */
+ public static final int REGISTRATION_DOMAINS = ( 1 << 7 );
+
+ /** Maximum length, in bytes, of a domain name represented as an escaped C-String. */
+ public static final int MAX_DOMAIN_NAME = 1005;
+
+ /** Pass for ifIndex to specify all available interfaces. */
+ public static final int ALL_INTERFACES = 0;
+
+ /** Pass for ifIndex to specify the localhost interface. */
+ public static final int LOCALHOST_ONLY = -1;
+
+ /** Browse for instances of a service.<P>
+
+ Note: browsing consumes network bandwidth. Call {@link DNSSDService#stop} when you have finished browsing.<P>
+
+ @param flags
+ Currently ignored, reserved for future use.
+ <P>
+ @param ifIndex
+ 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.
+ <P>
+ @param regType
+ The registration type being browsed for followed by the protocol, separated by a
+ dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp".
+ <P>
+ @param 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).
+ <P>
+ @param listener
+ This object will get called when instances of the service are discovered (or disappear).
+ <P>
+ @return A {@link DNSSDService} that represents the active browse operation.
+
+ @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
+ @see RuntimePermission
+ */
+ public static DNSSDService browse( int flags, int ifIndex, String regType, String domain, BrowseListener listener)
+ throws DNSSDException
+ { return getInstance()._makeBrowser( flags, ifIndex, regType, domain, listener); }
+
+ /** Browse for instances of a service. Use default flags, ifIndex and domain.<P>
+
+ @param regType
+ The registration type being browsed for followed by the protocol, separated by a
+ dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp".
+ <P>
+ @param listener
+ This object will get called when instances of the service are discovered (or disappear).
+ <P>
+ @return A {@link DNSSDService} that represents the active browse operation.
+
+ @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
+ @see RuntimePermission
+ */
+ public static DNSSDService browse( String regType, BrowseListener listener)
+ throws DNSSDException
+ { return browse( 0, 0, regType, "", listener); }
+
+ /** Resolve a service name discovered via browse() to a target host name, port number, and txt record.<P>
+
+ Note: Applications should NOT use resolve() solely for txt record monitoring - use
+ queryRecord() instead, as it is more efficient for this task.<P>
+
+ Note: When the desired results have been returned, the client MUST terminate the resolve by
+ calling {@link DNSSDService#stop}.<P>
+
+ 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, use queryRecord().<P>
+
+ @param flags
+ Currently ignored, reserved for future use.
+ <P>
+ @param ifIndex
+ The interface on which to resolve the service. The client should
+ pass the interface on which the serviceName was discovered (i.e.
+ the ifIndex passed to the serviceFound() callback)
+ or 0 to resolve the named service on all available interfaces.
+ <P>
+ @param serviceName
+ The servicename to be resolved.
+ <P>
+ @param regType
+ The registration type being resolved followed by the protocol, separated by a
+ dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp".
+ <P>
+ @param domain
+ The domain on which the service is registered, i.e. the domain passed
+ to the serviceFound() callback.
+ <P>
+ @param listener
+ This object will get called when the service is resolved.
+ <P>
+ @return A {@link DNSSDService} that represents the active resolve operation.
+
+ @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
+ @see RuntimePermission
+ */
+ public static DNSSDService resolve( int flags, int ifIndex, String serviceName, String regType,
+ String domain, ResolveListener listener)
+ throws DNSSDException
+ { return getInstance()._resolve( flags, ifIndex, serviceName, regType, domain, listener); }
+
+ /** Register a service, to be discovered via browse() and resolve() calls.<P>
+ @param flags
+ Possible values are: NO_AUTO_RENAME.
+ <P>
+ @param ifIndex
+ 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).
+ <P>
+ @param serviceName
+ If non-null, specifies the service name to be registered.
+ Applications need not specify a name, in which case the
+ computer name is used (this name is communicated to the client via
+ the serviceRegistered() callback).
+ <P>
+ @param regType
+ The registration type being registered followed by the protocol, separated by a
+ dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp".
+ <P>
+ @param 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).
+ <P>
+ @param 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 {@link DNSSDRegistration#addRecord}.
+ <P>
+ @param port
+ The port 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.
+ <P>
+ @param txtRecord
+ The txt record rdata. May be null. Note that a non-null txtRecord
+ MUST be a properly formatted DNS TXT record, i.e. &lt;length byte&gt; &lt;data&gt;
+ &lt;length byte&gt; &lt;data&gt; ...
+ <P>
+ @param listener
+ This object will get called when the service is registered.
+ <P>
+ @return A {@link DNSSDRegistration} that controls the active registration.
+
+ @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
+ @see RuntimePermission
+ */
+ public static DNSSDRegistration register( int flags, int ifIndex, String serviceName, String regType,
+ String domain, String host, int port, TXTRecord txtRecord, RegisterListener listener)
+ throws DNSSDException
+ { return getInstance()._register( flags, ifIndex, serviceName, regType, domain, host, port, txtRecord, listener); }
+
+ /** Register a service, to be discovered via browse() and resolve() calls. Use default flags, ifIndex, domain, host and txtRecord.<P>
+ @param serviceName
+ If non-null, specifies the service name to be registered.
+ Applications need not specify a name, in which case the
+ computer name is used (this name is communicated to the client via
+ the serviceRegistered() callback).
+ <P>
+ @param regType
+ The registration type being registered followed by the protocol, separated by a
+ dot (e.g. "_ftp._tcp"). The transport protocol must be "_tcp" or "_udp".
+ <P>
+ @param port
+ The port 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.
+ <P>
+ @param listener
+ This object will get called when the service is registered.
+ <P>
+ @return A {@link DNSSDRegistration} that controls the active registration.
+
+ @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
+ @see RuntimePermission
+ */
+ public static DNSSDRegistration register( String serviceName, String regType, int port, RegisterListener listener)
+ throws DNSSDException
+ { return register( 0, 0, serviceName, regType, null, null, port, null, listener); }
+
+ /** Create a {@link DNSSDRecordRegistrar} allowing efficient registration of
+ multiple individual records.<P>
+ <P>
+ @return A {@link DNSSDRecordRegistrar} that can be used to register records.
+
+ @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
+ @see RuntimePermission
+ */
+ public static DNSSDRecordRegistrar createRecordRegistrar( RegisterRecordListener listener)
+ throws DNSSDException
+ { return getInstance()._createRecordRegistrar( listener); }
+
+ /** Query for an arbitrary DNS record.<P>
+ @param flags
+ Possible values are: MORE_COMING.
+ <P>
+ @param ifIndex
+ 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.
+ <P>
+ @param serviceName
+ The full domain name of the resource record to be queried for.
+ <P>
+ @param rrtype
+ The numerical type of the resource record to be queried for (e.g. PTR, SRV, etc)
+ as defined in nameser.h.
+ <P>
+ @param rrclass
+ The class of the resource record, as defined in nameser.h
+ (usually 1 for the Internet class).
+ <P>
+ @param listener
+ This object will get called when the query completes.
+ <P>
+ @return A {@link DNSSDService} that controls the active query.
+
+ @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
+ @see RuntimePermission
+ */
+ public static DNSSDService queryRecord( int flags, int ifIndex, String serviceName, int rrtype,
+ int rrclass, QueryListener listener)
+ throws DNSSDException
+ { return getInstance()._queryRecord( flags, ifIndex, serviceName, rrtype, rrclass, listener); }
+
+ /** Asynchronously enumerate domains available for browsing and registration.<P>
+
+ Currently, the only domain returned is "local.", but other domains will be returned in future.<P>
+
+ The enumeration MUST be cancelled by calling {@link DNSSDService#stop} when no more domains
+ are to be found.<P>
+ @param flags
+ Possible values are: BROWSE_DOMAINS, REGISTRATION_DOMAINS.
+ <P>
+ @param ifIndex
+ 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.
+ <P>
+ @param listener
+ This object will get called when domains are found.
+ <P>
+ @return A {@link DNSSDService} that controls the active enumeration.
+
+ @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
+ @see RuntimePermission
+ */
+ public static DNSSDService enumerateDomains( int flags, int ifIndex, DomainListener listener)
+ throws DNSSDException
+ { return getInstance()._enumerateDomains( flags, ifIndex, listener); }
+
+ /** Concatenate a three-part domain name (as provided to the listeners) into a
+ properly-escaped full domain name. Note that strings passed to listeners are
+ ALREADY ESCAPED where necessary.<P>
+ @param serviceName
+ The service name - any dots or slashes must NOT be escaped.
+ May be null (to construct a PTR record name, e.g. "_ftp._tcp.apple.com").
+ <P>
+ @param regType
+ The registration type followed by the protocol, separated by a dot (e.g. "_ftp._tcp").
+ <P>
+ @param domain
+ The domain name, e.g. "apple.com". Any literal dots or backslashes must be escaped.
+ <P>
+ @return The full domain name.
+
+ @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
+ @see RuntimePermission
+ */
+ public static String constructFullName( String serviceName, String regType, String domain)
+ throws DNSSDException
+ { return getInstance()._constructFullName( serviceName, regType, domain); }
+
+ /** 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.) <P>
+
+ 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.<P>
+ @param flags
+ Currently unused, reserved for future use.
+ <P>
+ @param ifIndex
+ If non-zero, specifies the interface on which to reconfirm the record
+ (the index for a given interface is determined via the if_nametoindex()
+ family of calls.) Passing 0 causes the name to be reconfirmed on all
+ interfaces. Passing -1 causes the name to be reconfirmed only on the
+ local host.
+ <P>
+ @param fullName
+ The resource record's full domain name.
+ <P>
+ @param rrtype
+ The resource record's type (e.g. PTR, SRV, etc) as defined in nameser.h.
+ <P>
+ @param rrclass
+ The class of the resource record, as defined in nameser.h (usually 1).
+ <P>
+ @param rdata
+ The raw rdata of the resource record.
+
+ @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
+ @see RuntimePermission
+ */
+ public static void reconfirmRecord( int flags, int ifIndex, String fullName, int rrtype,
+ int rrclass, byte[] rdata)
+ { getInstance()._reconfirmRecord( flags, ifIndex, fullName, rrtype, rrclass, rdata); }
+
+ /** Return the canonical name of a particular interface index.<P>
+ @param ifIndex
+ A valid interface index. Must not be ALL_INTERFACES.
+ <P>
+ @return The name of the interface, which should match java.net.NetworkInterface.getName().
+
+ @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
+ @see RuntimePermission
+ */
+ public static String getNameForIfIndex( int ifIndex)
+ { return getInstance()._getNameForIfIndex( ifIndex); }
+
+ /** Return the index of a named interface.<P>
+ @param ifName
+ A valid interface name. An example is java.net.NetworkInterface.getName().
+ <P>
+ @return The interface index.
+
+ @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
+ @see RuntimePermission
+ */
+ public static int getIfIndexForName( String ifName)
+ { return getInstance()._getIfIndexForName( ifName); }
+
+ protected DNSSD() {} // prevent direct instantiation
+
+ /** Return the single instance of DNSSD. */
+ static protected final DNSSD getInstance()
+ {
+ SecurityManager sm = System.getSecurityManager();
+ if ( sm != null)
+ sm.checkPermission( new RuntimePermission( "getDNSSDInstance"));
+ return fInstance;
+ }
+
+ abstract protected DNSSDService _makeBrowser( int flags, int ifIndex, String regType, String domain, BrowseListener listener)
+ throws DNSSDException;
+
+ abstract protected DNSSDService _resolve( int flags, int ifIndex, String serviceName, String regType,
+ String domain, ResolveListener listener)
+ throws DNSSDException;
+
+ abstract protected DNSSDRegistration _register( int flags, int ifIndex, String serviceName, String regType,
+ String domain, String host, int port, TXTRecord txtRecord, RegisterListener listener)
+ throws DNSSDException;
+
+ abstract protected DNSSDRecordRegistrar _createRecordRegistrar( RegisterRecordListener listener)
+ throws DNSSDException;
+
+ abstract protected DNSSDService _queryRecord( int flags, int ifIndex, String serviceName, int rrtype,
+ int rrclass, QueryListener listener)
+ throws DNSSDException;
+
+ abstract protected DNSSDService _enumerateDomains( int flags, int ifIndex, DomainListener listener)
+ throws DNSSDException;
+
+ abstract protected String _constructFullName( String serviceName, String regType, String domain)
+ throws DNSSDException;
+
+ abstract protected void _reconfirmRecord( int flags, int ifIndex, String fullName, int rrtype,
+ int rrclass, byte[] rdata);
+
+ abstract protected String _getNameForIfIndex( int ifIndex);
+
+ abstract protected int _getIfIndexForName( String ifName);
+
+ protected static DNSSD fInstance;
+
+ static
+ {
+ try
+ {
+ String name = System.getProperty( "com.apple.dnssd.DNSSD" );
+ if( name == null )
+ name = "com.apple.dnssd.AppleDNSSD"; // Fall back to Apple-provided class.
+ fInstance = (DNSSD) Class.forName( name ).newInstance();
+ }
+ catch( Exception e )
+ {
+ throw new InternalError( "cannot instantiate DNSSD" + e );
+ }
+ }
+}
+
+
+// Concrete implementation of DNSSDException
+class AppleDNSSDException extends DNSSDException
+{
+ public AppleDNSSDException( int errorCode) { fErrorCode = errorCode; }
+
+ public int getErrorCode() { return fErrorCode; }
+
+ public String getMessage()
+ {
+ final String kMessages[] = { // should probably be put into a resource or something
+ "UNKNOWN",
+ "NO_SUCH_NAME",
+ "NO_MEMORY",
+ "BAD_PARAM",
+ "BAD_REFERENCE",
+ "BAD_STATE",
+ "BAD_FLAGS",
+ "UNSUPPORTED",
+ "NOT_INITIALIZED",
+ "NO_CACHE",
+ "ALREADY_REGISTERED",
+ "NAME_CONFLICT",
+ "INVALID",
+ "FIREWALL",
+ "INCOMPATIBLE",
+ "BAD_INTERFACE_INDEX",
+ "REFUSED",
+ "NOSUCHRECORD",
+ "NOAUTH",
+ "NOSUCHKEY",
+ "NATTRAVERSAL",
+ "DOUBLENAT",
+ "BADTIME",
+ "BADSIG",
+ "BADKEY",
+ "TRANSIENT"
+ };
+
+ if ( fErrorCode <= UNKNOWN && fErrorCode > ( UNKNOWN - kMessages.length))
+ {
+ return "DNS-SD Error " + String.valueOf( fErrorCode) + ": " + kMessages[ UNKNOWN - fErrorCode];
+ }
+ else
+ return super.getMessage() + "(" + String.valueOf( fErrorCode) + ")";
+ }
+
+ protected int fErrorCode;
+}
+
+// The concrete, default implementation.
+class AppleDNSSD extends DNSSD
+{
+ static
+ {
+ System.loadLibrary( "jdns_sd");
+
+ int libInitResult = InitLibrary( 1);
+
+ if ( libInitResult != DNSSDException.NO_ERROR)
+ throw new InternalError( "cannot instantiate DNSSD: " + new AppleDNSSDException( libInitResult).getMessage());
+ }
+
+ static public boolean hasAutoCallbacks; // Set by InitLibrary() to value of AUTO_CALLBACKS
+
+ protected DNSSDService _makeBrowser( int flags, int ifIndex, String regType, String domain, BrowseListener client)
+ throws DNSSDException
+ {
+ return new AppleBrowser( flags, ifIndex, regType, domain, client);
+ }
+
+ protected DNSSDService _resolve( int flags, int ifIndex, String serviceName, String regType,
+ String domain, ResolveListener client)
+ throws DNSSDException
+ {
+ return new AppleResolver( flags, ifIndex, serviceName, regType, domain, client);
+ }
+
+ protected DNSSDRegistration _register( int flags, int ifIndex, String serviceName, String regType,
+ String domain, String host, int port, TXTRecord txtRecord, RegisterListener client)
+ throws DNSSDException
+ {
+ return new AppleRegistration( flags, ifIndex, serviceName, regType, domain, host, port,
+ ( txtRecord != null) ? txtRecord.getRawBytes() : null, client);
+ }
+
+ protected DNSSDRecordRegistrar _createRecordRegistrar( RegisterRecordListener listener)
+ throws DNSSDException
+ {
+ return new AppleRecordRegistrar( listener);
+ }
+
+ protected DNSSDService _queryRecord( int flags, int ifIndex, String serviceName, int rrtype,
+ int rrclass, QueryListener client)
+ throws DNSSDException
+ {
+ return new AppleQuery( flags, ifIndex, serviceName, rrtype, rrclass, client);
+ }
+
+ protected DNSSDService _enumerateDomains( int flags, int ifIndex, DomainListener listener)
+ throws DNSSDException
+ {
+ return new AppleDomainEnum( flags, ifIndex, listener);
+ }
+
+ protected String _constructFullName( String serviceName, String regType, String domain)
+ throws DNSSDException
+ {
+ String[] responseHolder = new String[1]; // lame maneuver to get around Java's lack of reference parameters
+
+ int rc = ConstructName( serviceName, regType, domain, responseHolder);
+ if ( rc != 0)
+ throw new AppleDNSSDException( rc);
+
+ return responseHolder[0];
+ }
+
+ protected void _reconfirmRecord( int flags, int ifIndex, String fullName, int rrtype,
+ int rrclass, byte[] rdata)
+ {
+ ReconfirmRecord( flags, ifIndex, fullName, rrtype, rrclass, rdata);
+ }
+
+ protected String _getNameForIfIndex( int ifIndex)
+ {
+ return GetNameForIfIndex( ifIndex);
+ }
+
+ protected int _getIfIndexForName( String ifName)
+ {
+ return GetIfIndexForName( ifName);
+ }
+
+
+ protected native int ConstructName( String serviceName, String regType, String domain, String[] pOut);
+
+ protected native void ReconfirmRecord( int flags, int ifIndex, String fullName, int rrtype,
+ int rrclass, byte[] rdata);
+
+ protected native String GetNameForIfIndex( int ifIndex);
+
+ protected native int GetIfIndexForName( String ifName);
+
+ protected static native int InitLibrary( int callerVersion);
+}
+
+class AppleService implements DNSSDService, Runnable
+{
+ public AppleService(BaseListener listener) { fNativeContext = 0; fListener = listener; }
+
+ public void stop() { this.HaltOperation(); }
+
+ /* Block until data arrives, or one second passes. Returns 1 if data present, 0 otherwise. */
+ protected native int BlockForData();
+
+ /* Call ProcessResults when data appears on socket descriptor. */
+ protected native int ProcessResults();
+
+ protected synchronized native void HaltOperation();
+
+ protected void ThrowOnErr( int rc) throws DNSSDException
+ {
+ if ( rc != 0)
+ throw new AppleDNSSDException( rc);
+ }
+
+ protected long /* warning */ fNativeContext; // Private storage for native side
+
+ public void run()
+ {
+ while ( true )
+ {
+ // Note: We want to allow our DNS-SD operation to be stopped from other threads, so we have to
+ // block waiting for data *outside* the synchronized section. Because we're doing this unsynchronized
+ // we have to write some careful code. Suppose our DNS-SD operation is stopped from some other thread,
+ // and then immediately afterwards that thread (or some third, unrelated thread) starts a new DNS-SD
+ // operation. The Unix kernel always allocates the lowest available file descriptor to a new socket,
+ // so the same file descriptor is highly likely to be reused for the new operation, and if our old
+ // stale ServiceThread accidentally consumes bytes off that new socket we'll get really messed up.
+ // To guard against that, before calling ProcessResults we check to ensure that our
+ // fNativeContext has not been deleted, which is a telltale sign that our operation was stopped.
+ // After calling ProcessResults we check again, because it's extremely common for callback
+ // functions to stop their own operation and start others. For example, a resolveListener callback
+ // may well stop the resolve and then start a QueryRecord call to monitor the TXT record.
+ //
+ // The remaining risk is that between our checking fNativeContext and calling ProcessResults(),
+ // some other thread could stop the operation and start a new one using same file descriptor, and
+ // we wouldn't know. To prevent this, the AppleService object's HaltOperation() routine is declared
+ // synchronized and we perform our checks synchronized on the AppleService object, which ensures
+ // that HaltOperation() can't execute while we're doing it. Because Java locks are re-entrant this
+ // locking DOESN'T prevent the callback routine from stopping its own operation, but DOES prevent
+ // any other thread from stopping it until after the callback has completed and returned to us here.
+
+ int result = BlockForData();
+ synchronized (this)
+ {
+ if (fNativeContext == 0) break; // Some other thread stopped our DNSSD operation; time to terminate this thread
+ if (result == 0) continue; // If BlockForData() said there was no data, go back and block again
+ result = ProcessResults();
+ if (fNativeContext == 0) break; // Event listener stopped its own DNSSD operation; terminate this thread
+ if (result != 0) { fListener.operationFailed(this, result); break; } // If error, notify listener
+ }
+ }
+ }
+
+ protected BaseListener fListener;
+}
+
+
+class AppleBrowser extends AppleService
+{
+ public AppleBrowser( int flags, int ifIndex, String regType, String domain, BrowseListener client)
+ throws DNSSDException
+ {
+ super(client);
+ this.ThrowOnErr( this.CreateBrowser( flags, ifIndex, regType, domain));
+ if ( !AppleDNSSD.hasAutoCallbacks)
+ new Thread(this).start();
+ }
+
+ // Sets fNativeContext. Returns non-zero on error.
+ protected native int CreateBrowser( int flags, int ifIndex, String regType, String domain);
+}
+
+class AppleResolver extends AppleService
+{
+ public AppleResolver( int flags, int ifIndex, String serviceName, String regType,
+ String domain, ResolveListener client)
+ throws DNSSDException
+ {
+ super(client);
+ this.ThrowOnErr( this.CreateResolver( flags, ifIndex, serviceName, regType, domain));
+ if ( !AppleDNSSD.hasAutoCallbacks)
+ new Thread(this).start();
+ }
+
+ // Sets fNativeContext. Returns non-zero on error.
+ protected native int CreateResolver( int flags, int ifIndex, String serviceName, String regType,
+ String domain);
+}
+
+// An AppleDNSRecord is a simple wrapper around a dns_sd DNSRecord.
+class AppleDNSRecord implements DNSRecord
+{
+ public AppleDNSRecord( AppleService owner)
+ {
+ fOwner = owner;
+ fRecord = 0; // record always starts out empty
+ }
+
+ public void update( int flags, byte[] rData, int ttl)
+ throws DNSSDException
+ {
+ this.ThrowOnErr( this.Update( flags, rData, ttl));
+ }
+
+ public void remove()
+ throws DNSSDException
+ {
+ this.ThrowOnErr( this.Remove());
+ }
+
+ protected long fRecord; // Really a DNSRecord; sizeof(int) == sizeof(void*) ?
+ protected AppleService fOwner;
+
+ protected void ThrowOnErr( int rc) throws DNSSDException
+ {
+ if ( rc != 0)
+ throw new AppleDNSSDException( rc);
+ }
+
+ protected native int Update( int flags, byte[] rData, int ttl);
+
+ protected native int Remove();
+}
+
+class AppleRegistration extends AppleService implements DNSSDRegistration
+{
+ public AppleRegistration( int flags, int ifIndex, String serviceName, String regType, String domain,
+ String host, int port, byte[] txtRecord, RegisterListener client)
+ throws DNSSDException
+ {
+ super(client);
+ this.ThrowOnErr( this.BeginRegister( ifIndex, flags, serviceName, regType, domain, host, port, txtRecord));
+ if ( !AppleDNSSD.hasAutoCallbacks)
+ new Thread(this).start();
+ }
+
+ public DNSRecord addRecord( int flags, int rrType, byte[] rData, int ttl)
+ throws DNSSDException
+ {
+ AppleDNSRecord newRecord = new AppleDNSRecord( this);
+
+ this.ThrowOnErr( this.AddRecord( flags, rrType, rData, ttl, newRecord));
+ return newRecord;
+ }
+
+ public DNSRecord getTXTRecord()
+ throws DNSSDException
+ {
+ return new AppleDNSRecord( this); // A record with ref 0 is understood to be primary TXT record
+ }
+
+ // Sets fNativeContext. Returns non-zero on error.
+ protected native int BeginRegister( int ifIndex, int flags, String serviceName, String regType,
+ String domain, String host, int port, byte[] txtRecord);
+
+ // Sets fNativeContext. Returns non-zero on error.
+ protected native int AddRecord( int flags, int rrType, byte[] rData, int ttl, AppleDNSRecord destObj);
+}
+
+class AppleRecordRegistrar extends AppleService implements DNSSDRecordRegistrar
+{
+ public AppleRecordRegistrar( RegisterRecordListener listener)
+ throws DNSSDException
+ {
+ super(listener);
+ this.ThrowOnErr( this.CreateConnection());
+ if ( !AppleDNSSD.hasAutoCallbacks)
+ new Thread(this).start();
+ }
+
+ public DNSRecord registerRecord( int flags, int ifIndex, String fullname, int rrtype,
+ int rrclass, byte[] rdata, int ttl)
+ throws DNSSDException
+ {
+ AppleDNSRecord newRecord = new AppleDNSRecord( this);
+
+ this.ThrowOnErr( this.RegisterRecord( flags, ifIndex, fullname, rrtype, rrclass, rdata, ttl, newRecord));
+ return newRecord;
+ }
+
+ // Sets fNativeContext. Returns non-zero on error.
+ protected native int CreateConnection();
+
+ // Sets fNativeContext. Returns non-zero on error.
+ protected native int RegisterRecord( int flags, int ifIndex, String fullname, int rrtype,
+ int rrclass, byte[] rdata, int ttl, AppleDNSRecord destObj);
+}
+
+class AppleQuery extends AppleService
+{
+ public AppleQuery( int flags, int ifIndex, String serviceName, int rrtype,
+ int rrclass, QueryListener client)
+ throws DNSSDException
+ {
+ super(client);
+ this.ThrowOnErr( this.CreateQuery( flags, ifIndex, serviceName, rrtype, rrclass));
+ if ( !AppleDNSSD.hasAutoCallbacks)
+ new Thread(this).start();
+ }
+
+ // Sets fNativeContext. Returns non-zero on error.
+ protected native int CreateQuery( int flags, int ifIndex, String serviceName, int rrtype, int rrclass);
+}
+
+class AppleDomainEnum extends AppleService
+{
+ public AppleDomainEnum( int flags, int ifIndex, DomainListener client)
+ throws DNSSDException
+ {
+ super(client);
+ this.ThrowOnErr( this.BeginEnum( flags, ifIndex));
+ if ( !AppleDNSSD.hasAutoCallbacks)
+ new Thread(this).start();
+ }
+
+ // Sets fNativeContext. Returns non-zero on error.
+ protected native int BeginEnum( int flags, int ifIndex);
+}
+
+
diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDException.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDException.java
new file mode 100644
index 0000000000..618128f31f
--- /dev/null
+++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDException.java
@@ -0,0 +1,76 @@
+/* -*- Mode: Java; 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.
+
+ Change History (most recent first):
+
+$Log: DNSSDException.java,v $
+Revision 1.4 2006/08/14 23:25:08 cheshire
+Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
+
+Revision 1.3 2005/07/10 22:19:01 cheshire
+Add missing error codes to list of public static final ints
+
+Revision 1.2 2004/04/30 21:48:27 rpantos
+Change line endings for CVS.
+
+Revision 1.1 2004/04/30 16:29:35 rpantos
+First checked in.
+
+ident "%Z%%M% %I% %E% SMI"
+
+*/
+
+package com.apple.dnssd;
+
+
+/**
+ Used to report various DNS-SD-related error conditions.
+*/
+
+abstract public class DNSSDException extends Exception
+{
+ public static final int NO_ERROR = 0;
+ public static final int UNKNOWN = -65537;
+ public static final int NO_SUCH_NAME = -65538;
+ public static final int NO_MEMORY = -65539;
+ public static final int BAD_PARAM = -65540;
+ public static final int BAD_REFERENCE = -65541;
+ public static final int BAD_STATE = -65542;
+ public static final int BAD_FLAGS = -65543;
+ public static final int UNSUPPORTED = -65544;
+ public static final int NOT_INITIALIZED = -65545;
+ public static final int NO_CACHE = -65546;
+ public static final int ALREADY_REGISTERED = -65547;
+ public static final int NAME_CONFLICT = -65548;
+ public static final int INVALID = -65549;
+ public static final int FIREWALL = -65550;
+ public static final int INCOMPATIBLE = -65551;
+ public static final int BAD_INTERFACE_INDEX = -65552;
+ public static final int REFUSED = -65553;
+ public static final int NOSUCHRECORD = -65554;
+ public static final int NOAUTH = -65555;
+ public static final int NOSUCHKEY = -65556;
+ public static final int NATTRAVERSAL = -65557;
+ public static final int DOUBLENAT = -65558;
+ public static final int BADTIME = -65559;
+ public static final int BADSIG = -65560;
+ public static final int BADKEY = -65561;
+ public static final int TRANSIENT = -65562;
+
+ /** Returns the sub-code that identifies the particular error. */
+ abstract public int getErrorCode();
+}
+
diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDRecordRegistrar.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDRecordRegistrar.java
new file mode 100644
index 0000000000..366d83476b
--- /dev/null
+++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDRecordRegistrar.java
@@ -0,0 +1,79 @@
+/* -*- Mode: Java; 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.
+
+ This file declares the public interface to DNSSDRecordRegistrar, a DNSSDService
+ subclass that allows efficient registration of multiple individual records.
+
+ Change History (most recent first):
+
+$Log: DNSSDRecordRegistrar.java,v $
+Revision 1.2 2006/08/14 23:25:08 cheshire
+Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
+
+Revision 1.1 2006/06/20 23:00:12 rpantos
+<rdar://problem/3839132> Java needs to implement DNSServiceRegisterRecord equivalent
+
+ident "%Z%%M% %I% %E% SMI"
+
+ */
+
+
+package com.apple.dnssd;
+
+
+/** An object for registering records, created by {@link DNSSD#createRecordRegistrar}. */
+
+public interface DNSSDRecordRegistrar extends DNSSDService
+{
+ /** Register an independent {@link DNSRecord}.<P>
+ @param flags
+ Possible values are SHARED or UNIQUE (see flag type definitions for details).
+ <P>
+ @param ifIndex
+ 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.
+ <P>
+ @param fullname
+ The full domain name of the resource record.
+ <P>
+ @param rrtype
+ The numerical type of the resource record to be queried for (e.g. PTR, SRV, etc)
+ as defined in nameser.h.
+ <P>
+ @param rrclass
+ The class of the resource record, as defined in nameser.h
+ (usually 1 for the Internet class).
+ <P>
+ @param rData
+ The new rdata as it is to appear in the DNS record.
+ <P>
+ @param ttl
+ The time to live of the resource record, in seconds. Pass 0 to use a default value.
+ <P>
+ @param listener
+ This object will get called when the service is registered.
+ <P>
+ @return A {@link DNSSDService} that can be used to abort the record registration.
+
+ @throws SecurityException If a security manager is present and denies <tt>RuntimePermission("getDNSSDInstance")</tt>.
+ @see RuntimePermission
+ */
+ public DNSRecord registerRecord( int flags, int ifIndex, String fullname, int rrtype,
+ int rrclass, byte[] rdata, int ttl)
+ throws DNSSDException;
+}
+
diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDRegistration.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDRegistration.java
new file mode 100644
index 0000000000..4ae38036ed
--- /dev/null
+++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDRegistration.java
@@ -0,0 +1,77 @@
+/* -*- Mode: Java; 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.
+
+ Change History (most recent first):
+
+$Log: DNSSDRegistration.java,v $
+Revision 1.3 2006/08/14 23:25:08 cheshire
+Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
+
+Revision 1.2 2004/12/11 03:01:00 rpantos
+<rdar://problem/3907498> Java DNSRecord API should be cleaned up
+
+Revision 1.1 2004/04/30 16:32:34 rpantos
+First checked in.
+
+ident "%Z%%M% %I% %E% SMI"
+
+ This file declares the public interface to DNSSDRegistration, a DNSSDService
+ subclass that allows a client to control a service registration.
+ */
+
+
+package com.apple.dnssd;
+
+
+/** A tracking object for a registration created by {@link DNSSD#register}. */
+
+public interface DNSSDRegistration extends DNSSDService
+{
+ /** Get a reference to the primary TXT record of a registered service.<P>
+ The record can be updated by sending it an update() message.<P>
+
+ <P>
+ @return A {@link DNSRecord}.
+ If {@link DNSSDRegistration#stop} is called, the DNSRecord is also
+ invalidated and may not be used further.
+ */
+ DNSRecord getTXTRecord()
+ throws DNSSDException;
+
+ /** Add a record to a registered service.<P>
+ The name of the record will be the same as the registered service's name.<P>
+ The record can be updated or deregistered by sending it an update() or remove() message.<P>
+
+ @param flags
+ Currently unused, reserved for future use.
+ <P>
+ @param rrType
+ The type of the record (e.g. TXT, SRV, etc), as defined in nameser.h.
+ <P>
+ @param rData
+ The raw rdata to be contained in the added resource record.
+ <P>
+ @param ttl
+ The time to live of the resource record, in seconds.
+ <P>
+ @return A {@link DNSRecord} that may be passed to updateRecord() or removeRecord().
+ If {@link DNSSDRegistration#stop} is called, the DNSRecord is also
+ invalidated and may not be used further.
+ */
+ DNSRecord addRecord( int flags, int rrType, byte[] rData, int ttl)
+ throws DNSSDException;
+}
+
diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDService.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDService.java
new file mode 100644
index 0000000000..93cc5fd2c9
--- /dev/null
+++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/DNSSDService.java
@@ -0,0 +1,52 @@
+/* -*- Mode: Java; 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.
+
+ Change History (most recent first):
+
+$Log: DNSSDService.java,v $
+Revision 1.3 2006/08/14 23:25:08 cheshire
+Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
+
+Revision 1.2 2004/04/30 21:48:27 rpantos
+Change line endings for CVS.
+
+Revision 1.1 2004/04/30 16:32:34 rpantos
+First checked in.
+
+ident "%Z%%M% %I% %E% SMI"
+
+ */
+
+
+package com.apple.dnssd;
+
+/** A tracking object for a service created by {@link DNSSD}. */
+
+public interface DNSSDService
+{
+ /**
+ Halt the active operation and free resources associated with the DNSSDService.<P>
+
+ Any services or records registered with this DNSSDService will be deregistered. Any
+ Browse, Resolve, or Query operations associated with this reference will be terminated.<P>
+
+ Note: if the service was initialized with DNSSD.register(), and an extra resource record was
+ added to the service via {@link DNSSDRegistration#addRecord}, the DNSRecord so created
+ is invalidated when this method is called - the DNSRecord may not be used afterward.
+ */
+ void stop();
+}
+
diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/DomainListener.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/DomainListener.java
new file mode 100644
index 0000000000..276d5b539a
--- /dev/null
+++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/DomainListener.java
@@ -0,0 +1,75 @@
+/* -*- Mode: Java; 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.
+
+ Change History (most recent first):
+
+$Log: DomainListener.java,v $
+Revision 1.3 2006/08/14 23:25:08 cheshire
+Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
+
+Revision 1.2 2004/04/30 21:48:27 rpantos
+Change line endings for CVS.
+
+Revision 1.1 2004/04/30 16:29:35 rpantos
+First checked in.
+
+ident "%Z%%M% %I% %E% SMI"
+
+ */
+
+
+package com.apple.dnssd;
+
+
+/**
+ A listener that receives results from {@link DNSSD#enumerateDomains}.
+*/
+
+public interface DomainListener extends BaseListener
+{
+ /** Called to report discovered domains.<P>
+
+ @param domainEnum
+ The active domain enumerator.
+ @param flags
+ Possible values are: DNSSD.MORE_COMING, DNSSD.DEFAULT
+ <P>
+ @param ifIndex
+ Specifies the interface on which the domain exists. (The index for a given
+ interface is determined via the if_nametoindex() family of calls.)
+ <P>
+ @param domain
+ The name of the domain.
+ */
+ void domainFound( DNSSDService domainEnum, int flags, int ifIndex, String domain);
+
+ /** Called to report that a domain has disappeared.<P>
+
+ @param domainEnum
+ The active domain enumerator.
+ @param flags
+ Possible values are: DNSSD.MORE_COMING, DNSSD.DEFAULT
+ <P>
+ @param ifIndex
+ Specifies the interface on which the domain exists. (The index for a given
+ interface is determined via the if_nametoindex() family of calls.)
+ <P>
+ @param domain
+ The name of the domain.
+ */
+ void domainLost( DNSSDService domainEnum, int flags, int ifIndex, String domain);
+}
+
diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/Makefile b/usr/src/lib/libdns_sd/java/com/apple/dnssd/Makefile
new file mode 100644
index 0000000000..e25208d774
--- /dev/null
+++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/Makefile
@@ -0,0 +1,150 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+include ../Makefile.com
+
+DNSSD_PKG = com.apple.dnssd
+
+TOP = $(SRC)/lib/libdns_sd/java
+JAVASRCDIR = $(TOP)/com/apple/dnssd
+CLASSPATH = $(TOP):com/apple/dnssd
+
+JAVAFLAGS += -source 1.4 -target 1.4
+SOURCE:sh = ls *.java
+CLASSES = $(SOURCE:java=class)
+JNIH = DNSSD.java.h
+JAR_FILE = dnssd.jar
+
+DOCDIR = $(JAVASRCDIR)/docs
+DOCAPIDIR = $(JAVASRCDIR)/docs/api
+DOCDESTDIR = $(ROOTDNSSDJAVAHOME)/javadoc/dnssd
+DOCAPIDESTDIR = $(DOCDESTDIR)/api
+DOCEXAMPLESDESTDIR = $(DOCDESTDIR)/examples
+
+EXAMPLESDIR = $(JAVASRCDIR)/docs/examples
+EXAMPLESSRC = $(JAVASRCDIR)/docs/examples/src
+SIMPLECHATOBJ = $(EXAMPLESDIR)/SwingBrowseListener.class \
+ $(EXAMPLESDIR)/SwingQueryListener.class \
+ $(EXAMPLESDIR)/SimpleChat.class
+BROWSERAPPOBJ = $(EXAMPLESDIR)/SwingResolveListener.class \
+ $(EXAMPLESDIR)/SwingDomainListener.class \
+ $(EXAMPLESDIR)/BrowserApp.class
+EXAMPLEOBJS = $(SIMPLECHATOBJ) $(BROWSERAPPOBJ)
+EXAMPLEJARS = SimpleChat.jar BrowserApp.jar
+
+INSTALL_JAR = $(ROOTDNSSDJAVAHOME)/$(JAR_FILE)
+INSTALL_EXAMPLEJARS = $(DOCEXAMPLESDESTDIR)/SimpleChat.jar \
+ $(DOCEXAMPLESDESTDIR)/BrowserApp.jar
+
+CLEAN_FILES = *.class $(JNIH) *.jar $(EXAMPLESDIR)/*.class $(EXAMPLESDIR)/*.jar
+
+DEFINES=
+
+INCLUDES= -I${JAVA_HOME}/include \
+ -I${JAVA_HOME}/include/solaris
+
+.KEEP_STATE:
+
+all: $(JNIH) $(CLASSES) $(EXAMPLEOBJS) doc
+
+install: $(CLASSES) $(ROOTDNSSDJAVAHOME) \
+ $(DOCEXAMPLESDESTDIR) $(DOCEXAMPLESSRCDESTDIR) \
+ $(JAR_FILE) $(INSTALL_JAR) $(JNIH) \
+ $(EXAMPLEJARS) $(INSTALL_EXAMPLEJARS) \
+ install_doc
+
+$(JNIH): $(CLASSES)
+ class="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"; \
+ $(JAVAH) -classpath $(CLASSPATH) -jni -o $(JNIH) $$class
+
+clean clobber:
+ $(RM) $(CLEAN_FILES)
+
+$(JAR_FILE): $(CLASSES)
+ cd $(TOP); \
+ $(JAR) -cvf $(TOP)/com/apple/dnssd/$(JAR_FILE) com/apple/dnssd/*.class
+
+$(EXAMPLESDIR)/%.class: $(EXAMPLESSRC)/%.java
+ $(JAVAC) $(JAVAFLAGS) $< -classpath $(CLASSPATH):$(EXAMPLESDIR) -d $(EXAMPLESDIR)
+
+SIMPLECHATMAN = $(EXAMPLESSRC)/SimpleChat.manifest
+
+SimpleChat.jar: $(SIMPLECHATOBJ) $(SIMPLECHATMAN)
+ cd $(EXAMPLESDIR); $(JAR) -cvfm $@ $(SIMPLECHATMAN) \
+ SwingBrowseListener.class SwingQueryListener.class \
+ SimpleChat.class SimpleChat\$$1.class \
+ ListenerThread.class TargetListElem.class \
+ TargetListModel.class src/SimpleChat.java \
+ src/SimpleChat.manifest src/SwingBrowseListener.java \
+ src/SwingQueryListener.java
+
+BROWSERAPPMAN = $(EXAMPLESSRC)/BrowserApp.manifest
+
+BrowserApp.jar: $(BROWSERAPPOBJ) $(BROWSERAPPMAN)
+ cd $(EXAMPLESDIR); $(JAR) -cvfm $@ $(BROWSERAPPMAN) \
+ BrowserApp\$$1.class BrowserApp.class \
+ BrowserListModel\$$BrowserListElem.class \
+ BrowserListModel.class DomainListModel.class \
+ ServicesBrowserListModel.class \
+ SwingResolveListener.class SwingDomainListener.class \
+ src/BrowserApp.java src/SwingResolveListener.java \
+ src/SwingDomainListener.java src/BrowserApp.manifest
+
+$(ROOTDNSSDJAVAHOME):
+ $(INS.dir)
+
+$(ROOTDNSSDJAVAHOME)/%: %
+ $(INS.file)
+
+$(DOCDESTDIR):
+ $(INS.dir)
+
+$(DOCAPIDESTDIR): $(DOCDESTDIR)
+ $(INS.dir)
+
+$(DOCEXAMPLESDESTDIR): $(DOCDESTDIR)
+ $(INS.dir)
+
+$(DOCEXAMPLESDESTDIR)/%: %
+ $(RM) $@; $(INS) -s -m $(FILEMODE) -f $(@D) $(EXAMPLESDIR)/$<
+
+install_doc: $(CLASSES) $(DOCAPIDESTDIR)
+ -$(RM) -r $(DOCAPIDESTDIR)/*
+ cd $(TOP); umask 022; \
+ $(JAVADOC) $(JAVASRCDIR)/*.java -classpath $(CLASSPATH) -d \
+ $(DOCAPIDESTDIR) -public $(DNSSD_PKG)
+
+doc:
+ -@mkdir -p $(DOCAPIDIR)
+ cd $(TOP); umask 022; \
+ $(JAVADOC) $(JAVASRCDIR)/*.java -classpath $(CLASSPATH) -d \
+ $(DOCAPIDIR) -public $(DNSSD_PKG)
diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/QueryListener.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/QueryListener.java
new file mode 100644
index 0000000000..7978dda42f
--- /dev/null
+++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/QueryListener.java
@@ -0,0 +1,71 @@
+/* -*- Mode: Java; 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.
+
+ Change History (most recent first):
+
+$Log: QueryListener.java,v $
+Revision 1.3 2006/08/14 23:25:08 cheshire
+Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
+
+Revision 1.2 2004/04/30 21:48:27 rpantos
+Change line endings for CVS.
+
+Revision 1.1 2004/04/30 16:29:35 rpantos
+First checked in.
+
+ident "%Z%%M% %I% %E% SMI"
+
+ */
+
+
+package com.apple.dnssd;
+
+
+/** A listener that receives results from {@link DNSSD#queryRecord}. */
+
+public interface QueryListener extends BaseListener
+{
+ /** Called when a record query has been completed.<P>
+
+ @param query
+ The active query object.
+ <P>
+ @param flags
+ Possible values are DNSSD.MORE_COMING.
+ <P>
+ @param ifIndex
+ The interface on which the query was resolved. (The index for a given
+ interface is determined via the if_nametoindex() family of calls.)
+ <P>
+ @param fullName
+ The resource record's full domain name.
+ <P>
+ @param rrtype
+ The resource record's type (e.g. PTR, SRV, etc) as defined by RFC 1035 and its updates.
+ <P>
+ @param rrclass
+ The class of the resource record, as defined by RFC 1035 and its updates.
+ <P>
+ @param rdata
+ The raw rdata of the resource record.
+ <P>
+ @param ttl
+ The resource record's time to live, in seconds.
+ */
+ void queryAnswered( DNSSDService query, int flags, int ifIndex, String fullName,
+ int rrtype, int rrclass, byte[] rdata, int ttl);
+}
+
diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/RegisterListener.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/RegisterListener.java
new file mode 100644
index 0000000000..d40b6f3e75
--- /dev/null
+++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/RegisterListener.java
@@ -0,0 +1,64 @@
+/* -*- Mode: Java; 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.
+
+ Change History (most recent first):
+
+$Log: RegisterListener.java,v $
+Revision 1.3 2006/08/14 23:25:08 cheshire
+Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
+
+Revision 1.2 2004/04/30 21:48:27 rpantos
+Change line endings for CVS.
+
+Revision 1.1 2004/04/30 16:29:35 rpantos
+First checked in.
+
+ident "%Z%%M% %I% %E% SMI"
+
+ */
+
+
+package com.apple.dnssd;
+
+
+/** A listener that receives results from {@link DNSSD#register}. */
+
+public interface RegisterListener extends BaseListener
+{
+ /** Called when a registration has been completed.<P>
+
+ @param registration
+ The active registration.
+ <P>
+ @param flags
+ Currently unused, reserved for future use.
+ <P>
+ @param serviceName
+ The service name registered (if the application did not specify a name in
+ DNSSD.register(), this indicates what name was automatically chosen).
+ <P>
+ @param regType
+ The type of service registered, as it was passed to DNSSD.register().
+ <P>
+ @param domain
+ The domain on which the service was registered. If the application did not
+ specify a domain in DNSSD.register(), this is the default domain
+ on which the service was registered.
+ */
+ void serviceRegistered( DNSSDRegistration registration, int flags, String serviceName,
+ String regType, String domain);
+}
+
diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/RegisterRecordListener.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/RegisterRecordListener.java
new file mode 100644
index 0000000000..6b8c757ae3
--- /dev/null
+++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/RegisterRecordListener.java
@@ -0,0 +1,49 @@
+/* -*- Mode: Java; 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.
+
+ Change History (most recent first):
+
+$Log: RegisterRecordListener.java,v $
+Revision 1.2 2006/08/14 23:25:08 cheshire
+Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
+
+Revision 1.1 2006/06/20 23:00:12 rpantos
+<rdar://problem/3839132> Java needs to implement DNSServiceRegisterRecord equivalent
+
+ident "%Z%%M% %I% %E% SMI"
+
+ */
+
+
+package com.apple.dnssd;
+
+
+/** A listener that receives results from {@link DNSSDRecordRegistrar#registerRecord}. */
+
+public interface RegisterRecordListener extends BaseListener
+{
+ /** Called when a record registration succeeds.<P>
+
+ @param record
+ A {@link DNSRecord}.
+ <P>
+ @param flags
+ Currently ignored, reserved for future use.
+ <P>
+ */
+ void recordRegistered( DNSRecord record, int flags);
+}
+
diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/ResolveListener.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/ResolveListener.java
new file mode 100644
index 0000000000..4589f53915
--- /dev/null
+++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/ResolveListener.java
@@ -0,0 +1,71 @@
+/* -*- Mode: Java; 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.
+
+ Change History (most recent first):
+
+$Log: ResolveListener.java,v $
+Revision 1.3 2006/08/14 23:25:08 cheshire
+Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
+
+Revision 1.2 2004/04/30 21:48:27 rpantos
+Change line endings for CVS.
+
+Revision 1.1 2004/04/30 16:29:35 rpantos
+First checked in.
+
+ident "%Z%%M% %I% %E% SMI"
+
+*/
+
+
+package com.apple.dnssd;
+
+
+/** A listener that receives results from {@link DNSSD#resolve}. */
+
+public interface ResolveListener extends BaseListener
+{
+ /** Called when a service has been resolved.<P>
+
+ @param resolver
+ The active resolver object.
+ <P>
+ @param flags
+ Currently unused, reserved for future use.
+ <P>
+ @param fullName
+ The full service domain name, in the form &lt;servicename&gt;.&lt;protocol&gt;.&lt;domain&gt;.
+ (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.
+ <P>
+ @param hostName
+ The target hostname of the machine providing the service. This name can
+ be passed to functions like queryRecord() to look up the host's IP address.
+ <P>
+ @param port
+ The port number on which connections are accepted for this service.
+ <P>
+ @param txtRecord
+ The service's primary txt record.
+ */
+ void serviceResolved( DNSSDService resolver, int flags, int ifIndex, String fullName,
+ String hostName, int port, TXTRecord txtRecord);
+}
+
diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/TXTRecord.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/TXTRecord.java
new file mode 100644
index 0000000000..c1c5f0b3b7
--- /dev/null
+++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/TXTRecord.java
@@ -0,0 +1,313 @@
+/* -*- Mode: Java; 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.
+
+ Change History (most recent first):
+
+$Log: TXTRecord.java,v $
+Revision 1.6 2006/08/14 23:25:08 cheshire
+Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
+
+Revision 1.5 2004/08/25 21:54:36 rpantos
+<rdar://problem/3773973> Fix getValue() for values containing '='.
+
+Revision 1.4 2004/08/04 01:04:50 rpantos
+<rdar://problems/3731579&3731582> Fix set(); add remove() & toString().
+
+Revision 1.3 2004/07/13 21:24:25 rpantos
+Fix for <rdar://problem/3701120>.
+
+Revision 1.2 2004/04/30 21:48:27 rpantos
+Change line endings for CVS.
+
+Revision 1.1 2004/04/30 16:29:35 rpantos
+First checked in.
+
+ident "%Z%%M% %I% %E% SMI"
+
+ To do:
+ - implement remove()
+ - fix set() to replace existing values
+ */
+
+
+package com.apple.dnssd;
+
+
+/**
+ Object used to construct and parse DNS-SD format TXT records.
+ For more info see <a href="http://files.dns-sd.org/draft-cheshire-dnsext-dns-sd.txt">DNS-Based Service Discovery</a>, section 6.
+*/
+
+public class TXTRecord
+{
+ /*
+ DNS-SD specifies that a TXT record corresponding to an SRV record consist of
+ a packed array of bytes, each preceded by a length byte. Each string
+ is an attribute-value pair.
+
+ The TXTRecord object stores the entire TXT data as a single byte array, traversing it
+ as need be to implement its various methods.
+ */
+
+ static final protected byte kAttrSep = '=';
+
+ protected byte[] fBytes;
+
+ /** Constructs a new, empty TXT record. */
+ public TXTRecord()
+ { fBytes = new byte[0]; }
+
+ /** Constructs a new TXT record from a byte array in the standard format. */
+ public TXTRecord( byte[] initBytes)
+ { fBytes = (byte[]) initBytes.clone(); }
+
+ /** Set a key/value pair in the TXT record. Setting an existing key will replace its value.<P>
+ @param key
+ The key name. Must be ASCII, with no '=' characters.
+ <P>
+ @param value
+ Value to be encoded into bytes using the default platform character set.
+ */
+ public void set( String key, String value)
+ {
+ byte[] valBytes = (value != null) ? value.getBytes() : null;
+ this.set( key, valBytes);
+ }
+
+ /** Set a key/value pair in the TXT record. Setting an existing key will replace its value.<P>
+ @param key
+ The key name. Must be ASCII, with no '=' characters.
+ <P>
+ @param value
+ Binary representation of the value.
+ */
+ public void set( String key, byte[] value)
+ {
+ byte[] keyBytes;
+ int valLen = (value != null) ? value.length : 0;
+
+ try {
+ keyBytes = key.getBytes( "US-ASCII");
+ }
+ catch ( java.io.UnsupportedEncodingException uee) {
+ throw new IllegalArgumentException();
+ }
+
+ for ( int i=0; i < keyBytes.length; i++)
+ if ( keyBytes[i] == '=')
+ throw new IllegalArgumentException();
+
+ if ( keyBytes.length + valLen >= 255)
+ throw new ArrayIndexOutOfBoundsException();
+
+ int prevLoc = this.remove( key);
+ if ( prevLoc == -1)
+ prevLoc = this.size();
+
+ this.insert( keyBytes, value, prevLoc);
+ }
+
+ protected void insert( byte[] keyBytes, byte[] value, int index)
+ // Insert a key-value pair at index
+ {
+ byte[] oldBytes = fBytes;
+ int valLen = (value != null) ? value.length : 0;
+ int insertion = 0;
+ byte newLen, avLen;
+
+ // locate the insertion point
+ for ( int i=0; i < index && insertion < fBytes.length; i++)
+ insertion += fBytes[ insertion] + 1;
+
+ avLen = (byte) ( keyBytes.length + valLen + (value != null ? 1 : 0));
+ newLen = (byte) ( avLen + oldBytes.length + 1);
+
+ fBytes = new byte[ newLen];
+ System.arraycopy( oldBytes, 0, fBytes, 0, insertion);
+ int secondHalfLen = oldBytes.length - insertion;
+ System.arraycopy( oldBytes, insertion, fBytes, newLen - secondHalfLen, secondHalfLen);
+ fBytes[ insertion] = avLen;
+ System.arraycopy( keyBytes, 0, fBytes, insertion + 1, keyBytes.length);
+ if ( value != null)
+ {
+ fBytes[ insertion + 1 + keyBytes.length] = kAttrSep;
+ System.arraycopy( value, 0, fBytes, insertion + keyBytes.length + 2, valLen);
+ }
+ }
+
+ /** Remove a key/value pair from the TXT record. Returns index it was at, or -1 if not found. */
+ public int remove( String key)
+ {
+ int avStart = 0;
+
+ for ( int i=0; avStart < fBytes.length; i++)
+ {
+ int avLen = fBytes[ avStart];
+ if ( key.length() <= avLen &&
+ ( key.length() == avLen || fBytes[ avStart + key.length() + 1] == kAttrSep))
+ {
+ String s = new String( fBytes, avStart + 1, key.length());
+ if ( 0 == key.compareToIgnoreCase( s))
+ {
+ byte[] oldBytes = fBytes;
+ fBytes = new byte[ oldBytes.length - avLen - 1];
+ System.arraycopy( oldBytes, 0, fBytes, 0, avStart);
+ System.arraycopy( oldBytes, avStart + avLen + 1, fBytes, avStart, oldBytes.length - avStart - avLen - 1);
+ return i;
+ }
+ }
+ avStart += avLen + 1;
+ }
+ return -1;
+ }
+
+ /** Return the number of keys in the TXT record. */
+ public int size()
+ {
+ int i, avStart;
+
+ for ( i=0, avStart=0; avStart < fBytes.length; i++)
+ avStart += fBytes[ avStart] + 1;
+ return i;
+ }
+
+ /** Return true if key is present in the TXT record, false if not. */
+ public boolean contains( String key)
+ {
+ String s = null;
+
+ for ( int i=0; null != ( s = this.getKey( i)); i++)
+ if ( 0 == key.compareToIgnoreCase( s))
+ return true;
+ return false;
+ }
+
+ /** Return a key in the TXT record by zero-based index. Returns null if index exceeds the total number of keys. */
+ public String getKey( int index)
+ {
+ int avStart = 0;
+
+ for ( int i=0; i < index && avStart < fBytes.length; i++)
+ avStart += fBytes[ avStart] + 1;
+
+ if ( avStart < fBytes.length)
+ {
+ int avLen = fBytes[ avStart];
+ int aLen = 0;
+
+ for ( aLen=0; aLen < avLen; aLen++)
+ if ( fBytes[ avStart + aLen + 1] == kAttrSep)
+ break;
+ return new String( fBytes, avStart + 1, aLen);
+ }
+ return null;
+ }
+
+ /**
+ Look up a key in the TXT record by zero-based index and return its value. <P>
+ Returns null if index exceeds the total number of keys.
+ Returns null if the key is present with no value.
+ */
+ public byte[] getValue( int index)
+ {
+ int avStart = 0;
+ byte[] value = null;
+
+ for ( int i=0; i < index && avStart < fBytes.length; i++)
+ avStart += fBytes[ avStart] + 1;
+
+ if ( avStart < fBytes.length)
+ {
+ int avLen = fBytes[ avStart];
+ int aLen = 0;
+
+ for ( aLen=0; aLen < avLen; aLen++)
+ {
+ if ( fBytes[ avStart + aLen + 1] == kAttrSep)
+ {
+ value = new byte[ avLen - aLen - 1];
+ System.arraycopy( fBytes, avStart + aLen + 2, value, 0, avLen - aLen - 1);
+ break;
+ }
+ }
+ }
+ return value;
+ }
+
+ /** Converts the result of getValue() to a string in the platform default character set. */
+ public String getValueAsString( int index)
+ {
+ byte[] value = this.getValue( index);
+ return value != null ? new String( value) : null;
+ }
+
+ /** Get the value associated with a key. Will be null if the key is not defined.
+ Array will have length 0 if the key is defined with an = but no value.<P>
+
+ @param forKey
+ The left-hand side of the key-value pair.
+ <P>
+ @return The binary representation of the value.
+ */
+ public byte[] getValue( String forKey)
+ {
+ String s = null;
+ int i;
+
+ for ( i=0; null != ( s = this.getKey( i)); i++)
+ if ( 0 == forKey.compareToIgnoreCase( s))
+ return this.getValue( i);
+ return null;
+ }
+
+ /** Converts the result of getValue() to a string in the platform default character set.<P>
+
+ @param forKey
+ The left-hand side of the key-value pair.
+ <P>
+ @return The value represented in the default platform character set.
+ */
+ public String getValueAsString( String forKey)
+ {
+ byte[] val = this.getValue( forKey);
+ return val != null ? new String( val) : null;
+ }
+
+ /** Return the contents of the TXT record as raw bytes. */
+ public byte[] getRawBytes() { return (byte[]) fBytes.clone(); }
+
+ /** Return a string representation of the object. */
+ public String toString()
+ {
+ String a, result = null;
+
+ for ( int i=0; null != ( a = this.getKey( i)); i++)
+ {
+ String av = String.valueOf( i) + "={" + a;
+ String val = this.getValueAsString( i);
+ if ( val != null)
+ av += "=" + val + "}";
+ else
+ av += "}";
+ if ( result == null)
+ result = av;
+ else
+ result = result + ", " + av;
+ }
+ return result != null ? result : "";
+ }
+}
+
diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/BrowserApp.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/BrowserApp.java
new file mode 100644
index 0000000000..381657151b
--- /dev/null
+++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/BrowserApp.java
@@ -0,0 +1,400 @@
+/* -*- Mode: Java; tab-width: 4 -*-
+ *
+ * Copyright (c) 2004 Apple Computer, 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.
+
+ BrowserApp demonstrates how to use DNS-SD to browse for and resolve services.
+
+ To do:
+ - display resolved TXTRecord
+
+ident "%Z%%M% %I% %E% SMI"
+
+ */
+
+
+import java.awt.*;
+import java.awt.event.*;
+import java.util.*;
+import java.text.*;
+import javax.swing.*;
+import javax.swing.event.*;
+
+import com.apple.dnssd.*;
+
+
+class BrowserApp implements ListSelectionListener, ResolveListener
+{
+ static BrowserApp app;
+ JFrame frame;
+ DomainListModel domainList;
+ BrowserListModel servicesList, serviceList;
+ JList domainPane, servicesPane, servicePane;
+ DNSSDService servicesBrowser, serviceBrowser, domainBrowser;
+ JLabel hostLabel, portLabel;
+
+ public BrowserApp()
+ {
+ frame = new JFrame("DNS-SD Service Browser");
+ frame.addWindowListener(new WindowAdapter() {
+ public void windowClosing(WindowEvent e) {System.exit(0);}
+ });
+
+ domainList = new DomainListModel();
+ servicesList = new ServicesBrowserListModel();
+ serviceList = new BrowserListModel();
+
+ try {
+ domainBrowser = DNSSD.enumerateDomains( DNSSD.BROWSE_DOMAINS, 0, domainList);
+
+ servicesBrowser = DNSSD.browse( 0, 0, "_services._dns-sd._udp.", "", servicesList);
+ serviceBrowser = null;
+ }
+ catch ( Exception ex) { terminateWithException( ex); }
+
+ this.setupSubPanes( frame.getContentPane());
+ frame.pack();
+ frame.setVisible(true);
+ }
+
+ protected void setupSubPanes( Container parent)
+ {
+ parent.setLayout( new BoxLayout( parent, BoxLayout.Y_AXIS));
+
+ JPanel browserRow = new JPanel();
+ browserRow.setLayout( new BoxLayout( browserRow, BoxLayout.X_AXIS));
+ domainPane = new JList( domainList);
+ domainPane.addListSelectionListener( this);
+ JScrollPane domainScroller = new JScrollPane( domainPane, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
+ browserRow.add( domainScroller);
+ servicesPane = new JList( servicesList);
+ servicesPane.addListSelectionListener( this);
+ JScrollPane servicesScroller = new JScrollPane( servicesPane, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
+ browserRow.add( servicesScroller);
+ servicePane = new JList( serviceList);
+ servicePane.addListSelectionListener( this);
+ JScrollPane serviceScroller = new JScrollPane( servicePane, JScrollPane.VERTICAL_SCROLLBAR_ALWAYS, JScrollPane.HORIZONTAL_SCROLLBAR_ALWAYS);
+ browserRow.add( serviceScroller);
+
+/*
+ JPanel buttonRow = new JPanel();
+ buttonRow.setLayout( new BoxLayout( buttonRow, BoxLayout.X_AXIS));
+ buttonRow.add( Box.createHorizontalGlue());
+ JButton connectButton = new JButton( "Don't Connect");
+ buttonRow.add( connectButton);
+ buttonRow.add( Box.createRigidArea( new Dimension( 16, 0)));
+*/
+
+ JPanel labelRow = new JPanel();
+ labelRow.setLayout( new BoxLayout( labelRow, BoxLayout.X_AXIS));
+ labelRow.add( new JLabel( " Host: "));
+ hostLabel = new JLabel();
+ labelRow.add( hostLabel);
+ labelRow.add( Box.createRigidArea( new Dimension( 32, 0)));
+ labelRow.add( new JLabel( "Port: "));
+ portLabel = new JLabel();
+ labelRow.add( portLabel);
+ labelRow.add( Box.createHorizontalGlue());
+
+ parent.add( browserRow);
+ parent.add( Box.createRigidArea( new Dimension( 0, 8)));
+ parent.add( labelRow);
+// parent.add( buttonRow);
+ parent.add( Box.createRigidArea( new Dimension( 0, 16)));
+ }
+
+ public void valueChanged( ListSelectionEvent e)
+ {
+ try {
+ if ( e.getSource() == domainPane && !e.getValueIsAdjusting())
+ {
+ int newSel = domainPane.getSelectedIndex();
+ if ( -1 != newSel)
+ {
+ if ( serviceBrowser != null)
+ serviceBrowser.stop();
+ serviceList.removeAllElements();
+ servicesBrowser = DNSSD.browse( 0, 0, "_services._dns-sd._udp.", "", servicesList);
+ }
+ }
+ else if ( e.getSource() == servicesPane && !e.getValueIsAdjusting())
+ {
+ int newSel = servicesPane.getSelectedIndex();
+ if ( serviceBrowser != null)
+ serviceBrowser.stop();
+ serviceList.removeAllElements();
+ if ( -1 != newSel)
+ serviceBrowser = DNSSD.browse( 0, 0, servicesList.getNthRegType( newSel), "", serviceList);
+ }
+ else if ( e.getSource() == servicePane && !e.getValueIsAdjusting())
+ {
+ int newSel = servicePane.getSelectedIndex();
+
+ hostLabel.setText( "");
+ portLabel.setText( "");
+
+ if ( -1 != newSel)
+ {
+ DNSSD.resolve( 0, serviceList.getNthInterface( newSel),
+ serviceList.getNthServiceName( newSel),
+ serviceList.getNthRegType( newSel),
+ serviceList.getNthDomain( newSel),
+ new SwingResolveListener( this));
+ }
+ }
+ }
+ catch ( Exception ex) { terminateWithException( ex); }
+ }
+
+ public void serviceResolved( DNSSDService resolver, int flags, int ifIndex, String fullName,
+ String hostName, int port, TXTRecord txtRecord)
+ {
+ hostLabel.setText( hostName);
+ portLabel.setText( String.valueOf( port));
+ }
+
+ public void operationFailed( DNSSDService service, int errorCode)
+ {
+ // handle failure here
+ }
+
+ protected static void terminateWithException( Exception e)
+ {
+ e.printStackTrace();
+ System.exit( -1);
+ }
+
+ public static void main(String s[])
+ {
+ app = new BrowserApp();
+ }
+}
+
+
+class BrowserListModel extends DefaultListModel implements BrowseListener, Runnable
+{
+ public BrowserListModel()
+ {
+ addCache = new Vector();
+ removeCache = new Vector();
+ }
+
+ /* The Browser invokes this callback when a service is discovered. */
+ public void serviceFound( DNSSDService browser, int flags, int ifIndex,
+ String serviceName, String regType, String domain)
+ {
+ addCache.add( new BrowserListElem( serviceName, domain, regType, ifIndex));
+ if ( ( flags & DNSSD.MORE_COMING) == 0)
+ this.scheduleOnEventThread();
+ }
+
+ public void serviceLost( DNSSDService browser, int flags, int ifIndex,
+ String serviceName, String regType, String domain)
+ {
+ removeCache.add( serviceName);
+ if ( ( flags & DNSSD.MORE_COMING) == 0)
+ this.scheduleOnEventThread();
+ }
+
+ public void run()
+ {
+ while ( removeCache.size() > 0)
+ {
+ String serviceName = (String) removeCache.remove( removeCache.size() - 1);
+ int matchInd = this.findMatching( serviceName); // probably doesn't handle near-duplicates well.
+ if ( matchInd != -1)
+ this.removeElementAt( matchInd);
+ }
+ while ( addCache.size() > 0)
+ {
+ BrowserListElem elem = (BrowserListElem) addCache.remove( addCache.size() - 1);
+ if ( -1 == this.findMatching( elem.fServiceName)) // probably doesn't handle near-duplicates well.
+ this.addInSortOrder( elem);
+ }
+ }
+
+ public void operationFailed( DNSSDService service, int errorCode)
+ {
+ // handle failure here
+ }
+
+ /* The list contains BrowserListElem's */
+ class BrowserListElem
+ {
+ public BrowserListElem( String serviceName, String domain, String type, int ifIndex)
+ { fServiceName = serviceName; fDomain = domain; fType = type; fInt = ifIndex; }
+
+ public String toString() { return fServiceName; }
+
+ public String fServiceName, fDomain, fType;
+ public int fInt;
+ }
+
+ public String getNthServiceName( int n)
+ {
+ BrowserListElem sel = (BrowserListElem) this.get( n);
+ return sel.fServiceName;
+ }
+
+ public String getNthRegType( int n)
+ {
+ BrowserListElem sel = (BrowserListElem) this.get( n);
+ return sel.fType;
+ }
+
+ public String getNthDomain( int n)
+ {
+ BrowserListElem sel = (BrowserListElem) this.get( n);
+ return sel.fDomain;
+ }
+
+ public int getNthInterface( int n)
+ {
+ BrowserListElem sel = (BrowserListElem) this.get( n);
+ return sel.fInt;
+ }
+
+ protected void addInSortOrder( Object obj)
+ {
+ int i;
+ for ( i = 0; i < this.size(); i++)
+ if ( sCollator.compare( obj.toString(), this.getElementAt( i).toString()) < 0)
+ break;
+ this.add( i, obj);
+ }
+
+ protected int findMatching( String match)
+ {
+ for ( int i = 0; i < this.size(); i++)
+ if ( match.equals( this.getElementAt( i).toString()))
+ return i;
+ return -1;
+ }
+
+ protected void scheduleOnEventThread()
+ {
+ try {
+ SwingUtilities.invokeAndWait( this);
+ }
+ catch ( Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ protected Vector removeCache; // list of serviceNames to remove
+ protected Vector addCache; // list of BrowserListElem's to add
+
+ protected static Collator sCollator;
+
+ static // Initialize our static variables
+ {
+ sCollator = Collator.getInstance();
+ sCollator.setStrength( Collator.PRIMARY);
+ }
+}
+
+
+class ServicesBrowserListModel extends BrowserListModel
+{
+ /* The Browser invokes this callback when a service is discovered. */
+ public void serviceFound( DNSSDService browser, int flags, int ifIndex,
+ String serviceName, String regType, String domain)
+ // Overridden to stuff serviceName into regType and make serviceName human-readable.
+ {
+ regType = serviceName + ( regType.startsWith( "_udp.") ? "._udp." : "._tcp.");
+ super.serviceFound( browser, flags, ifIndex, this.mapTypeToName( serviceName), regType, domain);
+ }
+
+ public void serviceLost( DNSSDService browser, int flags, int ifIndex,
+ String serviceName, String regType, String domain)
+ // Overridden to make serviceName human-readable.
+ {
+ super.serviceLost( browser, flags, ifIndex, this.mapTypeToName( serviceName), regType, domain);
+ }
+
+ protected String mapTypeToName( String type)
+ // Convert a registration type into a human-readable string. Returns original string on no-match.
+ {
+ final String[] namedServices = {
+ "_afpovertcp", "Apple File Sharing",
+ "_http", "World Wide Web servers",
+ "_daap", "Digital Audio Access",
+ "_apple-sasl", "Apple Password Servers",
+ "_distcc", "Distributed Compiler nodes",
+ "_finger", "Finger servers",
+ "_ichat", "iChat clients",
+ "_presence", "iChat AV clients",
+ "_ssh", "SSH servers",
+ "_telnet", "Telnet servers",
+ "_workstation", "Macintosh Manager clients",
+ "_bootps", "BootP servers",
+ "_xserveraid", "XServe RAID devices",
+ "_eppc", "Remote AppleEvents",
+ "_ftp", "FTP services",
+ "_tftp", "TFTP services"
+ };
+
+ for ( int i = 0; i < namedServices.length; i+=2)
+ if ( namedServices[i].equals( type))
+ return namedServices[i + 1];
+ return type;
+ }
+}
+
+
+class DomainListModel extends DefaultListModel implements DomainListener
+{
+ /* Called when a domain is discovered. */
+ public void domainFound( DNSSDService domainEnum, int flags, int ifIndex, String domain)
+ {
+ if ( !this.contains( domain))
+ this.addElement( domain);
+ }
+
+ public void domainLost( DNSSDService domainEnum, int flags, int ifIndex, String domain)
+ {
+ if ( this.contains( domain))
+ this.removeElement( domain);
+ }
+
+ public void operationFailed( DNSSDService service, int errorCode)
+ {
+ // handle failure here
+ }
+}
+
diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/BrowserApp.manifest b/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/BrowserApp.manifest
new file mode 100644
index 0000000000..472b17f638
--- /dev/null
+++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/BrowserApp.manifest
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Main-Class: BrowserApp
+Class-Path: /usr/share/lib/java/dnssd.jar
diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SimpleChat.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SimpleChat.java
new file mode 100644
index 0000000000..62dd11faa5
--- /dev/null
+++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SimpleChat.java
@@ -0,0 +1,336 @@
+/* -*- Mode: Java; tab-width: 4 -*-
+ *
+ * Copyright (c) 2004 Apple Computer, 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.
+
+ SimpleChat is a simple peer-to-peer chat program that demonstrates
+ DNS-SD registration, browsing, resolving and record-querying.
+
+ To do:
+ - implement better coloring algorithm
+
+ident "%Z%%M% %I% %E% SMI"
+
+ */
+
+
+import java.awt.*;
+import java.awt.event.*;
+import java.text.*;
+import java.net.*;
+import javax.swing.*;
+import javax.swing.event.*;
+import javax.swing.text.*;
+
+import com.apple.dnssd.*;
+
+
+class SimpleChat implements ResolveListener, RegisterListener, QueryListener,
+ ActionListener, ItemListener, Runnable
+{
+ Document textDoc; // Holds all the chat text
+ JTextField inputField; // Holds a pending chat response
+ String ourName; // name used to identify this user in chat
+ DNSSDService browser; // object that actively browses for other chat clients
+ DNSSDService resolver; // object that resolves other chat clients
+ DNSSDRegistration registration; // object that maintains our connection advertisement
+ JComboBox targetPicker; // Indicates who we're talking to
+ TargetListModel targetList; // and its list model
+ JButton sendButton; // Will send text in inputField to target
+ InetAddress buddyAddr; // and address
+ int buddyPort; // and port
+ DatagramPacket dataPacket; // Inbound data packet
+ DatagramSocket outSocket; // Outbound data socket
+ SimpleAttributeSet textAttribs;
+
+ static final String kChatExampleRegType = "_p2pchat._udp";
+ static final String kWireCharSet = "ISO-8859-1";
+
+ public SimpleChat() throws Exception
+ {
+ JFrame frame = new JFrame("SimpleChat");
+ frame.addWindowListener(new WindowAdapter() {
+ public void windowClosing(WindowEvent e) {System.exit(0);}
+ });
+
+ ourName = System.getProperty( "user.name");
+ targetList = new TargetListModel();
+ textAttribs = new SimpleAttributeSet();
+ DatagramSocket inSocket = new DatagramSocket();
+ dataPacket = new DatagramPacket( new byte[ 4096], 4096);
+ outSocket = new DatagramSocket();
+
+ this.setupSubPanes( frame.getContentPane(), frame.getRootPane());
+ frame.pack();
+ frame.setVisible(true);
+ inputField.requestFocusInWindow();
+
+ browser = DNSSD.browse( 0, 0, kChatExampleRegType, "", new SwingBrowseListener( targetList));
+
+ registration = DNSSD.register( 0, 0, ourName, kChatExampleRegType, "", "", inSocket.getLocalPort(), null, this);
+
+ new ListenerThread( this, inSocket, dataPacket).start();
+ }
+
+ protected void setupSubPanes( Container parent, JRootPane rootPane)
+ {
+ parent.setLayout( new BoxLayout( parent, BoxLayout.Y_AXIS));
+
+ JPanel textRow = new JPanel();
+ textRow.setLayout( new BoxLayout( textRow, BoxLayout.X_AXIS));
+ textRow.add( Box.createRigidArea( new Dimension( 16, 0)));
+ JEditorPane textPane = new JEditorPane( "text/html", "<BR>");
+ textPane.setPreferredSize( new Dimension( 400, 300));
+ textPane.setEditable( false);
+ JScrollPane textScroller = new JScrollPane( textPane);
+ textRow.add( textScroller);
+ textRow.add( Box.createRigidArea( new Dimension( 16, 0)));
+ textDoc = textPane.getDocument();
+
+ JPanel addressRow = new JPanel();
+ addressRow.setLayout( new BoxLayout( addressRow, BoxLayout.X_AXIS));
+ targetPicker = new JComboBox( targetList);
+ targetPicker.addItemListener( this);
+ addressRow.add( Box.createRigidArea( new Dimension( 16, 0)));
+ addressRow.add( new JLabel( "Talk to: "));
+ addressRow.add( targetPicker);
+ addressRow.add( Box.createHorizontalGlue());
+
+ JPanel buttonRow = new JPanel();
+ buttonRow.setLayout( new BoxLayout( buttonRow, BoxLayout.X_AXIS));
+ buttonRow.add( Box.createRigidArea( new Dimension( 16, 0)));
+ inputField = new JTextField();
+ // prevent inputField from hijacking <Enter> key
+ inputField.getKeymap().removeKeyStrokeBinding( KeyStroke.getKeyStroke( KeyEvent.VK_ENTER, 0));
+ buttonRow.add( inputField);
+ sendButton = new JButton( "Send");
+ buttonRow.add( Box.createRigidArea( new Dimension( 8, 0)));
+ buttonRow.add( sendButton);
+ buttonRow.add( Box.createRigidArea( new Dimension( 16, 0)));
+ rootPane.setDefaultButton( sendButton);
+ sendButton.addActionListener( this);
+ sendButton.setEnabled( false);
+
+ parent.add( Box.createRigidArea( new Dimension( 0, 16)));
+ parent.add( textRow);
+ parent.add( Box.createRigidArea( new Dimension( 0, 8)));
+ parent.add( addressRow);
+ parent.add( Box.createRigidArea( new Dimension( 0, 8)));
+ parent.add( buttonRow);
+ parent.add( Box.createRigidArea( new Dimension( 0, 16)));
+ }
+
+ public void serviceRegistered( DNSSDRegistration registration, int flags,
+ String serviceName, String regType, String domain)
+ {
+ ourName = serviceName; // might have been renamed on collision
+ }
+
+ public void operationFailed( DNSSDService service, int errorCode)
+ {
+ System.out.println( "Service reported error " + String.valueOf( errorCode));
+ }
+
+ public void serviceResolved( DNSSDService resolver, int flags, int ifIndex, String fullName,
+ String hostName, int port, TXTRecord txtRecord)
+ {
+ buddyPort = port;
+ try {
+ // Start a record query to obtain IP address from hostname
+ DNSSD.queryRecord( 0, ifIndex, hostName, 1 /* ns_t_a */, 1 /* ns_c_in */,
+ new SwingQueryListener( this));
+ }
+ catch ( Exception e) { terminateWithException( e); }
+ resolver.stop();
+ }
+
+ public void queryAnswered( DNSSDService query, int flags, int ifIndex, String fullName,
+ int rrtype, int rrclass, byte[] rdata, int ttl)
+ {
+ try {
+ buddyAddr = InetAddress.getByAddress( rdata);
+ }
+ catch ( Exception e) { terminateWithException( e); }
+ sendButton.setEnabled( true);
+ }
+
+ public void actionPerformed( ActionEvent e) // invoked when Send button is hit
+ {
+ try
+ {
+ String sendString = ourName + ": " + inputField.getText();
+ byte[] sendData = sendString.getBytes( kWireCharSet);
+ outSocket.send( new DatagramPacket( sendData, sendData.length, buddyAddr, buddyPort));
+ StyleConstants.setForeground( textAttribs, Color.black);
+ textDoc.insertString( textDoc.getLength(), inputField.getText() + "\n", textAttribs);
+ inputField.setText( "");
+ }
+ catch ( Exception exception) { terminateWithException( exception); }
+ }
+
+ public void itemStateChanged( ItemEvent e) // invoked when Target selection changes
+ {
+ sendButton.setEnabled( false);
+ if ( e.getStateChange() == ItemEvent.SELECTED)
+ {
+ try {
+ TargetListElem sel = (TargetListElem) targetList.getSelectedItem();
+ resolver = DNSSD.resolve( 0, sel.fInt, sel.fServiceName, sel.fType, sel.fDomain, this);
+ }
+ catch ( Exception exception) { terminateWithException( exception); }
+ }
+ }
+
+ public void run() // invoked on event thread when inbound packet arrives
+ {
+ try
+ {
+ String inMessage = new String( dataPacket.getData(), 0, dataPacket.getLength(), kWireCharSet);
+ StyleConstants.setForeground( textAttribs, this.getColorFor( dataPacket.getData(), dataPacket.getLength()));
+ textDoc.insertString( textDoc.getLength(), inMessage + "\n", textAttribs);
+ }
+ catch ( Exception e) { terminateWithException( e); }
+ }
+
+ protected Color getColorFor( byte[] chars, int length)
+ // Produce a mapping from a string to a color, suitable for text display
+ {
+ int rgb = 0;
+ for ( int i=0; i < length && chars[i] != ':'; i++)
+ rgb = rgb ^ ( (int) chars[i] << (i%3+2) * 8);
+ return new Color( rgb & 0x007F7FFF); // mask off high bits so it is a dark color
+
+// for ( int i=0; i < length && chars[i] != ':'; i++)
+
+ }
+
+ protected static void terminateWithException( Exception e)
+ {
+ e.printStackTrace();
+ System.exit( -1);
+ }
+
+ public static void main(String s[])
+ {
+ try {
+ new SimpleChat();
+ }
+ catch ( Exception e) { terminateWithException( e); }
+ }
+}
+
+
+
+class TargetListElem
+{
+ public TargetListElem( String serviceName, String domain, String type, int ifIndex)
+ { fServiceName = serviceName; fDomain = domain; fType = type; fInt = ifIndex; }
+
+ public String toString() { return fServiceName; }
+
+ public String fServiceName, fDomain, fType;
+ public int fInt;
+}
+
+class TargetListModel extends DefaultComboBoxModel implements BrowseListener
+{
+ /* The Browser invokes this callback when a service is discovered. */
+ public void serviceFound( DNSSDService browser, int flags, int ifIndex,
+ String serviceName, String regType, String domain)
+ {
+ TargetListElem match = this.findMatching( serviceName); // probably doesn't handle near-duplicates well.
+
+ if ( match == null)
+ this.addElement( new TargetListElem( serviceName, domain, regType, ifIndex));
+ }
+
+ /* The Browser invokes this callback when a service disappears. */
+ public void serviceLost( DNSSDService browser, int flags, int ifIndex,
+ String serviceName, String regType, String domain)
+ {
+ TargetListElem match = this.findMatching( serviceName); // probably doesn't handle near-duplicates well.
+
+ if ( match != null)
+ this.removeElement( match);
+ }
+
+ /* The Browser invokes this callback when a service disappears. */
+ public void operationFailed( DNSSDService service, int errorCode)
+ {
+ System.out.println( "Service reported error " + String.valueOf( errorCode));
+ }
+
+ protected TargetListElem findMatching( String match)
+ {
+ for ( int i = 0; i < this.getSize(); i++)
+ if ( match.equals( this.getElementAt( i).toString()))
+ return (TargetListElem) this.getElementAt( i);
+ return null;
+ }
+
+}
+
+
+// A ListenerThread runs its owner when datagram packet p appears on socket s.
+class ListenerThread extends Thread
+{
+ public ListenerThread( Runnable owner, DatagramSocket s, DatagramPacket p)
+ { fOwner = owner; fSocket = s; fPacket = p; }
+
+ public void run()
+ {
+ while ( true )
+ {
+ try
+ {
+ fSocket.receive( fPacket);
+ SwingUtilities.invokeAndWait( fOwner); // process data on main thread
+ }
+ catch( Exception e)
+ {
+ break; // terminate thread
+ }
+ }
+ }
+
+ protected Runnable fOwner;
+ protected DatagramSocket fSocket;
+ protected DatagramPacket fPacket;
+}
+
+
+
diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SimpleChat.manifest b/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SimpleChat.manifest
new file mode 100644
index 0000000000..71ca6a754e
--- /dev/null
+++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SimpleChat.manifest
@@ -0,0 +1,3 @@
+Manifest-Version: 1.0
+Main-Class: SimpleChat
+Class-Path: /usr/share/lib/java/dnssd.jar
diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SwingBrowseListener.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SwingBrowseListener.java
new file mode 100644
index 0000000000..c8ff1e6ca1
--- /dev/null
+++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SwingBrowseListener.java
@@ -0,0 +1,126 @@
+/* -*- Mode: Java; tab-width: 4 -*-
+ *
+ * Copyright (c) 2004 Apple Computer, 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.
+
+ident "%Z%%M% %I% %E% SMI"
+
+ */
+
+import javax.swing.*;
+import com.apple.dnssd.*;
+
+
+/** Use this to schedule BrowseListener callbacks via SwingUtilities.invokeAndWait(). */
+
+public class SwingBrowseListener implements Runnable, BrowseListener
+{
+ /** Create a listener for DNSSD that will call your listener on the Swing/AWT event thread. */
+ public SwingBrowseListener( BrowseListener listener)
+ { fListener = listener; fErrorCode = 0; }
+
+ /** (Clients should not call this method directly.) */
+ public void operationFailed( DNSSDService service, int errorCode)
+ {
+ fBrowser = service;
+ fErrorCode = errorCode;
+ this.schedule();
+ }
+
+ /** (Clients should not call this method directly.) */
+ public void serviceFound( DNSSDService browser, int flags, int ifIndex,
+ String serviceName, String regType, String domain)
+
+ {
+ fBrowser = browser;
+ fIsAdd = true;
+ fFlags = flags;
+ fIndex = ifIndex;
+ fService = serviceName;
+ fRegType = regType;
+ fDomain = domain;
+ this.schedule();
+ }
+
+ /** (Clients should not call this method directly.) */
+ public void serviceLost( DNSSDService browser, int flags, int ifIndex,
+ String serviceName, String regType, String domain)
+ {
+ fBrowser = browser;
+ fIsAdd = false;
+ fFlags = flags;
+ fIndex = ifIndex;
+ fService = serviceName;
+ fRegType = regType;
+ fDomain = domain;
+ this.schedule();
+ }
+
+ /** (Clients should not call this method directly.) */
+ public void run()
+ {
+ if ( fErrorCode != 0)
+ fListener.operationFailed( fBrowser, fErrorCode);
+ else if ( fIsAdd)
+ fListener.serviceFound( fBrowser, fFlags, fIndex, fService, fRegType, fDomain);
+ else
+ fListener.serviceLost( fBrowser, fFlags, fIndex, fService, fRegType, fDomain);
+ }
+
+ protected void schedule()
+ {
+ try {
+ SwingUtilities.invokeAndWait( this);
+ }
+ catch ( Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ protected BrowseListener fListener;
+
+ protected boolean fIsAdd;
+ protected DNSSDService fBrowser;
+ protected int fFlags;
+ protected int fIndex;
+ protected int fErrorCode;
+ protected String fService;
+ protected String fRegType;
+ protected String fDomain;
+}
+
diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SwingDomainListener.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SwingDomainListener.java
new file mode 100644
index 0000000000..988591ccfc
--- /dev/null
+++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SwingDomainListener.java
@@ -0,0 +1,119 @@
+/* -*- Mode: Java; tab-width: 4 -*-
+ *
+ * Copyright (c) 2004 Apple Computer, 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.
+
+ident "%Z%%M% %I% %E% SMI"
+
+ */
+
+
+import javax.swing.*;
+import com.apple.dnssd.*;
+
+
+/** Use this to schedule DomainListener callbacks via SwingUtilities.invokeAndWait(). */
+
+public class SwingDomainListener implements Runnable, DomainListener
+{
+ /** Create a listener for DNSSD that will call your listener on the Swing/AWT event thread. */
+ public SwingDomainListener( DomainListener listener)
+ { fListener = listener; fErrorCode = 0; }
+
+ /** (Clients should not call this method directly.) */
+ public void operationFailed( DNSSDService service, int errorCode)
+ {
+ fEnumerator = service;
+ fErrorCode = errorCode;
+ this.schedule();
+ }
+
+ /** (Clients should not call this method directly.) */
+ public void domainFound( DNSSDService domainEnum, int flags, int ifIndex, String domain)
+
+ {
+ fEnumerator = domainEnum;
+ fIsAdd = true;
+ fFlags = flags;
+ fIndex = ifIndex;
+ fDomain = domain;
+ this.schedule();
+ }
+
+ /** (Clients should not call this method directly.) */
+ public void domainLost( DNSSDService domainEnum, int flags, int ifIndex, String domain)
+ {
+ fEnumerator = domainEnum;
+ fIsAdd = false;
+ fFlags = flags;
+ fIndex = ifIndex;
+ fDomain = domain;
+ this.schedule();
+ }
+
+ /** (Clients should not call this method directly.) */
+ public void run()
+ {
+ if ( fErrorCode != 0)
+ fListener.operationFailed( fEnumerator, fErrorCode);
+ else if ( fIsAdd)
+ fListener.domainFound( fEnumerator, fFlags, fIndex, fDomain);
+ else
+ fListener.domainLost( fEnumerator, fFlags, fIndex, fDomain);
+ }
+
+ protected void schedule()
+ {
+ try {
+ SwingUtilities.invokeAndWait( this);
+ }
+ catch ( Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ protected DomainListener fListener;
+
+ protected boolean fIsAdd;
+ protected DNSSDService fEnumerator;
+ protected int fFlags;
+ protected int fIndex;
+ protected int fErrorCode;
+ protected String fDomain;
+}
+
diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SwingQueryListener.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SwingQueryListener.java
new file mode 100644
index 0000000000..1b301ce8e9
--- /dev/null
+++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SwingQueryListener.java
@@ -0,0 +1,111 @@
+/* -*- Mode: Java; tab-width: 4 -*-
+ *
+ * Copyright (c) 2004 Apple Computer, 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.
+
+ident "%Z%%M% %I% %E% SMI"
+
+ */
+
+
+import javax.swing.*;
+import com.apple.dnssd.*;
+
+
+/** Use this to schedule QueryListener callbacks via SwingUtilities.invokeAndWait(). */
+
+public class SwingQueryListener implements Runnable, QueryListener
+{
+ /** Create a listener for DNSSD that will call your listener on the Swing/AWT event thread. */
+ public SwingQueryListener( QueryListener listener)
+ { fListener = listener; }
+
+ public void operationFailed( DNSSDService service, int errorCode)
+ {
+ fQuery = service;
+ fErrorCode = errorCode;
+ this.schedule();
+ }
+
+ /** (Clients should not call this method directly.) */
+ public void queryAnswered( DNSSDService query, int flags, int ifIndex, String fullName,
+ int rrtype, int rrclass, byte[] rdata, int ttl)
+ {
+ fQuery = query;
+ fFlags = flags;
+ fIndex = ifIndex;
+ fFullName = fullName;
+ fType = rrtype;
+ fClass = rrclass;
+ fData = rdata;
+ fTTL = ttl;
+ this.schedule();
+ }
+
+ /** (Clients should not call this method directly.) */
+ public void run()
+ {
+ if ( fErrorCode != 0)
+ fListener.operationFailed( fQuery, fErrorCode);
+ else
+ fListener.queryAnswered( fQuery, fFlags, fIndex, fFullName, fType, fClass, fData, fTTL);
+ }
+
+ protected void schedule()
+ {
+ try {
+ SwingUtilities.invokeAndWait( this);
+ }
+ catch ( Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ protected QueryListener fListener;
+
+ protected DNSSDService fQuery;
+ protected int fFlags;
+ protected int fIndex;
+ protected int fErrorCode;
+ protected String fFullName;
+ protected int fType;
+ protected int fClass;
+ protected byte[] fData;
+ protected int fTTL;
+}
+
diff --git a/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SwingResolveListener.java b/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SwingResolveListener.java
new file mode 100644
index 0000000000..499da0707d
--- /dev/null
+++ b/usr/src/lib/libdns_sd/java/com/apple/dnssd/docs/examples/src/SwingResolveListener.java
@@ -0,0 +1,109 @@
+/* -*- Mode: Java; tab-width: 4 -*-
+ *
+ * Copyright (c) 2004 Apple Computer, 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.
+
+ident "%Z%%M% %I% %E% SMI"
+
+ */
+
+
+import javax.swing.*;
+import com.apple.dnssd.*;
+
+
+/** Use this to schedule ResolveListener callbacks via SwingUtilities.invokeAndWait(). */
+
+public class SwingResolveListener implements Runnable, ResolveListener
+{
+ /** Create a listener for DNSSD that will call your listener on the Swing/AWT event thread. */
+ public SwingResolveListener( ResolveListener listener)
+ { fListener = listener; }
+
+ public void operationFailed( DNSSDService service, int errorCode)
+ {
+ fResolver = service;
+ fErrorCode = errorCode;
+ this.schedule();
+ }
+
+ /** (Clients should not call this method directly.) */
+ public void serviceResolved( DNSSDService resolver, int flags, int ifIndex, String fullName,
+ String hostName, int port, TXTRecord txtRecord)
+ {
+ fResolver = resolver;
+ fFlags = flags;
+ fIndex = ifIndex;
+ fFullName = fullName;
+ fHostName = hostName;
+ fPort = port;
+ fTXTRecord = txtRecord;
+ this.schedule();
+ }
+
+ /** (Clients should not call this method directly.) */
+ public void run()
+ {
+ if ( fErrorCode != 0)
+ fListener.operationFailed( fResolver, fErrorCode);
+ else
+ fListener.serviceResolved( fResolver, fFlags, fIndex, fFullName, fHostName, fPort, fTXTRecord);
+ }
+
+ protected void schedule()
+ {
+ try {
+ SwingUtilities.invokeAndWait( this);
+ }
+ catch ( Exception e)
+ {
+ e.printStackTrace();
+ }
+ }
+
+ protected ResolveListener fListener;
+
+ protected DNSSDService fResolver;
+ protected int fFlags;
+ protected int fIndex;
+ protected int fErrorCode;
+ protected String fFullName;
+ protected String fHostName;
+ protected int fPort;
+ protected TXTRecord fTXTRecord;
+}
+
diff --git a/usr/src/lib/libdns_sd/java/common/JNISupport.c b/usr/src/lib/libdns_sd/java/common/JNISupport.c
new file mode 100644
index 0000000000..bdf453475d
--- /dev/null
+++ b/usr/src/lib/libdns_sd/java/common/JNISupport.c
@@ -0,0 +1,1107 @@
+/* -*- 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.
+
+ Change History (most recent first):
+
+$Log: JNISupport.c,v $
+Revision 1.17 2006/08/14 23:25:08 cheshire
+Re-licensed mDNSResponder daemon source code under Apache License, Version 2.0
+
+Revision 1.16 2006/07/14 02:35:47 cheshire
+Added (commented out) syslog debugging messages
+
+Revision 1.15 2006/06/27 19:34:43 cheshire
+<rdar://problem/4430023> txtRecord parameter of DNSServiceResolveReply() should be unsigned char *
+
+Revision 1.14 2006/06/20 23:03:35 rpantos
+<rdar://problem/3839132> Java needs to implement DNSServiceRegisterRecord equivalent
+
+Revision 1.13 2005/10/26 01:52:24 cheshire
+<rdar://problem/4316286> Race condition in Java code (doesn't work at all on Linux)
+
+Revision 1.12 2005/07/13 19:20:32 cheshire
+<rdar://problem/4175511> Race condition in Java API
+Additional cleanup suggested by Roger -- NewContext() doesn't need ownerClass parameter any more
+
+Revision 1.11 2005/07/11 01:55:21 cheshire
+<rdar://problem/4175511> Race condition in Java API
+
+Revision 1.10 2005/07/05 13:01:52 cheshire
+<rdar://problem/4169791> If mDNSResponder daemon is stopped, Java API spins, burning CPU time
+
+Revision 1.9 2004/12/11 03:01:00 rpantos
+<rdar://problem/3907498> Java DNSRecord API should be cleaned up
+
+Revision 1.8 2004/11/30 23:51:05 cheshire
+Remove double semicolons
+
+Revision 1.7 2004/11/23 08:12:04 shersche
+Implement if_nametoindex and if_indextoname for Win32 platforms
+
+Revision 1.6 2004/11/23 03:41:14 cheshire
+Change JNISupport.c to call if_indextoname & if_nametoindex directly.
+(May require some additional glue code to work on Windows.)
+
+Revision 1.5 2004/11/17 17:07:44 cheshire
+Updated comment about AUTO_CALLBACKS
+
+Revision 1.4 2004/11/12 03:23:09 rpantos
+rdar://problem/3809541 implement getIfIndexForName, getNameForIfIndex.
+
+Revision 1.3 2004/06/18 04:44:17 rpantos
+Adapt to API unification on Windows
+
+Revision 1.2 2004/05/28 23:34:42 ksekar
+<rdar://problem/3672903>: Java project build errors
+
+Revision 1.1 2004/04/30 16:29:35 rpantos
+First checked in.
+
+
+ This file contains the platform support for DNSSD and related Java classes.
+ It is used to shim through to the underlying <dns_sd.h> API.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+// AUTO_CALLBACKS should be set to 1 if the underlying mDNS implementation fires response
+// callbacks automatically (as in the early Windows prototypes).
+// AUTO_CALLBACKS should be set to 0 if the client must call DNSServiceProcessResult() to
+// invoke response callbacks (as is true on Mac OS X, Posix, Windows, etc.).
+// (Invoking callbacks automatically on a different thread sounds attractive, but while
+// the client gains by not needing to add an event source to its main event loop, it loses
+// by being forced to deal with concurrency and locking, which can be a bigger burden.)
+#ifndef AUTO_CALLBACKS
+#define AUTO_CALLBACKS 0
+#endif
+
+#if !AUTO_CALLBACKS
+#ifdef _WIN32
+#include <winsock2.h>
+#else //_WIN32
+#include <sys/types.h>
+#include <sys/select.h>
+#endif // _WIN32
+#endif // AUTO_CALLBACKS
+
+#include <dns_sd.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#ifdef _WIN32
+#include <winsock2.h>
+#include <iphlpapi.h>
+static char * if_indextoname( DWORD ifIndex, char * nameBuff);
+static DWORD if_nametoindex( const char * nameStr );
+#define IF_NAMESIZE MAX_ADAPTER_NAME_LENGTH
+#else // _WIN32
+#include <sys/socket.h>
+#include <net/if.h>
+#endif // _WIN32
+#include <jni.h>
+
+#include "DNSSD.java.h"
+
+//#include <syslog.h>
+
+// convenience definition
+#ifdef __GNUC__
+#define _UNUSED __attribute__ ((unused))
+#else
+#define _UNUSED
+#endif
+
+enum {
+ kInterfaceVersion = 1 // Must match version in .jar file
+};
+
+typedef struct OpContext OpContext;
+
+struct OpContext
+{
+ DNSServiceRef ServiceRef;
+ JNIEnv *Env;
+ jobject JavaObj;
+ jobject ClientObj;
+ jmethodID Callback;
+ jmethodID Callback2;
+};
+
+// For AUTO_CALLBACKS, we must attach the callback thread to the Java VM prior to upcall.
+#if AUTO_CALLBACKS
+JavaVM *gJavaVM = NULL;
+#endif
+
+
+JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleDNSSD_InitLibrary( JNIEnv *pEnv, jclass cls,
+ jint callerVersion)
+{
+ /* Ensure that caller & interface versions match. */
+ if ( callerVersion != kInterfaceVersion)
+ return kDNSServiceErr_Incompatible;
+
+#if AUTO_CALLBACKS
+ {
+ jsize numVMs;
+
+ if ( 0 != JNI_GetCreatedJavaVMs( &gJavaVM, 1, &numVMs))
+ return kDNSServiceErr_BadState;
+ }
+#endif
+
+ // Set AppleDNSSD.hasAutoCallbacks
+ {
+#if AUTO_CALLBACKS
+ jboolean hasAutoC = JNI_TRUE;
+#else
+ jboolean hasAutoC = JNI_FALSE;
+#endif
+ jfieldID hasAutoCField = (*pEnv)->GetStaticFieldID( pEnv, cls, "hasAutoCallbacks", "Z");
+ (*pEnv)->SetStaticBooleanField( pEnv, cls, hasAutoCField, hasAutoC);
+ }
+
+ return kDNSServiceErr_NoError;
+}
+
+
+static const char* SafeGetUTFChars( JNIEnv *pEnv, jstring str)
+// Wrapper for JNI GetStringUTFChars() that returns NULL for null str.
+{
+ return str != NULL ? (*pEnv)->GetStringUTFChars( pEnv, str, 0) : NULL;
+}
+
+static void SafeReleaseUTFChars( JNIEnv *pEnv, jstring str, const char *buff)
+// Wrapper for JNI GetStringUTFChars() that handles null str.
+{
+ if ( str != NULL)
+ (*pEnv)->ReleaseStringUTFChars( pEnv, str, buff);
+}
+
+
+#if AUTO_CALLBACKS
+static void SetupCallbackState( JNIEnv **ppEnv)
+{
+ (*gJavaVM)->AttachCurrentThread( gJavaVM, (void**) ppEnv, NULL);
+}
+
+static void TeardownCallbackState( void )
+{
+ (*gJavaVM)->DetachCurrentThread( gJavaVM);
+}
+
+#else // AUTO_CALLBACKS
+
+static void SetupCallbackState( JNIEnv **ppEnv _UNUSED)
+{
+ // No setup necessary if ProcessResults() has been called
+}
+
+static void TeardownCallbackState( void )
+{
+ // No teardown necessary if ProcessResults() has been called
+}
+#endif // AUTO_CALLBACKS
+
+
+static OpContext *NewContext( JNIEnv *pEnv, jobject owner,
+ const char *callbackName, const char *callbackSig)
+// Create and initialize a new OpContext.
+{
+ OpContext *pContext = (OpContext*) malloc( sizeof *pContext);
+
+ if ( pContext != NULL)
+ {
+ jfieldID clientField = (*pEnv)->GetFieldID( pEnv, (*pEnv)->GetObjectClass( pEnv, owner),
+ "fListener", "Lcom/apple/dnssd/BaseListener;");
+
+ pContext->JavaObj = (*pEnv)->NewWeakGlobalRef( pEnv, owner); // must convert local ref to global to cache;
+ pContext->ClientObj = (*pEnv)->GetObjectField( pEnv, owner, clientField);
+ pContext->ClientObj = (*pEnv)->NewWeakGlobalRef( pEnv, pContext->ClientObj); // must convert local ref to global to cache
+ pContext->Callback = (*pEnv)->GetMethodID( pEnv,
+ (*pEnv)->GetObjectClass( pEnv, pContext->ClientObj),
+ callbackName, callbackSig);
+ pContext->Callback2 = NULL; // not always used
+ }
+
+ return pContext;
+}
+
+
+static void ReportError( JNIEnv *pEnv, jobject target, jobject service, DNSServiceErrorType err)
+// Invoke operationFailed() method on target with err.
+{
+ jclass cls = (*pEnv)->GetObjectClass( pEnv, target);
+ jmethodID opFailed = (*pEnv)->GetMethodID( pEnv, cls, "operationFailed",
+ "(Lcom/apple/dnssd/DNSSDService;I)V");
+
+ (*pEnv)->CallVoidMethod( pEnv, target, opFailed, service, err);
+}
+
+JNIEXPORT void JNICALL Java_com_apple_dnssd_AppleService_HaltOperation( JNIEnv *pEnv, jobject pThis)
+/* Deallocate the dns_sd service browser and set the Java object's fNativeContext field to 0. */
+{
+ jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis);
+ jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J");
+
+ if ( contextField != 0)
+ {
+ OpContext *pContext = (OpContext*) (*pEnv)->GetLongField( pEnv, pThis, contextField);
+ if ( pContext != NULL)
+ {
+ // MUST clear fNativeContext first, BEFORE calling DNSServiceRefDeallocate()
+ (*pEnv)->SetLongField( pEnv, pThis, contextField, 0);
+ if ( pContext->ServiceRef != NULL)
+ DNSServiceRefDeallocate( pContext->ServiceRef);
+
+ (*pEnv)->DeleteWeakGlobalRef( pEnv, pContext->JavaObj);
+ (*pEnv)->DeleteWeakGlobalRef( pEnv, pContext->ClientObj);
+ free( pContext);
+ }
+ }
+}
+
+
+JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleService_BlockForData( JNIEnv *pEnv, jobject pThis)
+/* Block until data arrives, or one second passes. Returns 1 if data present, 0 otherwise. */
+{
+// BlockForData() not supported with AUTO_CALLBACKS
+#if !AUTO_CALLBACKS
+ jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis);
+ jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J");
+
+ if ( contextField != 0)
+ {
+ OpContext *pContext = (OpContext*) (*pEnv)->GetLongField( pEnv, pThis, contextField);
+ if ( pContext != NULL)
+ {
+ fd_set readFDs;
+ int sd = DNSServiceRefSockFD( pContext->ServiceRef);
+ struct timeval timeout = { 1, 0 };
+ FD_ZERO( &readFDs);
+ FD_SET( sd, &readFDs);
+
+ // Q: Why do we poll here?
+ // A: Because there's no other thread-safe way to do it.
+ // Mac OS X terminates a select() call if you close one of the sockets it's listening on, but Linux does not,
+ // and arguably Linux is correct (See <http://www.ussg.iu.edu/hypermail/linux/kernel/0405.1/0418.html>)
+ // The problem is that the Mac OS X behaviour assumes that it's okay for one thread to close a socket while
+ // some other thread is monitoring that socket in select(), but the difficulty is that there's no general way
+ // to make that thread-safe, because there's no atomic way to enter select() and release a lock simultaneously.
+ // If we try to do this without holding any lock, then right as we jump to the select() routine,
+ // some other thread could stop our operation (thereby closing the socket),
+ // and then that thread (or even some third, unrelated thread)
+ // could do some other DNS-SD operation (or some other operation that opens a new file descriptor)
+ // and then we'd blindly resume our fall into the select() call, now blocking on a file descriptor
+ // that may coincidentally have the same numerical value, but is semantically unrelated
+ // to the true file descriptor we thought we were blocking on.
+ // We can't stop this race condition from happening, but at least if we wake up once a second we can detect
+ // when fNativeContext has gone to zero, and thereby discover that we were blocking on the wrong fd.
+
+ if (select( sd + 1, &readFDs, (fd_set*) NULL, (fd_set*) NULL, &timeout) == 1) return(1);
+ }
+ }
+#endif // !AUTO_CALLBACKS
+ return(0);
+}
+
+
+JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleService_ProcessResults( JNIEnv *pEnv, jobject pThis)
+/* Call through to DNSServiceProcessResult() while data remains on socket. */
+{
+#if !AUTO_CALLBACKS // ProcessResults() not supported with AUTO_CALLBACKS
+
+ jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis);
+ jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J");
+ OpContext *pContext = (OpContext*) (*pEnv)->GetLongField( pEnv, pThis, contextField);
+ DNSServiceErrorType err = kDNSServiceErr_BadState;
+
+ if ( pContext != NULL)
+ {
+ int sd = DNSServiceRefSockFD( pContext->ServiceRef);
+ fd_set readFDs;
+ struct timeval zeroTimeout = { 0, 0 };
+
+ pContext->Env = pEnv;
+
+ FD_ZERO( &readFDs);
+ FD_SET( sd, &readFDs);
+
+ err = kDNSServiceErr_NoError;
+ if (0 < select(sd + 1, &readFDs, (fd_set*) NULL, (fd_set*) NULL, &zeroTimeout))
+ {
+ err = DNSServiceProcessResult(pContext->ServiceRef);
+ // Use caution here!
+ // We cannot touch any data structures associated with this operation!
+ // The DNSServiceProcessResult() routine should have invoked our callback,
+ // and our callback could have terminated the operation with op.stop();
+ // and that means HaltOperation() will have been called, which frees pContext.
+ // Basically, from here we just have to get out without touching any stale
+ // data structures that could blow up on us! Particularly, any attempt
+ // to loop here reading more results from the file descriptor is unsafe.
+ }
+ }
+ return err;
+#endif // AUTO_CALLBACKS
+}
+
+
+static void DNSSD_API ServiceBrowseReply( DNSServiceRef sdRef _UNUSED, DNSServiceFlags flags, uint32_t interfaceIndex,
+ DNSServiceErrorType errorCode, const char *serviceName, const char *regtype,
+ const char *replyDomain, void *context)
+{
+ OpContext *pContext = (OpContext*) context;
+
+ SetupCallbackState( &pContext->Env);
+
+ if ( pContext->ClientObj != NULL && pContext->Callback != NULL)
+ {
+ if ( errorCode == kDNSServiceErr_NoError)
+ {
+ (*pContext->Env)->CallVoidMethod( pContext->Env, pContext->ClientObj,
+ ( flags & kDNSServiceFlagsAdd) != 0 ? pContext->Callback : pContext->Callback2,
+ pContext->JavaObj, flags, interfaceIndex,
+ (*pContext->Env)->NewStringUTF( pContext->Env, serviceName),
+ (*pContext->Env)->NewStringUTF( pContext->Env, regtype),
+ (*pContext->Env)->NewStringUTF( pContext->Env, replyDomain));
+ }
+ else
+ ReportError( pContext->Env, pContext->ClientObj, pContext->JavaObj, errorCode);
+ }
+
+ TeardownCallbackState();
+}
+
+JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleBrowser_CreateBrowser( JNIEnv *pEnv, jobject pThis,
+ jint flags, jint ifIndex, jstring regType, jstring domain)
+{
+ jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis);
+ jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J");
+ OpContext *pContext = NULL;
+ DNSServiceErrorType err = kDNSServiceErr_NoError;
+
+ if ( contextField != 0)
+ pContext = NewContext( pEnv, pThis, "serviceFound",
+ "(Lcom/apple/dnssd/DNSSDService;IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
+ else
+ err = kDNSServiceErr_BadParam;
+
+ if ( pContext != NULL)
+ {
+ const char *regStr = SafeGetUTFChars( pEnv, regType);
+ const char *domainStr = SafeGetUTFChars( pEnv, domain);
+
+ pContext->Callback2 = (*pEnv)->GetMethodID( pEnv,
+ (*pEnv)->GetObjectClass( pEnv, pContext->ClientObj),
+ "serviceLost", "(Lcom/apple/dnssd/DNSSDService;IILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
+
+ err = DNSServiceBrowse( &pContext->ServiceRef, flags, ifIndex, regStr, domainStr, ServiceBrowseReply, pContext);
+ if ( err == kDNSServiceErr_NoError)
+ {
+ (*pEnv)->SetLongField( pEnv, pThis, contextField, (jlong) pContext);
+ }
+
+ SafeReleaseUTFChars( pEnv, regType, regStr);
+ SafeReleaseUTFChars( pEnv, domain, domainStr);
+ }
+ else
+ err = kDNSServiceErr_NoMemory;
+
+ return err;
+}
+
+
+static void DNSSD_API ServiceResolveReply( DNSServiceRef sdRef _UNUSED, 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)
+{
+ OpContext *pContext = (OpContext*) context;
+ jclass txtCls;
+ jmethodID txtCtor;
+ jbyteArray txtBytes;
+ jobject txtObj;
+ jbyte *pBytes;
+
+ SetupCallbackState( &pContext->Env);
+
+ txtCls = (*pContext->Env)->FindClass( pContext->Env, "com/apple/dnssd/TXTRecord");
+ txtCtor = (*pContext->Env)->GetMethodID( pContext->Env, txtCls, "<init>", "([B)V");
+
+ if ( pContext->ClientObj != NULL && pContext->Callback != NULL && txtCtor != NULL &&
+ NULL != ( txtBytes = (*pContext->Env)->NewByteArray( pContext->Env, txtLen)))
+ {
+ if ( errorCode == kDNSServiceErr_NoError)
+ {
+ // Since Java ints are defined to be big-endian, we canonicalize 'port' from a 16-bit
+ // pattern into a number here.
+ port = ( ((unsigned char*) &port)[0] << 8) | ((unsigned char*) &port)[1];
+
+ // Initialize txtBytes with contents of txtRecord
+ pBytes = (*pContext->Env)->GetByteArrayElements( pContext->Env, txtBytes, NULL);
+ memcpy( pBytes, txtRecord, txtLen);
+ (*pContext->Env)->ReleaseByteArrayElements( pContext->Env, txtBytes, pBytes, JNI_COMMIT);
+
+ // Construct txtObj with txtBytes
+ txtObj = (*pContext->Env)->NewObject( pContext->Env, txtCls, txtCtor, txtBytes);
+ (*pContext->Env)->DeleteLocalRef( pContext->Env, txtBytes);
+
+ (*pContext->Env)->CallVoidMethod( pContext->Env, pContext->ClientObj, pContext->Callback,
+ pContext->JavaObj, flags, interfaceIndex,
+ (*pContext->Env)->NewStringUTF( pContext->Env, fullname),
+ (*pContext->Env)->NewStringUTF( pContext->Env, hosttarget),
+ port, txtObj);
+ }
+ else
+ ReportError( pContext->Env, pContext->ClientObj, pContext->JavaObj, errorCode);
+ }
+
+ TeardownCallbackState();
+}
+
+JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleResolver_CreateResolver( JNIEnv *pEnv, jobject pThis,
+ jint flags, jint ifIndex, jstring serviceName, jstring regType, jstring domain)
+{
+ jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis);
+ jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J");
+ OpContext *pContext = NULL;
+ DNSServiceErrorType err = kDNSServiceErr_NoError;
+
+ if ( contextField != 0)
+ pContext = NewContext( pEnv, pThis, "serviceResolved",
+ "(Lcom/apple/dnssd/DNSSDService;IILjava/lang/String;Ljava/lang/String;ILcom/apple/dnssd/TXTRecord;)V");
+ else
+ err = kDNSServiceErr_BadParam;
+
+ if ( pContext != NULL)
+ {
+ const char *servStr = SafeGetUTFChars( pEnv, serviceName);
+ const char *regStr = SafeGetUTFChars( pEnv, regType);
+ const char *domainStr = SafeGetUTFChars( pEnv, domain);
+
+ err = DNSServiceResolve( &pContext->ServiceRef, flags, ifIndex,
+ servStr, regStr, domainStr, ServiceResolveReply, pContext);
+ if ( err == kDNSServiceErr_NoError)
+ {
+ (*pEnv)->SetLongField( pEnv, pThis, contextField, (jlong) pContext);
+ }
+
+ SafeReleaseUTFChars( pEnv, serviceName, servStr);
+ SafeReleaseUTFChars( pEnv, regType, regStr);
+ SafeReleaseUTFChars( pEnv, domain, domainStr);
+ }
+ else
+ err = kDNSServiceErr_NoMemory;
+
+ return err;
+}
+
+
+static void DNSSD_API ServiceRegisterReply( DNSServiceRef sdRef _UNUSED, DNSServiceFlags flags,
+ DNSServiceErrorType errorCode, const char *serviceName,
+ const char *regType, const char *domain, void *context)
+{
+ OpContext *pContext = (OpContext*) context;
+
+ SetupCallbackState( &pContext->Env);
+
+ if ( pContext->ClientObj != NULL && pContext->Callback != NULL)
+ {
+ if ( errorCode == kDNSServiceErr_NoError)
+ {
+ (*pContext->Env)->CallVoidMethod( pContext->Env, pContext->ClientObj, pContext->Callback,
+ pContext->JavaObj, flags,
+ (*pContext->Env)->NewStringUTF( pContext->Env, serviceName),
+ (*pContext->Env)->NewStringUTF( pContext->Env, regType),
+ (*pContext->Env)->NewStringUTF( pContext->Env, domain));
+ }
+ else
+ ReportError( pContext->Env, pContext->ClientObj, pContext->JavaObj, errorCode);
+ }
+ TeardownCallbackState();
+}
+
+JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleRegistration_BeginRegister( JNIEnv *pEnv, jobject pThis,
+ jint ifIndex, jint flags, jstring serviceName, jstring regType,
+ jstring domain, jstring host, jint port, jbyteArray txtRecord)
+{
+ //syslog(LOG_ERR, "BR");
+ jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis);
+ jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J");
+ OpContext *pContext = NULL;
+ DNSServiceErrorType err = kDNSServiceErr_NoError;
+ jbyte *pBytes;
+ jsize numBytes;
+
+ //syslog(LOG_ERR, "BR: contextField %d", contextField);
+
+ if ( contextField != 0)
+ pContext = NewContext( pEnv, pThis, "serviceRegistered",
+ "(Lcom/apple/dnssd/DNSSDRegistration;ILjava/lang/String;Ljava/lang/String;Ljava/lang/String;)V");
+ else
+ err = kDNSServiceErr_BadParam;
+
+ if ( pContext != NULL)
+ {
+ const char *servStr = SafeGetUTFChars( pEnv, serviceName);
+ const char *regStr = SafeGetUTFChars( pEnv, regType);
+ const char *domainStr = SafeGetUTFChars( pEnv, domain);
+ const char *hostStr = SafeGetUTFChars( pEnv, host);
+
+ //syslog(LOG_ERR, "BR: regStr %s", regStr);
+
+ // Since Java ints are defined to be big-endian, we de-canonicalize 'port' from a
+ // big-endian number into a 16-bit pattern here.
+ uint16_t portBits = port;
+ portBits = ( ((unsigned char*) &portBits)[0] << 8) | ((unsigned char*) &portBits)[1];
+
+ pBytes = txtRecord ? (*pEnv)->GetByteArrayElements( pEnv, txtRecord, NULL) : NULL;
+ numBytes = txtRecord ? (*pEnv)->GetArrayLength( pEnv, txtRecord) : 0;
+
+ err = DNSServiceRegister( &pContext->ServiceRef, flags, ifIndex, servStr, regStr,
+ domainStr, hostStr, portBits,
+ numBytes, pBytes, ServiceRegisterReply, pContext);
+ if ( err == kDNSServiceErr_NoError)
+ {
+ (*pEnv)->SetLongField( pEnv, pThis, contextField, (jlong) pContext);
+ }
+
+ if ( pBytes != NULL)
+ (*pEnv)->ReleaseByteArrayElements( pEnv, txtRecord, pBytes, 0);
+
+ SafeReleaseUTFChars( pEnv, serviceName, servStr);
+ SafeReleaseUTFChars( pEnv, regType, regStr);
+ SafeReleaseUTFChars( pEnv, domain, domainStr);
+ SafeReleaseUTFChars( pEnv, host, hostStr);
+ }
+ else
+ err = kDNSServiceErr_NoMemory;
+
+ return err;
+}
+
+JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleRegistration_AddRecord( JNIEnv *pEnv, jobject pThis,
+ jint flags, jint rrType, jbyteArray rData, jint ttl, jobject destObj)
+{
+ jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis);
+ jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J");
+ jclass destCls = (*pEnv)->GetObjectClass( pEnv, destObj);
+ jfieldID recField = (*pEnv)->GetFieldID( pEnv, destCls, "fRecord", "J");
+ OpContext *pContext = NULL;
+ DNSServiceErrorType err = kDNSServiceErr_NoError;
+ jbyte *pBytes;
+ jsize numBytes;
+ DNSRecordRef recRef;
+
+ if ( contextField != 0)
+ pContext = (OpContext*) (*pEnv)->GetLongField( pEnv, pThis, contextField);
+ if ( pContext == NULL || pContext->ServiceRef == NULL)
+ return kDNSServiceErr_BadParam;
+
+ pBytes = (*pEnv)->GetByteArrayElements( pEnv, rData, NULL);
+ numBytes = (*pEnv)->GetArrayLength( pEnv, rData);
+
+ err = DNSServiceAddRecord( pContext->ServiceRef, &recRef, flags, rrType, numBytes, pBytes, ttl);
+ if ( err == kDNSServiceErr_NoError)
+ {
+ (*pEnv)->SetLongField( pEnv, destObj, recField, (jlong) recRef);
+ }
+
+ if ( pBytes != NULL)
+ (*pEnv)->ReleaseByteArrayElements( pEnv, rData, pBytes, 0);
+
+ return err;
+}
+
+JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleDNSRecord_Update( JNIEnv *pEnv, jobject pThis,
+ jint flags, jbyteArray rData, jint ttl)
+{
+ jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis);
+ jfieldID ownerField = (*pEnv)->GetFieldID( pEnv, cls, "fOwner", "Lcom/apple/dnssd/AppleService;");
+ jfieldID recField = (*pEnv)->GetFieldID( pEnv, cls, "fRecord", "J");
+ OpContext *pContext = NULL;
+ DNSServiceErrorType err = kDNSServiceErr_NoError;
+ jbyte *pBytes;
+ jsize numBytes;
+ DNSRecordRef recRef = NULL;
+
+ if ( ownerField != 0)
+ {
+ jobject ownerObj = (*pEnv)->GetObjectField( pEnv, pThis, ownerField);
+ jclass ownerClass = (*pEnv)->GetObjectClass( pEnv, ownerObj);
+ jfieldID contextField = (*pEnv)->GetFieldID( pEnv, ownerClass, "fNativeContext", "J");
+ if ( contextField != 0)
+ pContext = (OpContext*) (*pEnv)->GetLongField( pEnv, ownerObj, contextField);
+ }
+ if ( recField != 0)
+ recRef = (DNSRecordRef) (*pEnv)->GetLongField( pEnv, pThis, recField);
+ if ( pContext == NULL || pContext->ServiceRef == NULL)
+ return kDNSServiceErr_BadParam;
+
+ pBytes = (*pEnv)->GetByteArrayElements( pEnv, rData, NULL);
+ numBytes = (*pEnv)->GetArrayLength( pEnv, rData);
+
+ err = DNSServiceUpdateRecord( pContext->ServiceRef, recRef, flags, numBytes, pBytes, ttl);
+
+ if ( pBytes != NULL)
+ (*pEnv)->ReleaseByteArrayElements( pEnv, rData, pBytes, 0);
+
+ return err;
+}
+
+JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleDNSRecord_Remove( JNIEnv *pEnv, jobject pThis)
+{
+ jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis);
+ jfieldID ownerField = (*pEnv)->GetFieldID( pEnv, cls, "fOwner", "Lcom/apple/dnssd/AppleService;");
+ jfieldID recField = (*pEnv)->GetFieldID( pEnv, cls, "fRecord", "J");
+ OpContext *pContext = NULL;
+ DNSServiceErrorType err = kDNSServiceErr_NoError;
+ DNSRecordRef recRef = NULL;
+
+ if ( ownerField != 0)
+ {
+ jobject ownerObj = (*pEnv)->GetObjectField( pEnv, pThis, ownerField);
+ jclass ownerClass = (*pEnv)->GetObjectClass( pEnv, ownerObj);
+ jfieldID contextField = (*pEnv)->GetFieldID( pEnv, ownerClass, "fNativeContext", "J");
+ if ( contextField != 0)
+ pContext = (OpContext*) (*pEnv)->GetLongField( pEnv, ownerObj, contextField);
+ }
+ if ( recField != 0)
+ recRef = (DNSRecordRef) (*pEnv)->GetLongField( pEnv, pThis, recField);
+ if ( pContext == NULL || pContext->ServiceRef == NULL)
+ return kDNSServiceErr_BadParam;
+
+ err = DNSServiceRemoveRecord( pContext->ServiceRef, recRef, 0);
+
+ return err;
+}
+
+
+JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleRecordRegistrar_CreateConnection( JNIEnv *pEnv, jobject pThis)
+{
+ jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis);
+ jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J");
+ OpContext *pContext = NULL;
+ DNSServiceErrorType err = kDNSServiceErr_NoError;
+
+ if ( contextField != 0)
+ pContext = NewContext( pEnv, pThis, "recordRegistered", "(Lcom/apple/dnssd/DNSRecord;I)V");
+ else
+ err = kDNSServiceErr_BadParam;
+
+ if ( pContext != NULL)
+ {
+ err = DNSServiceCreateConnection( &pContext->ServiceRef);
+ if ( err == kDNSServiceErr_NoError)
+ {
+ (*pEnv)->SetLongField( pEnv, pThis, contextField, (jlong) pContext);
+ }
+ }
+ else
+ err = kDNSServiceErr_NoMemory;
+
+ return err;
+}
+
+struct RecordRegistrationRef
+{
+ OpContext *Context;
+ jobject RecordObj;
+};
+typedef struct RecordRegistrationRef RecordRegistrationRef;
+
+static void DNSSD_API RegisterRecordReply( DNSServiceRef sdRef _UNUSED,
+ DNSRecordRef recordRef _UNUSED, DNSServiceFlags flags,
+ DNSServiceErrorType errorCode, void *context)
+{
+ RecordRegistrationRef *regEnvelope = (RecordRegistrationRef*) context;
+ OpContext *pContext = regEnvelope->Context;
+
+ SetupCallbackState( &pContext->Env);
+
+ if ( pContext->ClientObj != NULL && pContext->Callback != NULL)
+ {
+ if ( errorCode == kDNSServiceErr_NoError)
+ {
+ (*pContext->Env)->CallVoidMethod( pContext->Env, pContext->ClientObj, pContext->Callback,
+ regEnvelope->RecordObj, flags);
+ }
+ else
+ ReportError( pContext->Env, pContext->ClientObj, pContext->JavaObj, errorCode);
+ }
+
+ (*pContext->Env)->DeleteWeakGlobalRef( pContext->Env, regEnvelope->RecordObj);
+ free( regEnvelope);
+
+ TeardownCallbackState();
+}
+
+JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleRecordRegistrar_RegisterRecord( JNIEnv *pEnv, jobject pThis,
+ jint flags, jint ifIndex, jstring fullname, jint rrType, jint rrClass,
+ jbyteArray rData, jint ttl, jobject destObj)
+{
+ jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis);
+ jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J");
+ jclass destCls = (*pEnv)->GetObjectClass( pEnv, destObj);
+ jfieldID recField = (*pEnv)->GetFieldID( pEnv, destCls, "fRecord", "J");
+ const char *nameStr = SafeGetUTFChars( pEnv, fullname);
+ OpContext *pContext = NULL;
+ DNSServiceErrorType err = kDNSServiceErr_NoError;
+ jbyte *pBytes;
+ jsize numBytes;
+ DNSRecordRef recRef;
+ RecordRegistrationRef *regEnvelope;
+
+ if ( contextField != 0)
+ pContext = (OpContext*) (*pEnv)->GetLongField( pEnv, pThis, contextField);
+ if ( pContext == NULL || pContext->ServiceRef == NULL || nameStr == NULL)
+ return kDNSServiceErr_BadParam;
+
+ regEnvelope = calloc( 1, sizeof *regEnvelope);
+ if ( regEnvelope == NULL)
+ return kDNSServiceErr_NoMemory;
+ regEnvelope->Context = pContext;
+ regEnvelope->RecordObj = (*pEnv)->NewWeakGlobalRef( pEnv, destObj); // must convert local ref to global to cache
+
+ pBytes = (*pEnv)->GetByteArrayElements( pEnv, rData, NULL);
+ numBytes = (*pEnv)->GetArrayLength( pEnv, rData);
+
+ err = DNSServiceRegisterRecord( pContext->ServiceRef, &recRef, flags, ifIndex,
+ nameStr, rrType, rrClass, numBytes, pBytes, ttl,
+ RegisterRecordReply, regEnvelope);
+
+ if ( err == kDNSServiceErr_NoError)
+ {
+ (*pEnv)->SetLongField( pEnv, destObj, recField, (jlong) recRef);
+ }
+ else
+ {
+ if ( regEnvelope->RecordObj != NULL)
+ (*pEnv)->DeleteWeakGlobalRef( pEnv, regEnvelope->RecordObj);
+ free( regEnvelope);
+ }
+
+ if ( pBytes != NULL)
+ (*pEnv)->ReleaseByteArrayElements( pEnv, rData, pBytes, 0);
+
+ SafeReleaseUTFChars( pEnv, fullname, nameStr);
+
+ return err;
+}
+
+
+static void DNSSD_API ServiceQueryReply( DNSServiceRef sdRef _UNUSED, DNSServiceFlags flags, uint32_t interfaceIndex,
+ DNSServiceErrorType errorCode, const char *serviceName,
+ uint16_t rrtype, uint16_t rrclass, uint16_t rdlen,
+ const void *rdata, uint32_t ttl, void *context)
+{
+ OpContext *pContext = (OpContext*) context;
+ jbyteArray rDataObj;
+ jbyte *pBytes;
+
+ SetupCallbackState( &pContext->Env);
+
+ if ( pContext->ClientObj != NULL && pContext->Callback != NULL &&
+ NULL != ( rDataObj = (*pContext->Env)->NewByteArray( pContext->Env, rdlen)))
+ {
+ if ( errorCode == kDNSServiceErr_NoError)
+ {
+ // Initialize rDataObj with contents of rdata
+ pBytes = (*pContext->Env)->GetByteArrayElements( pContext->Env, rDataObj, NULL);
+ memcpy( pBytes, rdata, rdlen);
+ (*pContext->Env)->ReleaseByteArrayElements( pContext->Env, rDataObj, pBytes, JNI_COMMIT);
+
+ (*pContext->Env)->CallVoidMethod( pContext->Env, pContext->ClientObj, pContext->Callback,
+ pContext->JavaObj, flags, interfaceIndex,
+ (*pContext->Env)->NewStringUTF( pContext->Env, serviceName),
+ rrtype, rrclass, rDataObj, ttl);
+ }
+ else
+ ReportError( pContext->Env, pContext->ClientObj, pContext->JavaObj, errorCode);
+ }
+ TeardownCallbackState();
+}
+
+JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleQuery_CreateQuery( JNIEnv *pEnv, jobject pThis,
+ jint flags, jint ifIndex, jstring serviceName, jint rrtype, jint rrclass)
+{
+ jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis);
+ jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J");
+ OpContext *pContext = NULL;
+ DNSServiceErrorType err = kDNSServiceErr_NoError;
+
+ if ( contextField != 0)
+ pContext = NewContext( pEnv, pThis, "queryAnswered",
+ "(Lcom/apple/dnssd/DNSSDService;IILjava/lang/String;II[BI)V");
+ else
+ err = kDNSServiceErr_BadParam;
+
+ if ( pContext != NULL)
+ {
+ const char *servStr = SafeGetUTFChars( pEnv, serviceName);
+
+ err = DNSServiceQueryRecord( &pContext->ServiceRef, flags, ifIndex, servStr,
+ rrtype, rrclass, ServiceQueryReply, pContext);
+ if ( err == kDNSServiceErr_NoError)
+ {
+ (*pEnv)->SetLongField( pEnv, pThis, contextField, (jlong) pContext);
+ }
+
+ SafeReleaseUTFChars( pEnv, serviceName, servStr);
+ }
+ else
+ err = kDNSServiceErr_NoMemory;
+
+ return err;
+}
+
+
+static void DNSSD_API DomainEnumReply( DNSServiceRef sdRef _UNUSED, DNSServiceFlags flags, uint32_t interfaceIndex,
+ DNSServiceErrorType errorCode, const char *replyDomain, void *context)
+{
+ OpContext *pContext = (OpContext*) context;
+
+ SetupCallbackState( &pContext->Env);
+
+ if ( pContext->ClientObj != NULL && pContext->Callback != NULL)
+ {
+ if ( errorCode == kDNSServiceErr_NoError)
+ {
+ (*pContext->Env)->CallVoidMethod( pContext->Env, pContext->ClientObj,
+ ( flags & kDNSServiceFlagsAdd) != 0 ? pContext->Callback : pContext->Callback2,
+ pContext->JavaObj, flags, interfaceIndex,
+ (*pContext->Env)->NewStringUTF( pContext->Env, replyDomain));
+ }
+ else
+ ReportError( pContext->Env, pContext->ClientObj, pContext->JavaObj, errorCode);
+ }
+ TeardownCallbackState();
+}
+
+JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleDomainEnum_BeginEnum( JNIEnv *pEnv, jobject pThis,
+ jint flags, jint ifIndex)
+{
+ jclass cls = (*pEnv)->GetObjectClass( pEnv, pThis);
+ jfieldID contextField = (*pEnv)->GetFieldID( pEnv, cls, "fNativeContext", "J");
+ OpContext *pContext = NULL;
+ DNSServiceErrorType err = kDNSServiceErr_NoError;
+
+ if ( contextField != 0)
+ pContext = NewContext( pEnv, pThis, "domainFound",
+ "(Lcom/apple/dnssd/DNSSDService;IILjava/lang/String;)V");
+ else
+ err = kDNSServiceErr_BadParam;
+
+ if ( pContext != NULL)
+ {
+ pContext->Callback2 = (*pEnv)->GetMethodID( pEnv,
+ (*pEnv)->GetObjectClass( pEnv, pContext->ClientObj),
+ "domainLost", "(Lcom/apple/dnssd/DNSSDService;IILjava/lang/String;)V");
+
+ err = DNSServiceEnumerateDomains( &pContext->ServiceRef, flags, ifIndex,
+ DomainEnumReply, pContext);
+ if ( err == kDNSServiceErr_NoError)
+ {
+ (*pEnv)->SetLongField( pEnv, pThis, contextField, (jlong) pContext);
+ }
+ }
+ else
+ err = kDNSServiceErr_NoMemory;
+
+ return err;
+}
+
+
+JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleDNSSD_ConstructName( JNIEnv *pEnv, jobject pThis _UNUSED,
+ jstring serviceName, jstring regtype, jstring domain, jobjectArray pOut)
+{
+ DNSServiceErrorType err = kDNSServiceErr_NoError;
+ const char *nameStr = SafeGetUTFChars( pEnv, serviceName);
+ const char *regStr = SafeGetUTFChars( pEnv, regtype);
+ const char *domStr = SafeGetUTFChars( pEnv, domain);
+ char buff[ kDNSServiceMaxDomainName + 1];
+
+ err = DNSServiceConstructFullName( buff, nameStr, regStr, domStr);
+
+ if ( err == kDNSServiceErr_NoError)
+ {
+ // pOut is expected to be a String[1] array.
+ (*pEnv)->SetObjectArrayElement( pEnv, pOut, 0, (*pEnv)->NewStringUTF( pEnv, buff));
+ }
+
+ SafeReleaseUTFChars( pEnv, serviceName, nameStr);
+ SafeReleaseUTFChars( pEnv, regtype, regStr);
+ SafeReleaseUTFChars( pEnv, domain, domStr);
+
+ return err;
+}
+
+JNIEXPORT void JNICALL Java_com_apple_dnssd_AppleDNSSD_ReconfirmRecord( JNIEnv *pEnv, jobject pThis _UNUSED,
+ jint flags, jint ifIndex, jstring fullName,
+ jint rrtype, jint rrclass, jbyteArray rdata)
+{
+ jbyte *pBytes;
+ jsize numBytes;
+ const char *nameStr = SafeGetUTFChars( pEnv, fullName);
+
+ pBytes = (*pEnv)->GetByteArrayElements( pEnv, rdata, NULL);
+ numBytes = (*pEnv)->GetArrayLength( pEnv, rdata);
+
+ DNSServiceReconfirmRecord( flags, ifIndex, nameStr, rrtype, rrclass, numBytes, pBytes);
+
+ if ( pBytes != NULL)
+ (*pEnv)->ReleaseByteArrayElements( pEnv, rdata, pBytes, 0);
+
+ SafeReleaseUTFChars( pEnv, fullName, nameStr);
+}
+
+#define LOCAL_ONLY_NAME "loo"
+
+JNIEXPORT jstring JNICALL Java_com_apple_dnssd_AppleDNSSD_GetNameForIfIndex( JNIEnv *pEnv, jobject pThis _UNUSED,
+ jint ifIndex)
+{
+ char *p = LOCAL_ONLY_NAME, nameBuff[IF_NAMESIZE];
+
+ if (ifIndex != (jint) kDNSServiceInterfaceIndexLocalOnly)
+ p = if_indextoname( ifIndex, nameBuff );
+
+ return (*pEnv)->NewStringUTF( pEnv, p);
+}
+
+
+JNIEXPORT jint JNICALL Java_com_apple_dnssd_AppleDNSSD_GetIfIndexForName( JNIEnv *pEnv, jobject pThis _UNUSED,
+ jstring ifName)
+{
+ uint32_t ifIndex = kDNSServiceInterfaceIndexLocalOnly;
+ const char *nameStr = SafeGetUTFChars( pEnv, ifName);
+
+ if (strcmp(nameStr, LOCAL_ONLY_NAME))
+ ifIndex = if_nametoindex( nameStr);
+
+ SafeReleaseUTFChars( pEnv, ifName, nameStr);
+
+ return ifIndex;
+}
+
+
+#if defined(_WIN32)
+static char*
+if_indextoname( DWORD ifIndex, char * nameBuff)
+{
+ PIP_ADAPTER_INFO pAdapterInfo = NULL;
+ PIP_ADAPTER_INFO pAdapter = NULL;
+ DWORD dwRetVal = 0;
+ char * ifName = NULL;
+ ULONG ulOutBufLen = 0;
+
+ if (GetAdaptersInfo( NULL, &ulOutBufLen) != ERROR_BUFFER_OVERFLOW)
+ {
+ goto exit;
+ }
+
+ pAdapterInfo = (IP_ADAPTER_INFO *) malloc(ulOutBufLen);
+
+ if (pAdapterInfo == NULL)
+ {
+ goto exit;
+ }
+
+ dwRetVal = GetAdaptersInfo( pAdapterInfo, &ulOutBufLen );
+
+ if (dwRetVal != NO_ERROR)
+ {
+ goto exit;
+ }
+
+ pAdapter = pAdapterInfo;
+ while (pAdapter)
+ {
+ if (pAdapter->Index == ifIndex)
+ {
+ // It would be better if we passed in the length of nameBuff to this
+ // function, so we would have absolute certainty that no buffer
+ // overflows would occur. Buffer overflows *shouldn't* occur because
+ // nameBuff is of size MAX_ADAPTER_NAME_LENGTH.
+ strcpy( nameBuff, pAdapter->AdapterName );
+ ifName = nameBuff;
+ break;
+ }
+
+ pAdapter = pAdapter->Next;
+ }
+
+exit:
+
+ if (pAdapterInfo != NULL)
+ {
+ free( pAdapterInfo );
+ pAdapterInfo = NULL;
+ }
+
+ return ifName;
+}
+
+
+static DWORD
+if_nametoindex( const char * nameStr )
+{
+ PIP_ADAPTER_INFO pAdapterInfo = NULL;
+ PIP_ADAPTER_INFO pAdapter = NULL;
+ DWORD dwRetVal = 0;
+ DWORD ifIndex = 0;
+ ULONG ulOutBufLen = 0;
+
+ if (GetAdaptersInfo( NULL, &ulOutBufLen) != ERROR_BUFFER_OVERFLOW)
+ {
+ goto exit;
+ }
+
+ pAdapterInfo = (IP_ADAPTER_INFO *) malloc(ulOutBufLen);
+
+ if (pAdapterInfo == NULL)
+ {
+ goto exit;
+ }
+
+ dwRetVal = GetAdaptersInfo( pAdapterInfo, &ulOutBufLen );
+
+ if (dwRetVal != NO_ERROR)
+ {
+ goto exit;
+ }
+
+ pAdapter = pAdapterInfo;
+ while (pAdapter)
+ {
+ if (strcmp(pAdapter->AdapterName, nameStr) == 0)
+ {
+ ifIndex = pAdapter->Index;
+ break;
+ }
+
+ pAdapter = pAdapter->Next;
+ }
+
+exit:
+
+ if (pAdapterInfo != NULL)
+ {
+ free( pAdapterInfo );
+ pAdapterInfo = NULL;
+ }
+
+ return ifIndex;
+}
+#endif
diff --git a/usr/src/lib/libdns_sd/java/common/mapfile-vers b/usr/src/lib/libdns_sd/java/common/mapfile-vers
new file mode 100644
index 0000000000..0ae50ad6ff
--- /dev/null
+++ b/usr/src/lib/libdns_sd/java/common/mapfile-vers
@@ -0,0 +1,48 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+SUNW_1.1 {
+ global:
+ Java_com_apple_dnssd_AppleBrowser_CreateBrowser;
+ Java_com_apple_dnssd_AppleDNSRecord_Remove;
+ Java_com_apple_dnssd_AppleDNSRecord_Update;
+ Java_com_apple_dnssd_AppleDNSSD_ConstructName;
+ Java_com_apple_dnssd_AppleDNSSD_GetIfIndexForName;
+ Java_com_apple_dnssd_AppleDNSSD_GetNameForIfIndex;
+ Java_com_apple_dnssd_AppleDNSSD_InitLibrary;
+ Java_com_apple_dnssd_AppleDNSSD_ReconfirmRecord;
+ Java_com_apple_dnssd_AppleDomainEnum_BeginEnum;
+ Java_com_apple_dnssd_AppleQuery_CreateQuery;
+ Java_com_apple_dnssd_AppleRecordRegistrar_CreateConnection;
+ Java_com_apple_dnssd_AppleRecordRegistrar_RegisterRecord;
+ Java_com_apple_dnssd_AppleRegistration_AddRecord;
+ Java_com_apple_dnssd_AppleRegistration_BeginRegister;
+ Java_com_apple_dnssd_AppleResolver_CreateResolver;
+ Java_com_apple_dnssd_AppleService_BlockForData;
+ Java_com_apple_dnssd_AppleService_HaltOperation;
+ Java_com_apple_dnssd_AppleService_ProcessResults;
+ local:
+ *;
+};
diff --git a/usr/src/lib/libdns_sd/java/i386/Makefile b/usr/src/lib/libdns_sd/java/i386/Makefile
new file mode 100644
index 0000000000..3b6c2a1943
--- /dev/null
+++ b/usr/src/lib/libdns_sd/java/i386/Makefile
@@ -0,0 +1,35 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+include ../Makefile.com
+
+.KEEP_STATE:
+
+CPPFLAGS += -_gcc=-Wno-pointer-to-int-cast -_gcc=-Wno-int-to-pointer-cast
+
+all: $(LIBS)
+
+install: all $(ROOTLIBS) $(ROOTLINKS)
diff --git a/usr/src/lib/libdns_sd/java/sparc/Makefile b/usr/src/lib/libdns_sd/java/sparc/Makefile
new file mode 100644
index 0000000000..3b6c2a1943
--- /dev/null
+++ b/usr/src/lib/libdns_sd/java/sparc/Makefile
@@ -0,0 +1,35 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+include ../Makefile.com
+
+.KEEP_STATE:
+
+CPPFLAGS += -_gcc=-Wno-pointer-to-int-cast -_gcc=-Wno-int-to-pointer-cast
+
+all: $(LIBS)
+
+install: all $(ROOTLIBS) $(ROOTLINKS)
diff --git a/usr/src/lib/libdns_sd/java/sparcv9/Makefile b/usr/src/lib/libdns_sd/java/sparcv9/Makefile
new file mode 100644
index 0000000000..b645e1caed
--- /dev/null
+++ b/usr/src/lib/libdns_sd/java/sparcv9/Makefile
@@ -0,0 +1,32 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+include ../Makefile.com
+include ../../../Makefile.lib.64
+
+all: $(LIBS)
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64)
diff --git a/usr/src/lib/libdns_sd/sparc/Makefile b/usr/src/lib/libdns_sd/sparc/Makefile
new file mode 100644
index 0000000000..9c1be5bf3a
--- /dev/null
+++ b/usr/src/lib/libdns_sd/sparc/Makefile
@@ -0,0 +1,28 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+include ../Makefile.com
+
+install: all $(ROOTLIBS) $(ROOTLINKS) $(ROOTLINT)
diff --git a/usr/src/lib/libdns_sd/sparcv9/Makefile b/usr/src/lib/libdns_sd/sparcv9/Makefile
new file mode 100644
index 0000000000..abae92c693
--- /dev/null
+++ b/usr/src/lib/libdns_sd/sparcv9/Makefile
@@ -0,0 +1,35 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+
+include ../Makefile.com
+include ../../Makefile.lib.64
+
+CFLAGS64 += -erroff=E_ASSIGNMENT_TYPE_MISMATCH
+
+.KEEP_STATE:
+
+all: $(LIBS)
+
+install: all $(ROOTLIBS64) $(ROOTLINKS64)
diff --git a/usr/src/lib/libsecdb/auth_attr.txt b/usr/src/lib/libsecdb/auth_attr.txt
index 2dc8835ffd..f9a27370ba 100644
--- a/usr/src/lib/libsecdb/auth_attr.txt
+++ b/usr/src/lib/libsecdb/auth_attr.txt
@@ -116,6 +116,7 @@ solaris.smf.manage.idmap:::Manage Identity Mapping Service States::help=SmfIdmap
solaris.smf.manage.inetd:::Manage inetd and inetd managed services States::help=SmfIntedStates.html
solaris.smf.manage.ipsec:::Manage IPsec Service States::help=SmfIPsecStates.html
solaris.smf.manage.labels:::Manage label server::help=LabelServer.html
+solaris.smf.manage.mdns:::Manage Multicast DNS Service States::help=SmfMDNSStates.html
solaris.smf.manage.name-service-cache:::Manage Name Service Cache Daemon Service States::help=SmfNscdStates.html
solaris.smf.manage.nwam:::Manage Network Auto-Magic Service States::help=SmfNWAMStates.html
solaris.smf.manage.power:::Manage Power Management Service States::help=SmfPowerStates.html
@@ -132,6 +133,7 @@ solaris.smf.value.:::Change Values of SMF Service Properties::help=SmfValueHeade
solaris.smf.value.idmap:::Change Values of SMF Identity Mapping Service Properties::help=SmfValueIdmap.html
solaris.smf.value.inetd:::Change values of SMF Inetd configuration paramaters::help=SmfValueInted.html
solaris.smf.value.ipsec:::Change Values of SMF IPsec Properties::help=SmfValueIPsec.html
+solaris.smf.value.mdns:::Change Values of MDNS Service Properties::help=SmfValueMDNS.html
solaris.smf.value.nwam:::Change Values of SMF Network Auto-Magic Properties::help=SmfValueNWAM.html
solaris.smf.value.routing:::Change Values of SMF Routing Properties::help=SmfValueRouting.html
solaris.smf.value.tnd:::Change Trusted Network Daemon Service Property Values::help=ValueTND.html
diff --git a/usr/src/lib/libsecdb/help/auths/Makefile b/usr/src/lib/libsecdb/help/auths/Makefile
index 9548660950..906c321caf 100644
--- a/usr/src/lib/libsecdb/help/auths/Makefile
+++ b/usr/src/lib/libsecdb/help/auths/Makefile
@@ -71,6 +71,7 @@ HTMLENTS = \
SmfInetdStates.html \
SmfIPsecStates.html \
SmfManageHeader.html \
+ SmfMDNSStates.html \
SmfModifyAppl.html \
SmfModifyDepend.html \
SmfModifyFramework.html \
@@ -86,6 +87,7 @@ HTMLENTS = \
SmfValueHeader.html \
SmfValueInetd.html \
SmfValueIPsec.html \
+ SmfValueMDNS.html \
SmfValueNWAM.html \
SmfValueRouting.html \
SmfWpaStates.html \
diff --git a/usr/src/lib/libsecdb/help/auths/SmfMDNSStates.html b/usr/src/lib/libsecdb/help/auths/SmfMDNSStates.html
new file mode 100644
index 0000000000..c3acac86e5
--- /dev/null
+++ b/usr/src/lib/libsecdb/help/auths/SmfMDNSStates.html
@@ -0,0 +1,40 @@
+<HTML>
+<!--
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License (the "License").
+ You may not use this file except in compliance with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+Use is subject to license terms.
+-->
+<!-- SCCS keyword
+#ident "%Z%%M% %I% %E% SMI"
+-->
+<!--
+ <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+-->
+<BODY>
+When Manage MDNS Service States is in the Authorizations Include
+column, it grants the authorization to enable, disable, or restart
+Network Auto-Magic (NWAM) services.
+<p>
+If Manage MDNS Service States is grayed, then you are not entitled
+to Add or Remove this authorization.
+<BR>&nbsp;
+</BODY>
+</HTML>
diff --git a/usr/src/lib/libsecdb/help/auths/SmfValueMDNS.html b/usr/src/lib/libsecdb/help/auths/SmfValueMDNS.html
new file mode 100644
index 0000000000..9cf3cd0916
--- /dev/null
+++ b/usr/src/lib/libsecdb/help/auths/SmfValueMDNS.html
@@ -0,0 +1,40 @@
+<HTML>
+<!--
+ CDDL HEADER START
+
+ The contents of this file are subject to the terms of the
+ Common Development and Distribution License (the "License").
+ You may not use this file except in compliance with the License.
+
+ You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ or http://www.opensolaris.org/os/licensing.
+ See the License for the specific language governing permissions
+ and limitations under the License.
+
+ When distributing Covered Code, include this CDDL HEADER in each
+ file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ If applicable, add the following below this CDDL HEADER, with the
+ fields enclosed by brackets "[]" replaced with your own identifying
+ information: Portions Copyright [yyyy] [name of copyright owner]
+
+ CDDL HEADER END
+
+Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+Use is subject to license terms.
+-->
+<!-- SCCS keyword
+#ident "%Z%%M% %I% %E% SMI"
+-->
+<!--
+ <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=iso-8859-1">
+-->
+<BODY>
+When Value MDNS Properties is in the Authorizations Include
+column, it grants the the authorization to change Multicast
+DNS (mdns) service property values.
+<P>
+If Value MDNS Properties is grayed, then you are not entitled to
+Add or Remove this authorization.
+<BR>&nbsp;
+</BODY>
+</HTML>
diff --git a/usr/src/lib/libsecdb/prof_attr.txt b/usr/src/lib/libsecdb/prof_attr.txt
index bd715a415a..7df27db207 100644
--- a/usr/src/lib/libsecdb/prof_attr.txt
+++ b/usr/src/lib/libsecdb/prof_attr.txt
@@ -52,7 +52,7 @@ Mail Management:::Manage sendmail & queues:auths=solaris.smf.manage.sendmail;hel
Maintenance and Repair:::Maintain and repair a system:auths=solaris.smf.manage.system-log,solaris.label.range;help=RtMaintAndRepair.html
Media Backup:::Backup files and file systems:help=RtMediaBkup.html
Media Restore:::Restore files and file systems from backups:help=RtMediaRestore.html
-Network Management:::Manage the host and network configuration:auths=solaris.smf.manage.name-service-cache,solaris.smf.manage.bind,solaris.smf.value.routing,solaris.smf.manage.routing,solaris.smf.value.nwam,solaris.smf.manage.nwam,solaris.smf.manage.tnd,solaris.smf.manage.tnctl,solaris.smf.manage.wpa;profiles=Network Wifi Management,Inetd Management;help=RtNetMngmnt.html
+Network Management:::Manage the host and network configuration:auths=solaris.smf.manage.name-service-cache,solaris.smf.manage.bind,solaris.smf.value.routing,solaris.smf.manage.routing,solaris.smf.value.nwam,solaris.smf.manage.nwam,solaris.smf.manage.tnd,solaris.smf.manage.tnctl,solaris.smf.manage.wpa,solaris.smf.value.mdns,solaris.smf.manage.mdns;profiles=Network Wifi Management,Inetd Management;help=RtNetMngmnt.html
Network Security:::Manage network and host security:auths=solaris.smf.manage.ssh,solaris.smf.value.tnd;profiles=Network Wifi Security,Network Link Security,Network IPsec Management;help=RtNetSecure.html
Network Wifi Management:::Manage wifi network configuration:auths=solaris.network.wifi.config;help=RtNetWifiMngmnt.html
Network Wifi Security:::Manage wifi network security:auths=solaris.network.wifi.wep;help=RtNetWifiSecure.html
diff --git a/usr/src/lib/nsswitch/Makefile b/usr/src/lib/nsswitch/Makefile
index 44c6081481..dda261d132 100644
--- a/usr/src/lib/nsswitch/Makefile
+++ b/usr/src/lib/nsswitch/Makefile
@@ -2,9 +2,8 @@
# CDDL HEADER START
#
# The contents of this file are subject to the terms of the
-# Common Development and Distribution License, Version 1.0 only
-# (the "License"). You may not use this file except in compliance
-# with the License.
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
#
# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
# or http://www.opensolaris.org/os/licensing.
@@ -22,7 +21,7 @@
#
#ident "%Z%%M% %I% %E% SMI"
#
-# Copyright 1991-2003 Sun Microsystems, Inc. All rights reserved.
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
# Use is subject to license terms.
#
# lib/nsswitch/Makefile
@@ -30,7 +29,7 @@
include $(SRC)/Makefile.master
-SUBDIRS= files nis nisplus compat dns ldap user
+SUBDIRS= files nis nisplus compat dns ldap user mdns
all:= TARGET= all
clean:= TARGET= clean
diff --git a/usr/src/lib/nsswitch/mdns/Makefile b/usr/src/lib/nsswitch/mdns/Makefile
new file mode 100644
index 0000000000..f67704e53a
--- /dev/null
+++ b/usr/src/lib/nsswitch/mdns/Makefile
@@ -0,0 +1,45 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# lib/nsswitch/mdns/Makefile
+
+include ../../../Makefile.master
+
+FILES_SUBDIRS= $(MACH) $(BUILD64) $(MACH64)
+
+all:= TARGET= all
+clean:= TARGET= clean
+clobber:= TARGET= clobber
+install:= TARGET= install
+lint:= TARGET= lint
+
+.KEEP_STATE:
+
+all clean clobber install lint debugmod: $(FILES_SUBDIRS)
+
+$(MACH) $(MACH64): FRC
+ @cd $@; pwd; $(MAKE) $(TARGET)
+
+FRC:
diff --git a/usr/src/lib/nsswitch/mdns/Makefile.com b/usr/src/lib/nsswitch/mdns/Makefile.com
new file mode 100644
index 0000000000..00e335d732
--- /dev/null
+++ b/usr/src/lib/nsswitch/mdns/Makefile.com
@@ -0,0 +1,42 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# lib/nsswitch/mdns/Makefile.com
+
+LIBRARY = libnss_mdns.a
+VERS = .1
+
+OBJECTS = gethostent.o \
+ gethostent6.o \
+ mdns_common.o
+
+# include common nsswitch library definitions.
+include ../../Makefile.com
+
+C99MODE = $(C99_ENABLE)
+LDLIBS += -lnsl -ldns_sd -lscf
+DYNLIB1 = nss_mdns.so$(VERS)
+
+lint := LDLIBS = $(LDLIBS.lib) -lc -lnsl -lscf -ldns_sd
diff --git a/usr/src/lib/nsswitch/mdns/amd64/Makefile b/usr/src/lib/nsswitch/mdns/amd64/Makefile
new file mode 100644
index 0000000000..7237707552
--- /dev/null
+++ b/usr/src/lib/nsswitch/mdns/amd64/Makefile
@@ -0,0 +1,34 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+
+include ../Makefile.com
+include $(SRC)/lib/Makefile.lib.64
+
+LIBS = $(DYNLIB1)
+
+include ../../Makefile.targ
+
+install: all $(ROOT64DYNLIB)
diff --git a/usr/src/lib/nsswitch/mdns/common/gethostent.c b/usr/src/lib/nsswitch/mdns/common/gethostent.c
new file mode 100644
index 0000000000..6b5756f0e9
--- /dev/null
+++ b/usr/src/lib/nsswitch/mdns/common/gethostent.c
@@ -0,0 +1,153 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "mdns_common.h"
+
+/*
+ * gethostby* functions for the hosts database. The hosts
+ * database stores IPv4 addresses only.
+ * mDNS query functions to perform the host lookup
+ * are in mdns/common/mdns_common.c file.
+ * _nss_mdns_hosts_constr is called to initialize
+ * the nsswitch backend data structures.
+ */
+
+static nss_status_t
+getbyname(be, a)
+ mdns_backend_ptr_t be;
+ void *a;
+{
+ struct mdns_querydata qdata;
+ char *hname;
+
+ (void) memset(&qdata, 0, sizeof (struct mdns_querydata));
+
+ qdata.argp = (nss_XbyY_args_t *)a;
+ hname = (char *)qdata.argp->key.name;
+
+ _nss_mdns_updatecfg(be);
+ return (_nss_mdns_querybyname(be, hname, AF_INET, &qdata));
+}
+
+/*ARGSUSED*/
+static nss_status_t
+getbyaddr(be, a)
+ mdns_backend_ptr_t be;
+ void *a;
+{
+ nss_XbyY_args_t *argp = (nss_XbyY_args_t *)a;
+ struct in_addr addr;
+ struct mdns_querydata qdata;
+ char buffer[sizeof ("255.255.255.255.in-addr.arpa.")];
+ uint8_t *p;
+
+ (void) memset(&qdata, 0, sizeof (struct mdns_querydata));
+ qdata.argp = argp;
+
+ argp->h_errno = 0;
+ if ((argp->key.hostaddr.type != AF_INET) ||
+ (argp->key.hostaddr.len != sizeof (addr)))
+ return (NSS_NOTFOUND);
+
+ (void) memcpy(&addr, argp->key.hostaddr.addr, sizeof (addr));
+
+ if (inet_ntop(AF_INET, (void *) &addr.s_addr,
+ (void *)qdata.paddrbuf,
+ sizeof (qdata.paddrbuf)) == NULL)
+ return (NSS_NOTFOUND);
+
+ qdata.af = AF_INET;
+ p = (uint8_t *)&addr.s_addr;
+ (void) snprintf(buffer, sizeof (buffer),
+ "%u.%u.%u.%u.in-addr.arpa.", p[3], p[2], p[1], p[0]);
+
+ _nss_mdns_updatecfg(be);
+ return (_nss_mdns_querybyaddr(be, buffer, qdata.af, &qdata));
+}
+
+/*ARGSUSED*/
+static nss_status_t
+_nss_mdns_getent(be, args)
+ mdns_backend_ptr_t be;
+ void *args;
+{
+ return (NSS_UNAVAIL);
+}
+
+/*ARGSUSED*/
+static nss_status_t
+_nss_mdns_setent(be, dummy)
+ mdns_backend_ptr_t be;
+ void *dummy;
+{
+ return (NSS_UNAVAIL);
+}
+
+/*ARGSUSED*/
+static nss_status_t
+_nss_mdns_endent(be, dummy)
+ mdns_backend_ptr_t be;
+ void *dummy;
+{
+ return (NSS_UNAVAIL);
+}
+
+/*ARGSUSED*/
+static nss_status_t
+_nss_mdns_hosts_destr(be, dummy)
+ mdns_backend_ptr_t be;
+ void *dummy;
+{
+ _nss_mdns_destr(be);
+ return (NSS_SUCCESS);
+}
+
+static mdns_backend_op_t host_ops[] = {
+ _nss_mdns_hosts_destr,
+ _nss_mdns_endent,
+ _nss_mdns_setent,
+ _nss_mdns_getent,
+ getbyname,
+ getbyaddr,
+};
+
+/*ARGSUSED*/
+nss_backend_t *
+_nss_mdns_hosts_constr(dummy1, dummy2, dummy3)
+ const char *dummy1, *dummy2, *dummy3;
+{
+ return (_nss_mdns_constr(host_ops,
+ sizeof (host_ops) / sizeof (host_ops[0])));
+}
+
+/*ARGSUSED*/
+nss_status_t
+_nss_get_mdns_hosts_name(mdns_backend_ptr_t *be, void **bufp, size_t *sizep)
+{
+ return (_nss_mdns_gethost_withttl(*bufp, *sizep, 0));
+}
diff --git a/usr/src/lib/nsswitch/mdns/common/gethostent6.c b/usr/src/lib/nsswitch/mdns/common/gethostent6.c
new file mode 100644
index 0000000000..e4288de82b
--- /dev/null
+++ b/usr/src/lib/nsswitch/mdns/common/gethostent6.c
@@ -0,0 +1,174 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "mdns_common.h"
+
+/*
+ * gethostby* functions for the ipnodes database. The ipnodes
+ * database stores both IPv4 and IPv6 address information.
+ * mDNS query functions to perform the host lookup are
+ * in mdns/common/mdns_common.c file.
+ * _nss_mdns_ipnodes_constr is called to initialize
+ * the nsswitch backend data structures.
+ */
+
+static nss_status_t
+getbyname(be, a)
+ mdns_backend_ptr_t be;
+ void *a;
+{
+ nss_XbyY_args_t *argp = (nss_XbyY_args_t *)a;
+ int af = argp->key.ipnode.af_family;
+ char *hname = (char *)argp->key.ipnode.name;
+ struct mdns_querydata qdata;
+
+ (void) memset(&qdata, 0, sizeof (struct mdns_querydata));
+ qdata.argp = argp;
+
+ _nss_mdns_updatecfg(be);
+ return (_nss_mdns_querybyname(be, hname, af, &qdata));
+}
+
+static nss_status_t
+getbyaddr(be, a)
+ mdns_backend_ptr_t be;
+ void *a;
+{
+ int i;
+ char ch;
+ char *chptr;
+ uint8_t *p;
+ struct in6_addr *addr;
+ struct mdns_querydata qdata;
+ char addrqryname[ sizeof ("f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f") + \
+ sizeof (".f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.f.ip6.arpa.")];
+ nss_XbyY_args_t *argp = (nss_XbyY_args_t *)a;
+
+ (void) memset(&qdata, 0, sizeof (struct mdns_querydata));
+ qdata.argp = argp;
+ argp->h_errno = 0;
+
+ if ((argp->key.hostaddr.type != AF_INET6) ||
+ (argp->key.hostaddr.len != sizeof (*addr)))
+ return (NSS_NOTFOUND);
+
+ /* LINTED E_BAD_PTR_CAST_ALIGN */
+ addr = (struct in6_addr *)(argp->key.hostaddr.addr);
+
+ if (IN6_IS_ADDR_V4MAPPED(addr)) {
+ struct in_addr ipv4addr;
+
+ IN6_V4MAPPED_TO_INADDR(addr, &ipv4addr);
+ if (inet_ntop(AF_INET, (void *) &ipv4addr.s_addr,
+ (void *)qdata.paddrbuf, sizeof (qdata.paddrbuf)) == NULL)
+ return (NSS_NOTFOUND);
+ qdata.af = AF_INET;
+ p = (uint8_t *)&ipv4addr.s_addr;
+ (void) snprintf(addrqryname, sizeof (addrqryname),
+ "%u.%u.%u.%u.in-addr.arpa.", p[3], p[2], p[1], p[0]);
+ } else {
+ if (inet_ntop(AF_INET6, (void *)addr,
+ (void *)qdata.paddrbuf,
+ sizeof (qdata.paddrbuf)) == NULL)
+ return (NSS_NOTFOUND);
+ qdata.af = AF_INET6;
+ chptr = addrqryname;
+ for (i = 0; i < 16; i++)
+ {
+ ch = ((char *)addr)[15-i];
+ chptr += snprintf(chptr, sizeof (addrqryname)-i*4,
+ "%X.%X.", ch&0x0f, (ch>>4)&0x0f);
+ }
+ (void) strlcpy(chptr, "ip6.arpa.", sizeof (addrqryname)-64);
+ }
+
+ _nss_mdns_updatecfg(be);
+ return (_nss_mdns_querybyaddr(be, addrqryname, qdata.af, &qdata));
+}
+
+/*ARGSUSED*/
+static nss_status_t
+_nss_mdns_getent(be, args)
+ mdns_backend_ptr_t be;
+ void *args;
+{
+ return (NSS_UNAVAIL);
+}
+
+/*ARGSUSED*/
+static nss_status_t
+_nss_mdns_setent(be, dummy)
+ mdns_backend_ptr_t be;
+ void *dummy;
+{
+ return (NSS_UNAVAIL);
+}
+
+/*ARGSUSED*/
+static nss_status_t
+_nss_mdns_endent(be, dummy)
+ mdns_backend_ptr_t be;
+ void *dummy;
+{
+ return (NSS_UNAVAIL);
+}
+
+/*ARGSUSED*/
+static nss_status_t
+_nss_mdns_ipnodes_destr(be, dummy)
+ mdns_backend_ptr_t be;
+ void *dummy;
+{
+ _nss_mdns_destr(be);
+ return (NSS_SUCCESS);
+}
+
+static mdns_backend_op_t ipnodes_ops[] = {
+ _nss_mdns_ipnodes_destr,
+ _nss_mdns_endent,
+ _nss_mdns_setent,
+ _nss_mdns_getent,
+ getbyname,
+ getbyaddr,
+};
+
+/*ARGSUSED*/
+nss_backend_t *
+_nss_mdns_ipnodes_constr(dummy1, dummy2, dummy3)
+ const char *dummy1, *dummy2, *dummy3;
+{
+ return (_nss_mdns_constr(ipnodes_ops,
+ sizeof (ipnodes_ops) / sizeof (ipnodes_ops[0])));
+}
+
+/*ARGSUSED*/
+nss_status_t
+_nss_get_mdns_ipnodes_name(mdns_backend_ptr_t *be, void **bufp, size_t *sizep)
+{
+ return (_nss_mdns_gethost_withttl(*bufp, *sizep, 1));
+}
diff --git a/usr/src/lib/nsswitch/mdns/common/mapfile-vers b/usr/src/lib/nsswitch/mdns/common/mapfile-vers
new file mode 100644
index 0000000000..cc7767771a
--- /dev/null
+++ b/usr/src/lib/nsswitch/mdns/common/mapfile-vers
@@ -0,0 +1,36 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# Generic interface definition for usr/src/lib/nsswitch/mdns.
+
+SUNWprivate_1.1 {
+ global:
+ _nss_mdns_hosts_constr;
+ _nss_mdns_ipnodes_constr;
+ _nss_get_mdns_hosts_name;
+ _nss_get_mdns_ipnodes_name;
+ local:
+ *;
+};
diff --git a/usr/src/lib/nsswitch/mdns/common/mdns_common.c b/usr/src/lib/nsswitch/mdns/common/mdns_common.c
new file mode 100644
index 0000000000..3e914e5744
--- /dev/null
+++ b/usr/src/lib/nsswitch/mdns/common/mdns_common.c
@@ -0,0 +1,763 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#include "mdns_common.h"
+
+static int _nss_mdns_queryrecord(const char *rrname, int rrclass, int rrtype,
+ DNSServiceQueryRecordReply callback,
+ struct mdns_querydata *data);
+static void _nss_mdns_get_svcstatetimestamp(struct timeval *);
+static void _nss_mdns_loadsmfcfg(mdns_backend_ptr_t);
+static void _nss_mdns_freesmfcfg(mdns_backend_ptr_t);
+static boolean_t cmpdmn(char *, char **, int);
+static char *RDataToName(char *data, char *buffer, int datalen, int buflen);
+static int searchdomain(mdns_backend_ptr_t, char *, int, char **);
+static boolean_t validdomain(mdns_backend_ptr_t, char *, int);
+
+/*
+ * This file includes the functions to query for host name
+ * information via Multicast DNS (mDNS). The function
+ * _nss_mdns_queryrecord queries for the host information via
+ * Multicast DNS. _nss_mdns_querybyname and _nss_mdns_querybyaddr
+ * query for host IP address and hostname by querying for A/AAAA
+ * and PTR DNS resource records respectively. DNSServiceQueryRecord
+ * in libdns_sd sends a request to the mDNS daemon (mdnsd) to place
+ * the DNS query via multicast and return the results.
+ * mdnsd is managed by SMF (FMRI: svc:/network/dns/multicast:default).
+ *
+ * gethostent.c and gethostent6.c implement the nsswitch 'hosts'
+ * backend module getXbyY functions: getbyname and getbyaddr.
+ * getby* functions in gethostent.c supports only IPv4 and
+ * getby* functions in gethostent6.c returns both IPv4 and
+ * IPv6 results. Functions in gethostent.c and gethostent6.c
+ * call the _nss_mdns_queryby* functions in mdns_common.c to
+ * query for host information via mDNS.
+ *
+ * Configuration for mdns is stored in SMF and is accessed using
+ * the FMRI: svc:/network/dns/multicast:default. Configuration
+ * includes the list of valid DNS domains checked before querying host
+ * information via mDNS and the search list to use for host lookup via
+ * mDNS. The default valid domain list in the mDNS service supports host
+ * lookups for hostnames in the ".local" domain and hostname queries
+ * for link-local IPv4 and IPv6 addresses. _nss_mdns_loadsmfcfg
+ * loads the nss_mdns configuration from SMF and the function
+ * _nss_mdns_updatecfg checks for any updates in nss_mdns configuration.
+ */
+
+static int
+_nss_mdns_queryrecord(const char *rrname, int rrclass, int rrtype,
+ DNSServiceQueryRecordReply callback,
+ struct mdns_querydata *data)
+{
+ int sockfd;
+ int flags = kDNSServiceFlagsForceMulticast; /* Multicast only */
+ int opinterface = kDNSServiceInterfaceIndexAny;
+ DNSServiceErrorType err;
+ DNSServiceRef ref = NULL;
+ int ret;
+ struct fd_set readfds;
+ struct timeval tv;
+
+ data->status = NSS_NOTFOUND;
+#ifdef DEBUG
+ syslog(LOG_DEBUG, "nss_mdns: query called rrname:%s rrtype:%d",
+ rrname, rrtype);
+#endif
+ err = DNSServiceQueryRecord(&ref, flags, opinterface,
+ rrname, rrtype, rrclass, callback, data);
+ if (err != kDNSServiceErr_NoError || ref == NULL ||
+ (sockfd = DNSServiceRefSockFD(ref)) == NULL) {
+ DNSServiceRefDeallocate(ref);
+ data->status = NSS_UNAVAIL;
+ return (NSS_UNAVAIL);
+ }
+
+ do {
+ FD_ZERO(&readfds);
+ FD_SET(sockfd, &readfds);
+ tv.tv_sec = NSSMDNS_MAXQRYTMO;
+ tv.tv_usec = 0;
+
+ /* Wait until response received from mDNS daemon */
+ ret = select(sockfd + 1, &readfds, NULL, NULL, &tv);
+ if (!((ret > 0) && FD_ISSET(sockfd, &readfds) &&
+ (DNSServiceProcessResult(ref) == kDNSServiceErr_NoError))) {
+ data->status = NSS_NOTFOUND;
+ if (errno != EINTR)
+ data->qrydone = B_TRUE;
+ }
+ } while (data->qrydone != B_TRUE);
+
+ if (data->status == NSS_SUCCESS && (data->withttlbuffer == NULL)) {
+ nss_XbyY_args_t *argp = data->argp;
+ if (argp->buf.result != NULL) {
+ int stat;
+
+ if (data->buffer == NULL) {
+ data->status = NSS_NOTFOUND;
+ DNSServiceRefDeallocate(ref);
+ return (data->status);
+ }
+ stat = (*argp->str2ent)(data->buffer,
+ strlen(data->buffer),
+ argp->buf.result, argp->buf.buffer,
+ argp->buf.buflen);
+ if (stat == NSS_STR_PARSE_SUCCESS) {
+ argp->returnval = argp->buf.result;
+ argp->returnlen = 1;
+ } else {
+ data->status = NSS_NOTFOUND;
+ if (stat == NSS_STR_PARSE_ERANGE)
+ argp->erange = 1;
+ }
+ free(data->buffer);
+ } else {
+ argp->returnval = argp->buf.buffer;
+ argp->returnlen = strlen(argp->buf.buffer);
+ }
+ data->buffer = NULL;
+ data->buflen = 0;
+ }
+
+ if (data->status != NSS_SUCCESS)
+ data->argp->h_errno = HOST_NOT_FOUND;
+
+ DNSServiceRefDeallocate(ref);
+ return (data->status);
+}
+
+static void
+/* LINTED E_FUNC_ARG_UNUSED */
+_nss_mdns_querynamereply(DNSServiceRef sdRef, const DNSServiceFlags flags,
+ /* LINTED E_FUNC_ARG_UNUSED */
+ uint32_t ifIndex, DNSServiceErrorType errorCode,
+ const char *fullname, uint16_t rrtype, uint16_t rrclass,
+ /* LINTED E_FUNC_ARG_UNUSED */
+ uint16_t rdlen, const void *rdata, uint32_t ttl,
+ void *context)
+{
+ struct mdns_querydata *qdata;
+ nss_XbyY_args_t *argp;
+ int firstent = 0;
+ int af;
+ char addrstore[INET6_ADDRSTRLEN];
+ char *buffer;
+ int len;
+ int remlen;
+
+ qdata = (struct mdns_querydata *)context;
+ argp = qdata->argp;
+
+ if (errorCode != kDNSServiceErr_NoError) {
+ qdata->qrydone = B_TRUE;
+ return;
+ }
+ if ((flags & kDNSServiceFlagsMoreComing))
+ qdata->qrydone = B_FALSE;
+ else
+ qdata->qrydone = B_TRUE;
+ if (!(flags & kDNSServiceFlagsAdd))
+ return;
+ if (rrclass != kDNSServiceClass_IN)
+ return;
+
+ if (rrtype == kDNSServiceType_A)
+ af = AF_INET;
+ else if (rrtype == kDNSServiceType_AAAA)
+ af = AF_INET6;
+ else
+ return;
+
+ if (qdata->buffer == NULL) {
+ if (qdata->withttlbsize > 0) {
+ remlen = qdata->buflen =
+ qdata->withttlbsize;
+ buffer = qdata->buffer =
+ qdata->withttlbuffer;
+ (void) memset(qdata->buffer, 0, remlen);
+ } else {
+ remlen = qdata->buflen =
+ argp->buf.buflen;
+ if (argp->buf.result != NULL) {
+ buffer = qdata->buffer =
+ calloc(1, remlen);
+ } else {
+ /* Return in file format */
+ (void) memset(argp->buf.buffer,
+ 0, remlen);
+ buffer = qdata->buffer = argp->buf.buffer;
+ }
+ }
+ firstent = 1;
+ } else {
+ buffer = qdata->buffer + strlen(qdata->buffer);
+ remlen = qdata->buflen - strlen(qdata->buffer);
+ }
+
+#ifdef DEBUG
+ syslog(LOG_DEBUG, "nss_mdns: querynamereply remlen:%d", remlen);
+#endif
+ if (inet_ntop(af, rdata, addrstore, INET6_ADDRSTRLEN) != NULL) {
+ if (firstent)
+ len = snprintf(buffer, remlen, "%s %s",
+ addrstore, fullname);
+ else
+ len = snprintf(buffer, remlen, "\n%s %s",
+ addrstore, fullname);
+ if (len >= remlen || len < 0) {
+ qdata->status = NSS_NOTFOUND;
+ qdata->argp->erange = 1;
+ qdata->argp->h_errno = HOST_NOT_FOUND;
+ return;
+ }
+ qdata->ttl = ttl;
+ qdata->status = NSS_SUCCESS;
+#ifdef DEBUG
+ syslog(LOG_DEBUG, "nss_mdns: querynamereply buffer:%s", buffer);
+#endif
+ } else {
+ qdata->status = NSS_NOTFOUND;
+ qdata->argp->h_errno = HOST_NOT_FOUND;
+ }
+}
+
+int
+_nss_mdns_querybyname(mdns_backend_ptr_t be, char *qname,
+ int af, struct mdns_querydata *data)
+{
+ int rrtype;
+ int rrclass;
+ int srchidx = 0;
+ int rc;
+ char hname[MAXDNAME];
+ char *name;
+ char *sname;
+
+ rrclass = kDNSServiceClass_IN;
+ if (af == AF_INET6)
+ rrtype = kDNSServiceType_ANY;
+ else if (af == AF_INET)
+ rrtype = kDNSServiceType_A;
+ else
+ return (NSS_NOTFOUND);
+
+ name = strdup(qname);
+ if (name == NULL)
+ return (NSS_UNAVAIL);
+
+ while ((srchidx = searchdomain(be, name, srchidx, &sname)) != -1) {
+ if (sname != NULL)
+ (void) snprintf(hname, sizeof (hname), "%s.%s",
+ name, sname);
+ else
+ (void) strlcpy(hname, name, sizeof (hname));
+#ifdef DEBUG
+ syslog(LOG_DEBUG, "nss_mdns: querybyname called" \
+ " srchidx:%d af:%d hname:%s", srchidx, af, qname);
+#endif
+ rc = _nss_mdns_queryrecord(hname, rrclass, rrtype,
+ _nss_mdns_querynamereply, data);
+ if ((rc == NSS_UNAVAIL) || (rc == NSS_SUCCESS)) {
+ free(name);
+ return (rc);
+ }
+ }
+ free(name);
+ return (NSS_NOTFOUND);
+}
+
+static void
+/* LINTED E_FUNC_ARG_UNUSED */
+_nss_mdns_queryaddrreply(DNSServiceRef sdRef, const DNSServiceFlags flags,
+ /* LINTED E_FUNC_ARG_UNUSED */
+ uint32_t ifIndex, DNSServiceErrorType errorCode,
+ /* LINTED E_FUNC_ARG_UNUSED */
+ const char *fullname, uint16_t rrtype, uint16_t rrclass,
+ uint16_t rdlen, const void *rdata, uint32_t ttl,
+ void *context)
+{
+ struct mdns_querydata *qdata;
+ nss_XbyY_args_t *argp;
+ char hostname[NI_MAXHOST];
+ int firstent = 0;
+ char *buffer;
+ int len;
+ int remlen;
+
+ qdata = (struct mdns_querydata *)context;
+ argp = qdata->argp;
+
+ if (errorCode != kDNSServiceErr_NoError) {
+ qdata->qrydone = B_TRUE;
+ return;
+ }
+ if ((flags & kDNSServiceFlagsMoreComing))
+ qdata->qrydone = B_FALSE;
+ else
+ qdata->qrydone = B_TRUE;
+ if (!(flags & kDNSServiceFlagsAdd))
+ return;
+ if (rrclass != kDNSServiceClass_IN)
+ return;
+ if (rrtype != kDNSServiceType_PTR)
+ return;
+
+ if (qdata->buffer == NULL) {
+ remlen = qdata->buflen = argp->buf.buflen;
+ if (argp->buf.result != NULL) {
+ buffer = qdata->buffer = calloc(1, remlen);
+ } else {
+ /* Return in file format */
+ (void) memset(argp->buf.buffer, 0, remlen);
+ buffer = qdata->buffer = argp->buf.buffer;
+ }
+ firstent = 1;
+ } else {
+ buffer = qdata->buffer + strlen(qdata->buffer);
+ remlen = qdata->buflen - strlen(qdata->buffer);
+ }
+
+ if (RDataToName((char *)rdata, hostname, rdlen, NI_MAXHOST) == NULL) {
+ qdata->status = NSS_NOTFOUND;
+ qdata->argp->h_errno = HOST_NOT_FOUND;
+ return;
+ }
+
+#ifdef DEBUG
+ syslog(LOG_DEBUG, "nss_mdns: querynamereply remlen:%d", remlen);
+#endif
+ if (firstent)
+ len = snprintf(buffer, remlen, "%s %s",
+ qdata->paddrbuf, hostname);
+ else
+ len = snprintf(buffer, remlen, "\n%s %s",
+ qdata->paddrbuf, hostname);
+ if (len >= remlen || len < 0) {
+ qdata->status = NSS_NOTFOUND;
+ qdata->argp->erange = 1;
+ qdata->argp->h_errno = HOST_NOT_FOUND;
+ return;
+ }
+ qdata->status = NSS_SUCCESS;
+ qdata->ttl = ttl;
+}
+
+int
+/* LINTED E_FUNC_ARG_UNUSED */
+_nss_mdns_querybyaddr(mdns_backend_ptr_t be, char *name, int af,
+ struct mdns_querydata *data)
+{
+ int rrtype;
+ int rrclass;
+
+#ifdef DEBUG
+ syslog(LOG_DEBUG, "nss_mdns: querybyaddr called" \
+ " af:%d addr:%s", af, name);
+#endif
+ rrclass = kDNSServiceClass_IN;
+ rrtype = kDNSServiceType_PTR;
+
+ if (validdomain(be, name, 0) == B_FALSE) {
+ data->status = NSS_NOTFOUND;
+ return (NSS_NOTFOUND);
+ }
+ return (_nss_mdns_queryrecord(name, rrclass, rrtype,
+ _nss_mdns_queryaddrreply, data));
+}
+
+/*
+ * Converts the encoded name in RData returned
+ * by mDNS query to name in file format
+ */
+static char *
+RDataToName(char *data, char *buffer, int datalen, int buflen)
+{
+ char *src = data;
+ char *srcend = data + datalen;
+ char *ptr = buffer;
+ char *end;
+ char *bend = buffer + buflen - 1; /* terminal '\0' */
+ int domainlen = 0;
+
+ while ((src < srcend) && (*src != 0)) {
+
+ /* first byte is len */
+ domainlen = *src++;
+ end = src + domainlen;
+
+ while ((src < end) && (ptr < bend)) {
+ uint8_t ch = *src++;
+ if (ch == '.' || ch == '\\') {
+ *ptr++ = '\\';
+ }
+ *ptr++ = ch;
+ }
+
+ /*
+ * Check if we copied entire domain str. and
+ * if space is still remaining for '.' seperator
+ */
+ if ((src != end) || (ptr == bend))
+ return (NULL);
+ *ptr++ = '.';
+ }
+ *ptr = '\0';
+ return (ptr);
+}
+
+nss_backend_t *
+_nss_mdns_constr(mdns_backend_op_t ops[], int n_ops)
+{
+ mdns_backend_ptr_t be;
+
+ if ((be = (mdns_backend_ptr_t)calloc(1, sizeof (*be))) == NULL)
+ return (NULL);
+ be->ops = ops;
+ be->n_ops = n_ops;
+ _nss_mdns_updatecfg(be);
+ return ((nss_backend_t *)be);
+}
+
+void
+_nss_mdns_destr(mdns_backend_ptr_t be)
+{
+ if (be != NULL) {
+ _nss_mdns_freesmfcfg(be);
+ free(be);
+ }
+}
+
+static int
+searchdomain(mdns_backend_ptr_t be, char *name, int srchidx, char **sname)
+{
+ int trailing_dot = 0;
+ char *ch;
+ *sname = NULL;
+
+ ch = name + strlen(name) - 1;
+ if ((*ch) == '.')
+ trailing_dot++;
+
+ if (trailing_dot && srchidx > 0)
+ /*
+ * If there is a trailing dot in the query
+ * name, do not perform any additional queries
+ * with search domains.
+ */
+ return (-1);
+
+ if (srchidx == 0) {
+ /*
+ * If there is a trailing dot in the query
+ * or atleast one dot in the query name then
+ * perform a query as-is once first.
+ */
+ ++srchidx;
+ if ((trailing_dot || (strchr(name, '.') != NULL))) {
+ if (validdomain(be, name, 1) == B_TRUE)
+ return (srchidx);
+ else if (trailing_dot)
+ return (-1);
+ }
+ }
+
+ if ((srchidx > NSSMDNS_MAXSRCHDMNS) ||
+ (be->dmnsrchlist[srchidx-1] == NULL))
+ return (-1);
+
+ *sname = be->dmnsrchlist[srchidx-1];
+ return (++srchidx);
+}
+
+/*
+ * This function determines if the domain name in the query
+ * matches any of the valid & search domains in the nss_mdns
+ * configuration.
+ */
+static boolean_t
+validdomain(mdns_backend_ptr_t be, char *name, int chksrchdmns)
+{
+ char *nameptr;
+
+ /* Remove any trailing and leading dots in the name */
+ nameptr = name + strlen(name) - 1;
+ while (*nameptr && (nameptr != name) && (*nameptr == '.'))
+ nameptr--;
+ *(++nameptr) = '\0';
+ nameptr = name;
+ while (*nameptr && (*nameptr == '.'))
+ nameptr++;
+ if (*nameptr == '\0')
+ return (B_FALSE);
+
+ /* Compare with search domains */
+ if (chksrchdmns && (cmpdmn(nameptr, be->dmnsrchlist,
+ NSSMDNS_MAXSRCHDMNS) == B_TRUE))
+ return (B_TRUE);
+
+ /* Compare with valid domains */
+ return (cmpdmn(nameptr, be->validdmnlist, NSSMDNS_MAXVALIDDMNS));
+}
+
+static boolean_t
+cmpdmn(char *name, char **dmnlist, int maxdmns)
+{
+ char *vptr;
+ int vdlen;
+ char *cptr;
+ int nlen;
+ int i;
+
+ nlen = strlen(name);
+ for (i = 0; (i < maxdmns) &&
+ ((vptr = dmnlist[i]) != NULL); i++) {
+ vdlen = strlen(vptr);
+ if (vdlen > nlen)
+ continue;
+ cptr = name + nlen - vdlen;
+ if (strncasecmp(cptr, vptr, vdlen) == 0)
+ return (B_TRUE);
+ }
+ return (B_FALSE);
+}
+
+static void
+_nss_mdns_get_svcstatetimestamp(struct timeval *ptv)
+{
+ scf_handle_t *h;
+ scf_simple_prop_t *sprop;
+ int32_t nsec;
+
+ (void) memset(ptv, 0, sizeof (struct timeval));
+
+ h = scf_handle_create(SCF_VERSION);
+ if (h == NULL)
+ return;
+
+ if (scf_handle_bind(h) == -1) {
+ scf_handle_destroy(h);
+ return;
+ }
+
+ if ((sprop = scf_simple_prop_get(h, SMF_MDNS_FMRI,
+ SCF_PG_RESTARTER, SCF_PROPERTY_STATE_TIMESTAMP)) != NULL) {
+ ptv->tv_sec = *(time_t *)(scf_simple_prop_next_time(sprop,
+ &nsec));
+ ptv->tv_usec = nsec / 1000;
+ scf_simple_prop_free(sprop);
+ }
+
+ if (h != NULL)
+ scf_handle_destroy(h);
+}
+
+void
+_nss_mdns_updatecfg(mdns_backend_ptr_t be)
+{
+ struct timeval statetimestamp;
+
+ /*
+ * Update configuration if current svc state timestamp
+ * is different from last known svc state timestamp
+ */
+ _nss_mdns_get_svcstatetimestamp(&statetimestamp);
+ if ((statetimestamp.tv_sec == 0) && (statetimestamp.tv_usec == 0)) {
+ syslog(LOG_ERR, "nss_mdns: error checking " \
+ "svc:/network/dns/multicast:default" \
+ " service timestamp");
+ } else if ((be->conftimestamp.tv_sec == statetimestamp.tv_sec) &&
+ (be->conftimestamp.tv_usec == statetimestamp.tv_usec)) {
+ return;
+ }
+
+ _nss_mdns_freesmfcfg(be);
+ _nss_mdns_loadsmfcfg(be);
+ be->conftimestamp.tv_sec = statetimestamp.tv_sec;
+ be->conftimestamp.tv_usec = statetimestamp.tv_usec;
+}
+
+static void
+load_mdns_domaincfg(scf_handle_t *h, char **storelist,
+ const char *scfprop, int maxprops)
+{
+ scf_simple_prop_t *sprop;
+ char *tchr;
+ char *pchr;
+ int tlen;
+ int cnt = 0;
+
+ if ((sprop = scf_simple_prop_get(h, SMF_MDNS_FMRI,
+ SMF_NSSMDNSCFG_PROPGRP, scfprop)) == NULL)
+ return;
+
+ while ((cnt < maxprops) &&
+ (tchr = scf_simple_prop_next_astring(sprop)) != NULL) {
+
+ /* Remove beginning & trailing '.' chars */
+ while (*tchr && (*tchr == '.'))
+ tchr++;
+
+ if (*tchr && ((tlen = strlen(tchr)) < MAXDNAME)) {
+ pchr = &tchr[tlen-1];
+ while ((pchr != tchr) && (*pchr == '.'))
+ pchr--;
+ *(++pchr) = '\0';
+ storelist[cnt] = strdup(tchr);
+ cnt++;
+ }
+ }
+ scf_simple_prop_free(sprop);
+}
+
+static void
+_nss_mdns_loadsmfcfg(mdns_backend_ptr_t be)
+{
+ scf_handle_t *h;
+
+ h = scf_handle_create(SCF_VERSION);
+ if (h == NULL)
+ return;
+
+ if (scf_handle_bind(h) == -1) {
+ scf_handle_destroy(h);
+ return;
+ }
+
+ load_mdns_domaincfg(h, &(be->dmnsrchlist[0]),
+ SMF_NSSMDNSCFG_SRCHPROP, NSSMDNS_MAXSRCHDMNS);
+
+ load_mdns_domaincfg(h, &(be->validdmnlist[0]),
+ SMF_NSSMDNSCFG_DMNPROP, NSSMDNS_MAXVALIDDMNS);
+
+ if (h != NULL)
+ scf_handle_destroy(h);
+}
+
+static void
+_nss_mdns_freesmfcfg(mdns_backend_ptr_t be)
+{
+ int idx;
+ if (be == NULL)
+ return;
+ for (idx = 0; idx < NSSMDNS_MAXSRCHDMNS; idx++) {
+ if (be->dmnsrchlist[idx] != NULL) {
+ free(be->dmnsrchlist[idx]);
+ be->dmnsrchlist[idx] = NULL;
+ }
+ }
+ for (idx = 0; idx < NSSMDNS_MAXVALIDDMNS; idx++) {
+ if (be->validdmnlist[idx] != NULL) {
+ free(be->validdmnlist[idx]);
+ be->validdmnlist[idx] = NULL;
+ }
+ }
+}
+
+/*
+ * Performs lookup for IP address by hostname via mDNS and returns
+ * results along with the TTL value from the mDNS resource records.
+ * Called by nscd wth a ptr to packed bufer and packed buffer size.
+ */
+nss_status_t
+_nss_mdns_gethost_withttl(void *buffer, size_t bufsize, int ipnode)
+{
+ nss_pheader_t *pbuf = (nss_pheader_t *)buffer;
+ nss_XbyY_args_t arg;
+ int dbop;
+ int af;
+ int len;
+ int blen;
+ char *dbname;
+ nss_status_t sret;
+ char *hname;
+ struct mdns_querydata qdata;
+ nssuint_t *pttl;
+ mdns_backend_ptr_t be = NULL;
+
+ (void) memset(&qdata, 0, sizeof (struct mdns_querydata));
+
+ qdata.argp = &arg;
+
+ /*
+ * Retrieve withttl buffer and size from the passed packed buffer.
+ * Results are returned along with ttl in this buffer.
+ */
+ qdata.withttlbsize = pbuf->data_len - sizeof (nssuint_t);
+ qdata.withttlbuffer = (char *)buffer + pbuf->data_off;
+
+ sret = nss_packed_getkey(buffer, bufsize, &dbname, &dbop, &arg);
+ if (sret != NSS_SUCCESS)
+ return (NSS_ERROR);
+
+ if (ipnode) {
+ if (arg.key.ipnode.flags != 0)
+ return (NSS_ERROR);
+ hname = (char *)arg.key.ipnode.name;
+ af = arg.key.ipnode.af_family;
+ } else {
+ af = AF_INET;
+ hname = (char *)arg.key.name;
+ }
+
+ if ((be = (mdns_backend_ptr_t)calloc(1, sizeof (*be))) == NULL)
+ return (NSS_ERROR);
+ _nss_mdns_updatecfg(be);
+
+ /* Zero out the withttl buffer prior to use */
+ (void) memset(qdata.withttlbuffer, 0, qdata.withttlbsize);
+
+#ifdef DEBUG
+ syslog(LOG_DEBUG, "nss_mdns: querybyname withttl called" \
+ " af:%d hname:%s", af, hname);
+#endif
+ if (_nss_mdns_querybyname(be, hname, af, &qdata) == NSS_SUCCESS) {
+ blen = strlen(qdata.buffer);
+ len = ROUND_UP(blen, sizeof (nssuint_t));
+
+ if (len + sizeof (nssuint_t) > pbuf->data_len) {
+ _nss_mdns_freesmfcfg(be);
+ free(be);
+ return (NSS_ERROR);
+ }
+
+ pbuf->ext_off = pbuf->data_off + len;
+ pbuf->ext_len = sizeof (nssuint_t);
+ pbuf->data_len = blen;
+
+ /* Return ttl in the packed buffer at ext_off */
+ pttl = (nssuint_t *)((void *)((char *)pbuf + pbuf->ext_off));
+ *pttl = qdata.ttl;
+
+ _nss_mdns_freesmfcfg(be);
+ free(be);
+ return (NSS_SUCCESS);
+ }
+ _nss_mdns_freesmfcfg(be);
+ free(be);
+ return (NSS_ERROR);
+}
diff --git a/usr/src/lib/nsswitch/mdns/common/mdns_common.h b/usr/src/lib/nsswitch/mdns/common/mdns_common.h
new file mode 100644
index 0000000000..0170b0fe18
--- /dev/null
+++ b/usr/src/lib/nsswitch/mdns/common/mdns_common.h
@@ -0,0 +1,107 @@
+/*
+ * CDDL HEADER START
+ *
+ * The contents of this file are subject to the terms of the
+ * Common Development and Distribution License (the "License").
+ * You may not use this file except in compliance with the License.
+ *
+ * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+ * or http://www.opensolaris.org/os/licensing.
+ * See the License for the specific language governing permissions
+ * and limitations under the License.
+ *
+ * When distributing Covered Code, include this CDDL HEADER in each
+ * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+ * If applicable, add the following below this CDDL HEADER, with the
+ * fields enclosed by brackets "[]" replaced with your own identifying
+ * information: Portions Copyright [yyyy] [name of copyright owner]
+ *
+ * CDDL HEADER END
+ */
+
+/*
+ * Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+ * Use is subject to license terms.
+ */
+
+#pragma ident "%Z%%M% %I% %E% SMI"
+
+#ifndef _MDNS_COMMON_H
+#define _MDNS_COMMON_H
+
+#include <stdio.h>
+#include <ctype.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <netdb.h>
+#include <strings.h>
+#include <syslog.h>
+#include <sys/time.h>
+#include <sys/select.h>
+#include <netinet/in.h>
+#include <arpa/inet.h>
+#include <arpa/nameser.h>
+#include <nsswitch.h>
+#include <nss_common.h>
+#include <nss_dbdefs.h>
+#include <stdlib.h>
+#include <signal.h>
+#include <libscf.h>
+#include <dns_sd.h>
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+#define NSSMDNS_MAXQRYTMO 1
+#define NSSMDNS_MAXSRCHDMNS 6
+#define NSSMDNS_MAXVALIDDMNS 10
+
+#define SMF_MDNS_FMRI "svc:/network/dns/multicast:default"
+#define SMF_NSSMDNSCFG_PROPGRP "nss_mdns_config"
+#define SMF_NSSMDNSCFG_SRCHPROP "search"
+#define SMF_NSSMDNSCFG_DMNPROP "domain"
+
+typedef struct mdns_backend *mdns_backend_ptr_t;
+typedef nss_status_t (*mdns_backend_op_t)(mdns_backend_ptr_t, void *);
+
+struct mdns_backend {
+ mdns_backend_op_t *ops;
+ nss_dbop_t n_ops;
+ char *dmnsrchlist[NSSMDNS_MAXSRCHDMNS];
+ char *validdmnlist[NSSMDNS_MAXVALIDDMNS];
+ struct timeval conftimestamp;
+};
+
+struct mdns_querydata {
+ nss_XbyY_args_t *argp;
+ char *buffer;
+ int buflen;
+ int ttl;
+ boolean_t qrydone;
+ int status;
+ int af;
+ char paddrbuf[INET6_ADDRSTRLEN + 1];
+ int withttlbsize;
+ char *withttlbuffer;
+};
+
+nss_backend_t *_nss_mdns_constr(mdns_backend_op_t *, int);
+void _nss_mdns_destr(mdns_backend_ptr_t);
+int _nss_mdns_querybyname(mdns_backend_ptr_t, char *name,
+ int af, struct mdns_querydata *data);
+int _nss_mdns_querybyaddr(mdns_backend_ptr_t, char *name,
+ int af, struct mdns_querydata *data);
+void _nss_mdns_updatecfg(mdns_backend_ptr_t);
+
+nss_status_t _nss_mdns_gethost_withttl(void *buf, size_t bufsize, int ipnode);
+nss_status_t _nss_get_mdns_hosts_name(mdns_backend_ptr_t *be,
+ void **bufp, size_t *sizep);
+nss_status_t _nss_get_mdns_ipnodes_name(mdns_backend_ptr_t *be,
+ void **bufp, size_t *sizep);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* _MDNS_COMMON_H */
diff --git a/usr/src/lib/nsswitch/mdns/i386/Makefile b/usr/src/lib/nsswitch/mdns/i386/Makefile
new file mode 100644
index 0000000000..a250b9553d
--- /dev/null
+++ b/usr/src/lib/nsswitch/mdns/i386/Makefile
@@ -0,0 +1,34 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# lib/nsswitch/mdns/i386/Makefile
+
+include ../Makefile.com
+
+LIBS = $(DYNLIB1)
+
+include ../../Makefile.targ
+
+install: all $(ROOTLIBS)
diff --git a/usr/src/lib/nsswitch/mdns/sparc/Makefile b/usr/src/lib/nsswitch/mdns/sparc/Makefile
new file mode 100644
index 0000000000..8fa0a071cb
--- /dev/null
+++ b/usr/src/lib/nsswitch/mdns/sparc/Makefile
@@ -0,0 +1,34 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# lib/nsswitch/mdns/sparc/Makefile
+
+include ../Makefile.com
+
+LIBS = $(DYNLIB1)
+
+include ../../Makefile.targ
+
+install: all $(ROOTLIBS)
diff --git a/usr/src/lib/nsswitch/mdns/sparcv9/Makefile b/usr/src/lib/nsswitch/mdns/sparcv9/Makefile
new file mode 100644
index 0000000000..ca03048942
--- /dev/null
+++ b/usr/src/lib/nsswitch/mdns/sparcv9/Makefile
@@ -0,0 +1,35 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+#ident "%Z%%M% %I% %E% SMI"
+#
+# lib/nsswitch/mdns/sparcv9/Makefile
+
+include ../Makefile.com
+include $(SRC)/lib/Makefile.lib.64
+
+LIBS = $(DYNLIB1)
+
+include ../../Makefile.targ
+
+install: all $(ROOT64DYNLIB)
diff --git a/usr/src/pkgdefs/Makefile b/usr/src/pkgdefs/Makefile
index c58e7b553d..f3d97d538d 100644
--- a/usr/src/pkgdefs/Makefile
+++ b/usr/src/pkgdefs/Makefile
@@ -195,6 +195,8 @@ COMMON_SUBDIRS= \
SUNWdhcsu \
SUNWdmgtr \
SUNWdmgtu \
+ SUNWdsdr \
+ SUNWdsdu \
SUNWdoc \
SUNWdpl \
SUNWdtrc \
diff --git a/usr/src/pkgdefs/SUNW0on/prototype_com b/usr/src/pkgdefs/SUNW0on/prototype_com
index bb97b7cb47..ce503fdc43 100644
--- a/usr/src/pkgdefs/SUNW0on/prototype_com
+++ b/usr/src/pkgdefs/SUNW0on/prototype_com
@@ -240,6 +240,7 @@ f none usr/lib/help/auths/locale/SmfCronStates.html 444 root bin
f none usr/lib/help/auths/locale/SmfHeader.html 444 root bin
f none usr/lib/help/auths/locale/SmfInetdStates.html 444 root bin
f none usr/lib/help/auths/locale/SmfManageHeader.html 444 root bin
+f none usr/lib/help/auths/locale/SmfMDNSStates.html 444 root bin
f none usr/lib/help/auths/locale/SmfModifyAppl.html 444 root bin
f none usr/lib/help/auths/locale/SmfModifyDepend.html 444 root bin
f none usr/lib/help/auths/locale/SmfModifyFramework.html 444 root bin
@@ -256,6 +257,7 @@ f none usr/lib/help/auths/locale/SmfSyslogStates.html 444 root bin
f none usr/lib/help/auths/locale/SmfValueHeader.html 444 root bin
f none usr/lib/help/auths/locale/SmfValueInetd.html 444 root bin
f none usr/lib/help/auths/locale/SmfValueIPsec.html 444 root bin
+f none usr/lib/help/auths/locale/SmfValueMDNS.html 444 root bin
f none usr/lib/help/auths/locale/SmfValueNWAM.html 444 root bin
f none usr/lib/help/auths/locale/SmfValueRouting.html 444 root bin
f none usr/lib/help/auths/locale/SmfWpaStates.html 444 root bin
diff --git a/usr/src/pkgdefs/SUNWcsu/prototype_com b/usr/src/pkgdefs/SUNWcsu/prototype_com
index ebaecf2e1d..4e154ce0bc 100644
--- a/usr/src/pkgdefs/SUNWcsu/prototype_com
+++ b/usr/src/pkgdefs/SUNWcsu/prototype_com
@@ -478,6 +478,7 @@ f none usr/lib/help/auths/locale/C/SmfAutofsStates.html 444 root bin
f none usr/lib/help/auths/locale/C/SmfCronStates.html 444 root bin
f none usr/lib/help/auths/locale/C/SmfHeader.html 444 root bin
f none usr/lib/help/auths/locale/C/SmfManageHeader.html 444 root bin
+f none usr/lib/help/auths/locale/C/SmfMDNSStates.html 444 root bin
f none usr/lib/help/auths/locale/C/SmfModifyAppl.html 444 root bin
f none usr/lib/help/auths/locale/C/SmfModifyDepend.html 444 root bin
f none usr/lib/help/auths/locale/C/SmfModifyFramework.html 444 root bin
@@ -495,6 +496,7 @@ f none usr/lib/help/auths/locale/C/SmfSyslogStates.html 444 root bin
f none usr/lib/help/auths/locale/C/SmfValueHeader.html 444 root bin
f none usr/lib/help/auths/locale/C/SmfValueInetd.html 444 root bin
f none usr/lib/help/auths/locale/C/SmfValueIPsec.html 444 root bin
+f none usr/lib/help/auths/locale/C/SmfValueMDNS.html 444 root bin
f none usr/lib/help/auths/locale/C/SmfValueNWAM.html 444 root bin
f none usr/lib/help/auths/locale/C/SmfValueRouting.html 444 root bin
f none usr/lib/help/auths/locale/C/SmfWpaStates.html 444 root bin
diff --git a/usr/src/pkgdefs/SUNWdsdr/Makefile b/usr/src/pkgdefs/SUNWdsdr/Makefile
new file mode 100644
index 0000000000..0398b50af2
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWdsdr/Makefile
@@ -0,0 +1,37 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+#ident "%Z%%M% %I% %E% SMI"
+
+include ../Makefile.com
+
+DATAFILES= i.manifest r.manifest
+
+.KEEP_STATE:
+
+all: $(FILES) depend
+
+install: all pkg
+
+include ../Makefile.targ
diff --git a/usr/src/pkgdefs/SUNWdsdr/depend b/usr/src/pkgdefs/SUNWdsdr/depend
new file mode 100644
index 0000000000..e4e4bd8ce0
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWdsdr/depend
@@ -0,0 +1,54 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+#ident "%Z%%M% %I% %E% SMI"
+
+#
+# This package information file defines software dependencies associated
+# with the pkg. You can define three types of pkg dependencies with this file:
+# P indicates a prerequisite for installation
+# I indicates an incompatible package
+# R indicates a reverse dependency
+# <pkg.abbr> see pkginfo(4), PKG parameter
+# <name> see pkginfo(4), NAME parameter
+# <version> see pkginfo(4), VERSION parameter
+# <arch> see pkginfo(4), ARCH parameter
+# <type> <pkg.abbr> <name>
+# (<arch>)<version>
+# (<arch>)<version>
+# ...
+# <type> <pkg.abbr> <name>
+# ...
+#
+
+P SUNWcar Core Architecture, (Root)
+P SUNWcakr Core Solaris Kernel Architecture (Root)
+P SUNWkvm Core Architecture, (Kvm)
+P SUNWcsr Core Solaris, (Root)
+P SUNWckr Core Solaris Kernel (Root)
+P SUNWcnetr Core Solaris Network Infrastructure (Root)
+P SUNWcsu Core Solaris, (Usr)
+P SUNWcslr Core Solaris Libraries (Root)
+P SUNWcsd Core Solaris Devices
+P SUNWdsdu Multicast DNS and Service Discovery (Usr)
diff --git a/usr/src/pkgdefs/SUNWdsdr/pkginfo.tmpl b/usr/src/pkgdefs/SUNWdsdr/pkginfo.tmpl
new file mode 100644
index 0000000000..f1c7c71f41
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWdsdr/pkginfo.tmpl
@@ -0,0 +1,50 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+#ident "%Z%%M% %I% %E% SMI"
+
+#
+# This required package information file describes characteristics of the
+# package, such as package abbreviation, full package name, package version,
+# and package architecture.
+#
+PKG="SUNWdsdr"
+NAME="Multicast DNS and Service Discovery (Root)"
+ARCH="ISA"
+VERSION="ONVERS,REV=0.0.0"
+SUNW_PRODNAME="SunOS"
+SUNW_PRODVERS="RELEASE/VERSION"
+SUNW_PKGTYPE="root"
+MAXINST="1000"
+CATEGORY="system"
+DESC="Root components for Multicast DNS daemon and service discovery support"
+VENDOR="Sun Microsystems, Inc."
+HOTLINE="Please contact your local service provider"
+EMAIL=""
+CLASSES="none manifest"
+BASEDIR=/
+SUNW_PKGVERS="1.0"
+SUNW_PKG_ALLZONES="true"
+SUNW_PKG_HOLLOW="false"
+SUNW_PKG_THISZONE="false"
diff --git a/usr/src/pkgdefs/SUNWdsdr/prototype_com b/usr/src/pkgdefs/SUNWdsdr/prototype_com
new file mode 100644
index 0000000000..88d9bf9ad8
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWdsdr/prototype_com
@@ -0,0 +1,41 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+#ident "%Z%%M% %I% %E% SMI"
+
+# packaging files
+i depend
+i pkginfo
+i i.manifest
+i r.manifest
+
+#
+# SUNWdsdr
+#
+d none var 0755 root sys
+d none var/svc 0755 root sys
+d none var/svc/manifest 0755 root sys
+d none var/svc/manifest/network 0755 root sys
+d none var/svc/manifest/network/dns 0755 root sys
+f manifest var/svc/manifest/network/dns/multicast.xml 0444 root sys
diff --git a/usr/src/pkgdefs/SUNWdsdr/prototype_i386 b/usr/src/pkgdefs/SUNWdsdr/prototype_i386
new file mode 100644
index 0000000000..bbc6e3681b
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWdsdr/prototype_i386
@@ -0,0 +1,27 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+#ident "%Z%%M% %I% %E% SMI"
+
+!include prototype_com
diff --git a/usr/src/pkgdefs/SUNWdsdr/prototype_sparc b/usr/src/pkgdefs/SUNWdsdr/prototype_sparc
new file mode 100644
index 0000000000..bbc6e3681b
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWdsdr/prototype_sparc
@@ -0,0 +1,27 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+#ident "%Z%%M% %I% %E% SMI"
+
+!include prototype_com
diff --git a/usr/src/pkgdefs/SUNWdsdu/Makefile b/usr/src/pkgdefs/SUNWdsdu/Makefile
new file mode 100644
index 0000000000..b02d335b05
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWdsdu/Makefile
@@ -0,0 +1,37 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+#ident "%Z%%M% %I% %E% SMI"
+
+include ../Makefile.com
+
+DATAFILES=
+
+.KEEP_STATE:
+
+all: $(FILES) depend
+
+install: all pkg
+
+include ../Makefile.targ
diff --git a/usr/src/pkgdefs/SUNWdsdu/copyright b/usr/src/pkgdefs/SUNWdsdu/copyright
new file mode 100644
index 0000000000..c64647a000
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWdsdu/copyright
@@ -0,0 +1,43 @@
+#ident "%Z%%M% %I% %E% SMI"
+
+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.
+
+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.
+
+Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+Use is subject to license terms.
diff --git a/usr/src/pkgdefs/SUNWdsdu/depend b/usr/src/pkgdefs/SUNWdsdu/depend
new file mode 100644
index 0000000000..e4aa905021
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWdsdu/depend
@@ -0,0 +1,53 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+#ident "%Z%%M% %I% %E% SMI"
+
+#
+# This package information file defines software dependencies associated
+# with the pkg. You can define three types of pkg dependencies with this file:
+# P indicates a prerequisite for installation
+# I indicates an incompatible package
+# R indicates a reverse dependency
+# <pkg.abbr> see pkginfo(4), PKG parameter
+# <name> see pkginfo(4), NAME parameter
+# <version> see pkginfo(4), VERSION parameter
+# <arch> see pkginfo(4), ARCH parameter
+# <type> <pkg.abbr> <name>
+# (<arch>)<version>
+# (<arch>)<version>
+# ...
+# <type> <pkg.abbr> <name>
+# ...
+#
+
+P SUNWcar Core Architecture, (Root)
+P SUNWcakr Core Solaris Kernel Architecture (Root)
+P SUNWkvm Core Architecture, (Kvm)
+P SUNWcsr Core Solaris, (Root)
+P SUNWckr Core Solaris Kernel (Root)
+P SUNWcnetr Core Solaris Network Infrastructure (Root)
+P SUNWcsu Core Solaris, (Usr)
+P SUNWcslr Core Solaris Libraries (Root)
+P SUNWcsd Core Solaris Devices
diff --git a/usr/src/pkgdefs/SUNWdsdu/pkginfo.tmpl b/usr/src/pkgdefs/SUNWdsdu/pkginfo.tmpl
new file mode 100644
index 0000000000..25d81c188c
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWdsdu/pkginfo.tmpl
@@ -0,0 +1,50 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+#ident "%Z%%M% %I% %E% SMI"
+
+#
+# This required package information file describes characteristics of the
+# package, such as package abbreviation, full package name, package version,
+# and package architecture.
+#
+PKG="SUNWdsdu"
+NAME="Multicast DNS and Service Discovery (Usr)"
+ARCH="ISA"
+VERSION="ONVERS,REV=0.0.0"
+SUNW_PRODNAME="SunOS"
+SUNW_PRODVERS="RELEASE/VERSION"
+SUNW_PKGTYPE="usr"
+MAXINST="1000"
+CATEGORY="system"
+DESC="Multicast DNS daemon and service discovery modules"
+VENDOR="Sun Microsystems, Inc."
+HOTLINE="Please contact your local service provider"
+EMAIL=""
+CLASSES="none"
+BASEDIR=/
+SUNW_PKGVERS="1.0"
+SUNW_PKG_ALLZONES="true"
+SUNW_PKG_HOLLOW="false"
+SUNW_PKG_THISZONE="false"
diff --git a/usr/src/pkgdefs/SUNWdsdu/prototype_com b/usr/src/pkgdefs/SUNWdsdu/prototype_com
new file mode 100644
index 0000000000..46f38dc5f7
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWdsdu/prototype_com
@@ -0,0 +1,55 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+#ident "%Z%%M% %I% %E% SMI"
+
+# packaging files
+i copyright
+i depend
+i pkginfo
+
+#
+# SUNWdsdu
+#
+d none usr 755 root sys
+d none usr/bin 755 root bin
+f none usr/bin/dns-sd 0555 root bin
+d none usr/include 755 root bin
+f none usr/include/dns_sd.h 644 root bin
+d none usr/lib 755 root bin
+d none usr/lib/inet 755 root bin
+f none usr/lib/inet/mdnsd 0555 root bin
+f none usr/lib/libdns_sd.so.1 755 root bin
+s none usr/lib/libdns_sd.so=libdns_sd.so.1
+f none usr/lib/libjdns_sd.so.1 755 root bin
+s none usr/lib/libjdns_sd.so=libjdns_sd.so.1
+f none usr/lib/llib-ldns_sd 644 root bin
+f none usr/lib/llib-ldns_sd.ln 644 root bin
+f none usr/lib/nss_mdns.so.1 755 root bin
+d none usr/share 755 root sys
+d none usr/share/lib 755 root sys
+d none usr/share/lib/java 755 root sys
+f none usr/share/lib/java/dnssd.jar 644 root sys
+
+!include prototype_doc
diff --git a/usr/src/pkgdefs/SUNWdsdu/prototype_doc b/usr/src/pkgdefs/SUNWdsdu/prototype_doc
new file mode 100644
index 0000000000..4bba668e41
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWdsdu/prototype_doc
@@ -0,0 +1,66 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+#
+
+#ident "%Z%%M% %I% %E% SMI"
+
+d none usr/share/lib/java/javadoc 0755 root other
+d none usr/share/lib/java/javadoc/dnssd 0755 root other
+d none usr/share/lib/java/javadoc/dnssd/api 0755 root other
+f none usr/share/lib/java/javadoc/dnssd/api/allclasses-frame.html 0644 root other
+f none usr/share/lib/java/javadoc/dnssd/api/allclasses-noframe.html 0644 root other
+d none usr/share/lib/java/javadoc/dnssd/api/com 0755 root other
+d none usr/share/lib/java/javadoc/dnssd/api/com/apple 0755 root other
+d none usr/share/lib/java/javadoc/dnssd/api/com/apple/dnssd 0755 root other
+f none usr/share/lib/java/javadoc/dnssd/api/com/apple/dnssd/BaseListener.html 0644 root other
+f none usr/share/lib/java/javadoc/dnssd/api/com/apple/dnssd/BrowseListener.html 0644 root other
+f none usr/share/lib/java/javadoc/dnssd/api/com/apple/dnssd/DNSRecord.html 0644 root other
+f none usr/share/lib/java/javadoc/dnssd/api/com/apple/dnssd/DNSSD.html 0644 root other
+f none usr/share/lib/java/javadoc/dnssd/api/com/apple/dnssd/DNSSDException.html 0644 root other
+f none usr/share/lib/java/javadoc/dnssd/api/com/apple/dnssd/DNSSDRecordRegistrar.html 0644 root other
+f none usr/share/lib/java/javadoc/dnssd/api/com/apple/dnssd/DNSSDRegistration.html 0644 root other
+f none usr/share/lib/java/javadoc/dnssd/api/com/apple/dnssd/DNSSDService.html 0644 root other
+f none usr/share/lib/java/javadoc/dnssd/api/com/apple/dnssd/DomainListener.html 0644 root other
+f none usr/share/lib/java/javadoc/dnssd/api/com/apple/dnssd/package-frame.html 0644 root other
+f none usr/share/lib/java/javadoc/dnssd/api/com/apple/dnssd/package-summary.html 0644 root other
+f none usr/share/lib/java/javadoc/dnssd/api/com/apple/dnssd/package-tree.html 0644 root other
+f none usr/share/lib/java/javadoc/dnssd/api/com/apple/dnssd/QueryListener.html 0644 root other
+f none usr/share/lib/java/javadoc/dnssd/api/com/apple/dnssd/RegisterListener.html 0644 root other
+f none usr/share/lib/java/javadoc/dnssd/api/com/apple/dnssd/RegisterRecordListener.html 0644 root other
+f none usr/share/lib/java/javadoc/dnssd/api/com/apple/dnssd/ResolveListener.html 0644 root other
+f none usr/share/lib/java/javadoc/dnssd/api/com/apple/dnssd/TXTRecord.html 0644 root other
+f none usr/share/lib/java/javadoc/dnssd/api/constant-values.html 0644 root other
+f none usr/share/lib/java/javadoc/dnssd/api/deprecated-list.html 0644 root other
+f none usr/share/lib/java/javadoc/dnssd/api/help-doc.html 0644 root other
+f none usr/share/lib/java/javadoc/dnssd/api/index-all.html 0644 root other
+f none usr/share/lib/java/javadoc/dnssd/api/index.html 0644 root other
+f none usr/share/lib/java/javadoc/dnssd/api/overview-tree.html 0644 root other
+f none usr/share/lib/java/javadoc/dnssd/api/package-list 0644 root other
+d none usr/share/lib/java/javadoc/dnssd/api/resources 0755 root other
+f none usr/share/lib/java/javadoc/dnssd/api/resources/inherit.gif 0644 root other
+f none usr/share/lib/java/javadoc/dnssd/api/serialized-form.html 0644 root other
+f none usr/share/lib/java/javadoc/dnssd/api/stylesheet.css 0644 root other
+d none usr/share/lib/java/javadoc/dnssd/examples 0755 root other
+f none usr/share/lib/java/javadoc/dnssd/examples/BrowserApp.jar 0644 root sys
+f none usr/share/lib/java/javadoc/dnssd/examples/SimpleChat.jar 0644 root sys
diff --git a/usr/src/pkgdefs/SUNWdsdu/prototype_i386 b/usr/src/pkgdefs/SUNWdsdu/prototype_i386
new file mode 100644
index 0000000000..6e46b6f421
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWdsdu/prototype_i386
@@ -0,0 +1,36 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+#ident "%Z%%M% %I% %E% SMI"
+
+!include prototype_com
+
+#
+d none usr/lib/amd64 755 root bin
+f none usr/lib/amd64/libdns_sd.so.1 755 root bin
+s none usr/lib/amd64/libdns_sd.so=libdns_sd.so.1
+f none usr/lib/amd64/libjdns_sd.so.1 755 root bin
+s none usr/lib/amd64/libjdns_sd.so=libjdns_sd.so.1
+f none usr/lib/amd64/llib-ldns_sd.ln 644 root bin
+f none usr/lib/amd64/nss_mdns.so.1 755 root bin
diff --git a/usr/src/pkgdefs/SUNWdsdu/prototype_sparc b/usr/src/pkgdefs/SUNWdsdu/prototype_sparc
new file mode 100644
index 0000000000..5b12fe85f6
--- /dev/null
+++ b/usr/src/pkgdefs/SUNWdsdu/prototype_sparc
@@ -0,0 +1,36 @@
+#
+# CDDL HEADER START
+#
+# The contents of this file are subject to the terms of the
+# Common Development and Distribution License (the "License").
+# You may not use this file except in compliance with the License.
+#
+# You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
+# or http://www.opensolaris.org/os/licensing.
+# See the License for the specific language governing permissions
+# and limitations under the License.
+#
+# When distributing Covered Code, include this CDDL HEADER in each
+# file and include the License file at usr/src/OPENSOLARIS.LICENSE.
+# If applicable, add the following below this CDDL HEADER, with the
+# fields enclosed by brackets "[]" replaced with your own identifying
+# information: Portions Copyright [yyyy] [name of copyright owner]
+#
+# CDDL HEADER END
+#
+#
+# Copyright 2007 Sun Microsystems, Inc. All rights reserved.
+# Use is subject to license terms.
+
+#ident "%Z%%M% %I% %E% SMI"
+
+!include prototype_com
+
+#
+d none usr/lib/sparcv9 755 root bin
+f none usr/lib/sparcv9/libdns_sd.so.1 755 root bin
+s none usr/lib/sparcv9/libdns_sd.so=libdns_sd.so.1
+f none usr/lib/sparcv9/libjdns_sd.so.1 755 root bin
+s none usr/lib/sparcv9/libjdns_sd.so=libjdns_sd.so.1
+f none usr/lib/sparcv9/llib-ldns_sd.ln 644 root bin
+f none usr/lib/sparcv9/nss_mdns.so.1 755 root bin
diff --git a/usr/src/uts/common/netinet/in.h b/usr/src/uts/common/netinet/in.h
index 951d4c8653..f46c9753d2 100644
--- a/usr/src/uts/common/netinet/in.h
+++ b/usr/src/uts/common/netinet/in.h
@@ -197,6 +197,7 @@ typedef uint16_t sa_family_t;
#define IPPORT_NAMESERVER 42
#define IPPORT_WHOIS 43
#define IPPORT_DOMAIN 53
+#define IPPORT_MDNS 5353
#define IPPORT_MTP 57
/*